From e1fbda6f56e229ee51731493bac1d7331aebfee8 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Wed, 1 Aug 2018 14:09:00 +0000 Subject: [PATCH 001/686] Update docs version and clear release notes after 8.0.0 version bump git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338559 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/ReleaseNotes.rst | 220 +----------------------------------------- docs/conf.py | 4 +- 2 files changed, 7 insertions(+), 217 deletions(-) diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index ef2f0604b..26f47c1fa 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -1,5 +1,5 @@ =================================================== -Extra Clang Tools 7.0.0 (In-Progress) Release Notes +Extra Clang Tools 8.0.0 (In-Progress) Release Notes =================================================== .. contents:: @@ -10,7 +10,7 @@ Written by the `LLVM Team `_ .. warning:: - These are in-progress notes for the upcoming Extra Clang Tools 7 release. + These are in-progress notes for the upcoming Extra Clang Tools 8 release. Release notes for previous releases can be found on `the Download Page `_. @@ -18,7 +18,7 @@ Introduction ============ This document contains the release notes for the Extra Clang Tools, part of the -Clang release 7.0.0. Here we describe the status of the Extra Clang Tools in +Clang release 8.0.0. Here we describe the status of the Extra Clang Tools in some detail, including major improvements from the previous release and new feature work. All LLVM releases may be downloaded from the `LLVM releases web site `_. @@ -32,7 +32,7 @@ main Clang web page, this document applies to the *next* release, not the current one. To see the release notes for a specific release, please see the `releases page `_. -What's New in Extra Clang Tools 7.0.0? +What's New in Extra Clang Tools 8.0.0? ====================================== Some of the major new features and improvements to Extra Clang Tools are listed @@ -57,217 +57,7 @@ The improvements are... Improvements to clang-tidy -------------------------- -- The checks profiling info can now be stored as JSON files for futher - post-processing and analysis. - -- New module `abseil` for checks related to the `Abseil `_ - library. - -- New module ``portability``. - -- New module ``zircon`` for checks related to Fuchsia's Zircon kernel. - -- New :doc:`abseil-string-find-startswith - ` check. - - Checks whether a ``std::string::find()`` result is compared with 0, and - suggests replacing with ``absl::StartsWith()``. - -- New :doc:`android-comparison-in-temp-failure-retry - ` check. - - Diagnoses comparisons that appear to be incorrectly placed in the argument to - the ``TEMP_FAILURE_RETRY`` macro. - -- New :doc:`bugprone-exception-escape - ` check - - Finds functions which may throw an exception directly or indirectly, but they - should not. - -- New :doc:`bugprone-parent-virtual-call - ` check. - - Detects and fixes calls to grand-...parent virtual methods instead of calls - to overridden parent's virtual methods. - -- New :doc:`bugprone-terminating-continue - ` check - - Checks if a ``continue`` statement terminates the loop. - -- New :doc:`bugprone-throw-keyword-missing - ` check. - - Diagnoses when a temporary object that appears to be an exception is - constructed but not thrown. - -- New :doc:`bugprone-unused-return-value - ` check. - - Warns on unused function return values. - -- New :doc:`cert-msc32-c - ` check - - Detects inappropriate seeding of ``srand()`` function. - -- New :doc:`cert-msc51-cpp - ` check - - Detects inappropriate seeding of C++ random generators and C ``srand()`` function. - -- New :doc:`cppcoreguidelines-avoid-goto - ` check. - - The usage of ``goto`` for control flow is error prone and should be replaced - with looping constructs. Every backward jump is rejected. Forward jumps are - only allowed in nested loops. - -- New :doc:`cppcoreguidelines-narrowing-conversions - ` check - - Checks for narrowing conversions, e. g. ``int i = 0; i += 0.1;``. - -- New :doc:`fuchsia-multiple-inheritance - ` check. - - Warns if a class inherits from multiple classes that are not pure virtual. - -- New `fuchsia-restrict-system-includes - `_ check - - Checks for allowed system includes and suggests removal of any others. - -- New `fuchsia-statically-constructed-objects - `_ check - - Warns if global, non-trivial objects with static storage are constructed, - unless the object is statically initialized with a ``constexpr`` constructor - or has no explicit constructor. - -- New :doc:`fuchsia-trailing-return - ` check. - - Functions that have trailing returns are disallowed, except for those - using ``decltype`` specifiers and lambda with otherwise unutterable - return types. - -- New :doc:`hicpp-multiway-paths-covered - ` check. - - Checks on ``switch`` and ``if`` - ``else if`` constructs that do not cover all possible code paths. - -- New :doc:`modernize-use-uncaught-exceptions - ` check. - - Finds and replaces deprecated uses of ``std::uncaught_exception`` to - ``std::uncaught_exceptions``. - -- New :doc:`portability-simd-intrinsics - ` check. - - Warns or suggests alternatives if SIMD intrinsics are used which can be replaced by - ``std::experimental::simd`` operations. - -- New :doc:`readability-simplify-subscript-expr - ` check. - - Simplifies subscript expressions like ``s.data()[i]`` into ``s[i]``. - -- New :doc:`zircon-temporary-objects - ` check. - - Warns on construction of specific temporary objects in the Zircon kernel. - -- Added the missing bitwise assignment operations to - :doc:`hicpp-signed-bitwise `. - -- New option `MinTypeNameLength` for :doc:`modernize-use-auto - ` check to limit the minimal length of - type names to be replaced with ``auto``. Use to skip replacing short type - names like ``int``/``bool`` with ``auto``. Default value is 5 which means - replace types with the name length >= 5 letters only (ex. ``double``, - ``unsigned``). - -- Add `VariableThreshold` option to :doc:`readability-function-size - ` check. - - Flags functions that have more than a specified number of variables declared - in the body. - -- The `AnalyzeTemporaryDtors` option was removed, since the corresponding - `cfg-temporary-dtors` option of the Static Analyzer now defaults to `true`. - -- New alias :doc:`fuchsia-header-anon-namespaces - ` to :doc:`google-build-namespaces - ` - added. - -- New alias :doc:`hicpp-avoid-goto - ` to :doc:`cppcoreguidelines-avoid-goto - ` - added. - -- Removed the `google-readability-redundant-smartptr-get` alias of the - :doc:`readability-redundant-smartptr-get - ` check. - -- The 'misc-forwarding-reference-overload' check was renamed to :doc:`bugprone-forwarding-reference-overload - ` - -- The 'misc-incorrect-roundings' check was renamed to :doc:`bugprone-incorrect-roundings - ` - -- The 'misc-lambda-function-name' check was renamed to :doc:`bugprone-lambda-function-name - ` - -- The 'misc-macro-parentheses' check was renamed to :doc:`bugprone-macro-parentheses - ` - -- The 'misc-macro-repeated-side-effects' check was renamed to :doc:`bugprone-macro-repeated-side-effects - ` - -- The 'misc-misplaced-widening-cast' check was renamed to :doc:`bugprone-misplaced-widening-cast - ` - -- The 'misc-sizeof-container' check was renamed to :doc:`bugprone-sizeof-container - ` - -- The 'misc-sizeof-expression' check was renamed to :doc:`bugprone-sizeof-expression - ` - -- The 'misc-string-compare' check was renamed to :doc:`readability-string-compare - ` - -- The 'misc-string-integer-assignment' check was renamed to :doc:`bugprone-string-integer-assignment - ` - -- The 'misc-string-literal-with-embedded-nul' check was renamed to :doc:`bugprone-string-literal-with-embedded-nul - ` - -- The 'misc-suspicious-enum-usage' check was renamed to :doc:`bugprone-suspicious-enum-usage - ` - -- The 'misc-suspicious-missing-comma' check was renamed to :doc:`bugprone-suspicious-missing-comma - ` - -- The 'misc-suspicious-semicolon' check was renamed to :doc:`bugprone-suspicious-semicolon - ` - -- The 'misc-suspicious-string-compare' check was renamed to :doc:`bugprone-suspicious-string-compare - ` - -- The 'misc-swapped-arguments' check was renamed to :doc:`bugprone-swapped-arguments - ` - -- The 'misc-undelegated-constructor' check was renamed to :doc:`bugprone-undelegated-constructor - ` - -- The 'misc-unused-raii' check was renamed to :doc:`bugprone-unused-raii - ` - -- The 'google-runtime-member-string-references' check was removed. +The improvements are... Improvements to include-fixer ----------------------------- diff --git a/docs/conf.py b/docs/conf.py index 5ac182169..1b07e8efe 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,9 +49,9 @@ # built documents. # # The short version. -version = '7' +version = '8' # The full version, including alpha/beta/rc tags. -release = '7' +release = '8' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 1747bfa3da8acf857313412a6f1f59cf5860babd Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Wed, 1 Aug 2018 17:39:29 +0000 Subject: [PATCH 002/686] [clangd] allow clients to control the compilation database by passing in compilationDatabaseChanges in the 'workspace/didChangeConfiguration' request This commit allows clangd to use an in-memory compilation database that's controlled from the LSP client (-compile_args_from=lsp). It extends the 'workspace/didChangeConfiguration' request to allow the client to pass in a compilation database subset that needs to be updated in the workspace. Differential Revision: https://reviews.llvm.org/D49758 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338597 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 102 ++++++++++++++++-- clangd/ClangdLSPServer.h | 53 ++++++++- clangd/GlobalCompilationDatabase.cpp | 24 +++++ clangd/GlobalCompilationDatabase.h | 23 ++++ clangd/Protocol.cpp | 10 +- clangd/Protocol.h | 14 +++ clangd/tool/ClangdMain.cpp | 16 ++- .../did-change-configuration-params.test | 52 +++++++++ 8 files changed, 279 insertions(+), 15 deletions(-) create mode 100644 test/clangd/did-change-configuration-params.test diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index 7374c6d35..99353e6c5 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -135,11 +135,8 @@ void ClangdLSPServer::onExit(ExitParams &Params) { IsDone = true; } void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams &Params) { PathRef File = Params.textDocument.uri.file(); - if (Params.metadata && !Params.metadata->extraFlags.empty()) { - NonCachedCDB.setExtraFlagsForFile(File, - std::move(Params.metadata->extraFlags)); - CDB.invalidate(File); - } + if (Params.metadata && !Params.metadata->extraFlags.empty()) + CDB.setExtraFlagsForFile(File, std::move(Params.metadata->extraFlags)); std::string &Contents = Params.textDocument.text; @@ -250,6 +247,7 @@ void ClangdLSPServer::onDocumentDidClose(DidCloseTextDocumentParams &Params) { PathRef File = Params.textDocument.uri.file(); DraftMgr.removeDraft(File); Server.removeDocument(File); + CDB.invalidate(File); } void ClangdLSPServer::onDocumentOnTypeFormatting( @@ -405,12 +403,29 @@ void ClangdLSPServer::applyConfiguration( const ClangdConfigurationParamsChange &Settings) { // Compilation database change. if (Settings.compilationDatabasePath.hasValue()) { - NonCachedCDB.setCompileCommandsDir( - Settings.compilationDatabasePath.getValue()); - CDB.clear(); + CDB.setCompileCommandsDir(Settings.compilationDatabasePath.getValue()); reparseOpenedFiles(); } + + // Update to the compilation database. + if (Settings.compilationDatabaseChanges) { + const auto &CompileCommandUpdates = *Settings.compilationDatabaseChanges; + bool ShouldReparseOpenFiles = false; + for (auto &Entry : CompileCommandUpdates) { + /// The opened files need to be reparsed only when some existing + /// entries are changed. + PathRef File = Entry.first; + if (!CDB.setCompilationCommandForFile( + File, tooling::CompileCommand( + std::move(Entry.second.workingDirectory), File, + std::move(Entry.second.compilationCommand), + /*Output=*/""))) + ShouldReparseOpenFiles = true; + } + if (ShouldReparseOpenFiles) + reparseOpenedFiles(); + } } // FIXME: This function needs to be properly tested. @@ -422,10 +437,13 @@ void ClangdLSPServer::onChangeConfiguration( ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts, llvm::Optional CompileCommandsDir, + bool ShouldUseInMemoryCDB, const ClangdServer::Options &Opts) - : Out(Out), NonCachedCDB(std::move(CompileCommandsDir)), CDB(NonCachedCDB), + : Out(Out), CDB(ShouldUseInMemoryCDB ? CompilationDB::makeInMemory() + : CompilationDB::makeDirectoryBased( + std::move(CompileCommandsDir))), CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()), - Server(CDB, FSProvider, /*DiagConsumer=*/*this, Opts) {} + Server(CDB.getCDB(), FSProvider, /*DiagConsumer=*/*this, Opts) {} bool ClangdLSPServer::run(std::FILE *In, JSONStreamStyle InputStyle) { assert(!IsDone && "Run was called before"); @@ -504,3 +522,67 @@ void ClangdLSPServer::reparseOpenedFiles() { Server.addDocument(FilePath, *DraftMgr.getDraft(FilePath), WantDiagnostics::Auto); } + +ClangdLSPServer::CompilationDB ClangdLSPServer::CompilationDB::makeInMemory() { + return CompilationDB(llvm::make_unique(), nullptr, + /*IsDirectoryBased=*/false); +} + +ClangdLSPServer::CompilationDB +ClangdLSPServer::CompilationDB::makeDirectoryBased( + llvm::Optional CompileCommandsDir) { + auto CDB = llvm::make_unique( + std::move(CompileCommandsDir)); + auto CachingCDB = llvm::make_unique(*CDB); + return CompilationDB(std::move(CDB), std::move(CachingCDB), + /*IsDirectoryBased=*/true); +} + +void ClangdLSPServer::CompilationDB::invalidate(PathRef File) { + if (!IsDirectoryBased) + static_cast(CDB.get())->invalidate(File); + else + CachingCDB->invalidate(File); +} + +bool ClangdLSPServer::CompilationDB::setCompilationCommandForFile( + PathRef File, tooling::CompileCommand CompilationCommand) { + if (IsDirectoryBased) { + elog("Trying to set compile command for {0} while using directory-based " + "compilation database", + File); + return false; + } + return static_cast(CDB.get()) + ->setCompilationCommandForFile(File, std::move(CompilationCommand)); +} + +void ClangdLSPServer::CompilationDB::setExtraFlagsForFile( + PathRef File, std::vector ExtraFlags) { + if (!IsDirectoryBased) { + elog("Trying to set extra flags for {0} while using in-memory compilation " + "database", + File); + return; + } + static_cast(CDB.get()) + ->setExtraFlagsForFile(File, std::move(ExtraFlags)); + CachingCDB->invalidate(File); +} + +void ClangdLSPServer::CompilationDB::setCompileCommandsDir(Path P) { + if (!IsDirectoryBased) { + elog("Trying to set compile commands dir while using in-memory compilation " + "database"); + return; + } + static_cast(CDB.get()) + ->setCompileCommandsDir(P); + CachingCDB->clear(); +} + +GlobalCompilationDatabase &ClangdLSPServer::CompilationDB::getCDB() { + if (CachingCDB) + return *CachingCDB; + return *CDB; +} diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h index 644e7628c..3850e4a97 100644 --- a/clangd/ClangdLSPServer.h +++ b/clangd/ClangdLSPServer.h @@ -35,7 +35,7 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { /// for compile_commands.json in all parent directories of each file. ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts, llvm::Optional CompileCommandsDir, - const ClangdServer::Options &Opts); + bool ShouldUseInMemoryCDB, const ClangdServer::Options &Opts); /// Run LSP server loop, receiving input for it from \p In. \p In must be /// opened in binary mode. Output will be written using Out variable passed to @@ -100,10 +100,57 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { /// Caches FixIts per file and diagnostics llvm::StringMap FixItsMap; + /// Encapsulates the directory-based or the in-memory compilation database + /// that's used by the LSP server. + class CompilationDB { + public: + static CompilationDB makeInMemory(); + static CompilationDB + makeDirectoryBased(llvm::Optional CompileCommandsDir); + + void invalidate(PathRef File); + + /// Sets the compilation command for a particular file. + /// Only valid for in-memory CDB, no-op and error log on DirectoryBasedCDB. + /// + /// \returns True if the File had no compilation command before. + bool + setCompilationCommandForFile(PathRef File, + tooling::CompileCommand CompilationCommand); + + /// Adds extra compilation flags to the compilation command for a particular + /// file. Only valid for directory-based CDB, no-op and error log on + /// InMemoryCDB; + void setExtraFlagsForFile(PathRef File, + std::vector ExtraFlags); + + /// Set the compile commands directory to \p P. + /// Only valid for directory-based CDB, no-op and error log on InMemoryCDB; + void setCompileCommandsDir(Path P); + + /// Returns a CDB that should be used to get compile commands for the + /// current instance of ClangdLSPServer. + GlobalCompilationDatabase &getCDB(); + + private: + CompilationDB(std::unique_ptr CDB, + std::unique_ptr CachingCDB, + bool IsDirectoryBased) + : CDB(std::move(CDB)), CachingCDB(std::move(CachingCDB)), + IsDirectoryBased(IsDirectoryBased) {} + + // if IsDirectoryBased is true, an instance of InMemoryCDB. + // If IsDirectoryBased is false, an instance of DirectoryBasedCDB. + // unique_ptr CDB; + std::unique_ptr CDB; + // Non-null only for directory-based CDB + std::unique_ptr CachingCDB; + bool IsDirectoryBased; + }; + // Various ClangdServer parameters go here. It's important they're created // before ClangdServer. - DirectoryBasedGlobalCompilationDatabase NonCachedCDB; - CachingCompilationDb CDB; + CompilationDB CDB; RealFileSystemProvider FSProvider; /// Options used for code completion diff --git a/clangd/GlobalCompilationDatabase.cpp b/clangd/GlobalCompilationDatabase.cpp index be03e4c24..3bfc4eca1 100644 --- a/clangd/GlobalCompilationDatabase.cpp +++ b/clangd/GlobalCompilationDatabase.cpp @@ -152,5 +152,29 @@ void CachingCompilationDb::clear() { Cached.clear(); } +llvm::Optional +InMemoryCompilationDb::getCompileCommand(PathRef File) const { + std::lock_guard Lock(Mutex); + auto It = Commands.find(File); + if (It == Commands.end()) + return None; + return It->second; +} + +bool InMemoryCompilationDb::setCompilationCommandForFile( + PathRef File, tooling::CompileCommand CompilationCommand) { + std::unique_lock Lock(Mutex); + auto ItInserted = Commands.insert(std::make_pair(File, CompilationCommand)); + if (ItInserted.second) + return true; + ItInserted.first->setValue(std::move(CompilationCommand)); + return false; +} + +void InMemoryCompilationDb::invalidate(PathRef File) { + std::unique_lock Lock(Mutex); + Commands.erase(File); +} + } // namespace clangd } // namespace clang diff --git a/clangd/GlobalCompilationDatabase.h b/clangd/GlobalCompilationDatabase.h index ab89a185b..b64028fc6 100644 --- a/clangd/GlobalCompilationDatabase.h +++ b/clangd/GlobalCompilationDatabase.h @@ -113,6 +113,29 @@ class CachingCompilationDb : public GlobalCompilationDatabase { Cached; /* GUARDED_BY(Mut) */ }; +/// Gets compile args from an in-memory mapping based on a filepath. Typically +/// used by clients who provide the compile commands themselves. +class InMemoryCompilationDb : public GlobalCompilationDatabase { +public: + /// Gets compile command for \p File from the stored mapping. + llvm::Optional + getCompileCommand(PathRef File) const override; + + /// Sets the compilation command for a particular file. + /// + /// \returns True if the File had no compilation command before. + bool setCompilationCommandForFile(PathRef File, + tooling::CompileCommand CompilationCommand); + + /// Removes the compilation command for \p File if it's present in the + /// mapping. + void invalidate(PathRef File); + +private: + mutable std::mutex Mutex; + llvm::StringMap Commands; /* GUARDED_BY(Mut) */ +}; + } // namespace clangd } // namespace clang diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp index 5ecd7195c..202fbfb08 100644 --- a/clangd/Protocol.cpp +++ b/clangd/Protocol.cpp @@ -591,10 +591,18 @@ bool fromJSON(const json::Value &Params, DidChangeConfigurationParams &CCP) { return O && O.map("settings", CCP.settings); } +bool fromJSON(const llvm::json::Value &Params, + ClangdCompileCommand &CDbUpdate) { + json::ObjectMapper O(Params); + return O && O.map("workingDirectory", CDbUpdate.workingDirectory) && + O.map("compilationCommand", CDbUpdate.compilationCommand); +} + bool fromJSON(const json::Value &Params, ClangdConfigurationParamsChange &CCPC) { json::ObjectMapper O(Params); - return O && O.map("compilationDatabasePath", CCPC.compilationDatabasePath); + return O && O.map("compilationDatabasePath", CCPC.compilationDatabasePath) && + O.map("compilationDatabaseChanges", CCPC.compilationDatabaseChanges); } } // namespace clangd diff --git a/clangd/Protocol.h b/clangd/Protocol.h index 7f33976de..5056dbef8 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -322,11 +322,25 @@ struct ClientCapabilities { bool fromJSON(const llvm::json::Value &, ClientCapabilities &); +/// Clangd extension that's used in the 'compilationDatabaseChanges' in +/// workspace/didChangeConfiguration to record updates to the in-memory +/// compilation database. +struct ClangdCompileCommand { + std::string workingDirectory; + std::vector compilationCommand; +}; +bool fromJSON(const llvm::json::Value &, ClangdCompileCommand &); + /// Clangd extension to set clangd-specific "initializationOptions" in the /// "initialize" request and for the "workspace/didChangeConfiguration" /// notification since the data received is described as 'any' type in LSP. struct ClangdConfigurationParamsChange { llvm::Optional compilationDatabasePath; + + // The changes that happened to the compilation database. + // The key of the map is a file name. + llvm::Optional> + compilationDatabaseChanges; }; bool fromJSON(const llvm::json::Value &, ClangdConfigurationParamsChange &); diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 95ffdfad5..9e161f5e8 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -173,6 +173,18 @@ static llvm::cl::opt YamlSymbolFile( "eventually. Don't rely on it."), llvm::cl::init(""), llvm::cl::Hidden); +enum CompileArgsFrom { LSPCompileArgs, FilesystemCompileArgs }; + +static llvm::cl::opt CompileArgsFrom( + "compile_args_from", llvm::cl::desc("The source of compile commands"), + llvm::cl::values(clEnumValN(LSPCompileArgs, "lsp", + "All compile commands come from LSP and " + "'compile_commands.json' files are ignored"), + clEnumValN(FilesystemCompileArgs, "filesystem", + "All compile commands come from the " + "'compile_commands.json' files")), + llvm::cl::init(FilesystemCompileArgs), llvm::cl::Hidden); + int main(int argc, char *argv[]) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) { @@ -289,7 +301,9 @@ int main(int argc, char *argv[]) { } // Initialize and run ClangdLSPServer. - ClangdLSPServer LSPServer(Out, CCOpts, CompileCommandsDirPath, Opts); + ClangdLSPServer LSPServer( + Out, CCOpts, CompileCommandsDirPath, + /*ShouldUseInMemoryCDB=*/CompileArgsFrom == LSPCompileArgs, Opts); constexpr int NoShutdownRequestErrorCode = 1; llvm::set_thread_name("clangd.main"); // Change stdin to binary to not lose \r\n on windows. diff --git a/test/clangd/did-change-configuration-params.test b/test/clangd/did-change-configuration-params.test new file mode 100644 index 000000000..d46c043da --- /dev/null +++ b/test/clangd/did-change-configuration-params.test @@ -0,0 +1,52 @@ +# RUN: clangd -compile_args_from=lsp -lit-test < %s 2> %t | FileCheck -strict-whitespace %s +# RUN: cat %t | FileCheck --check-prefix=ERR %s +# UNSUPPORTED: mingw32,win32 +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabaseChanges":{"/clangd-test/foo.c": {"workingDirectory":"/clangd-test", "compilationCommand": ["clang", "-c", "foo.c"]}}}}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [], +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c" +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///bar.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [], +# CHECK-NEXT: "uri": "file://{{.*}}/bar.c" +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabaseChanges":{"/clangd-test/foo.c": {"workingDirectory":"/clangd-test2", "compilationCommand": ["clang", "-c", "foo.c", "-Wall", "-Werror"]}}}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "message": "variable 'i' is uninitialized when used here", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 28, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 27, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 1 +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c" +# CHECK-NEXT: } +# +# ERR: Updating file {{.*}}foo.c with command [{{.*}}clangd-test2] clang -c foo.c -Wall -Werror +# Don't reparse the second file: +# ERR: Skipping rebuild of the AST for {{.*}}bar.c +--- +{"jsonrpc":"2.0","id":5,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} + + From 5c8876801e26de012bd46ef6e01d36dc8a9d3068 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Thu, 2 Aug 2018 10:30:56 +0000 Subject: [PATCH 003/686] Replace hardcoded format styles in a few tools with the default style in libFormat. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338696 91177308-0d34-0410-b5e6-96231b3b80d8 --- change-namespace/ChangeNamespace.cpp | 3 ++- .../tool/ClangApplyReplacementsMain.cpp | 4 ++-- clang-move/ClangMove.cpp | 3 ++- include-fixer/tool/ClangIncludeFixer.cpp | 6 ++++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/change-namespace/ChangeNamespace.cpp b/change-namespace/ChangeNamespace.cpp index 35c321aed..378ffe1f9 100644 --- a/change-namespace/ChangeNamespace.cpp +++ b/change-namespace/ChangeNamespace.cpp @@ -989,7 +989,8 @@ void ChangeNamespaceTool::onEndOfTranslationUnit() { // Add replacements referring to the changed code to existing replacements, // which refers to the original code. Replaces = Replaces.merge(NewReplacements); - auto Style = format::getStyle("file", FilePath, FallbackStyle); + auto Style = + format::getStyle(format::DefaultFormatStyle, FilePath, FallbackStyle); if (!Style) { llvm::errs() << llvm::toString(Style.takeError()) << "\n"; continue; diff --git a/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp b/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp index 24a430f0d..8977b1315 100644 --- a/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp +++ b/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp @@ -97,8 +97,8 @@ int main(int argc, char **argv) { IntrusiveRefCntPtr(new DiagnosticIDs()), DiagOpts.get()); // Determine a formatting style from options. - auto FormatStyleOrError = - format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM"); + auto FormatStyleOrError = format::getStyle(FormatStyleOpt, FormatStyleConfig, + format::DefaultFallbackStyle); if (!FormatStyleOrError) { llvm::errs() << llvm::toString(FormatStyleOrError.takeError()) << "\n"; return 1; diff --git a/clang-move/ClangMove.cpp b/clang-move/ClangMove.cpp index e126852e3..b7a9363dc 100644 --- a/clang-move/ClangMove.cpp +++ b/clang-move/ClangMove.cpp @@ -795,7 +795,8 @@ void ClangMoveTool::removeDeclsInOldFiles() { // Ignore replacements for new.h/cc. if (SI == FilePathToFileID.end()) continue; llvm::StringRef Code = SM.getBufferData(SI->second); - auto Style = format::getStyle("file", FilePath, Context->FallbackStyle); + auto Style = format::getStyle(format::DefaultFormatStyle, FilePath, + Context->FallbackStyle); if (!Style) { llvm::errs() << llvm::toString(Style.takeError()) << "\n"; continue; diff --git a/include-fixer/tool/ClangIncludeFixer.cpp b/include-fixer/tool/ClangIncludeFixer.cpp index 2f2e45eaa..d9d97d238 100644 --- a/include-fixer/tool/ClangIncludeFixer.cpp +++ b/include-fixer/tool/ClangIncludeFixer.cpp @@ -324,7 +324,8 @@ int includeFixerMain(int argc, const char **argv) { const IncludeFixerContext::HeaderInfo &RHS) { return LHS.QualifiedName == RHS.QualifiedName; }); - auto InsertStyle = format::getStyle("file", Context.getFilePath(), Style); + auto InsertStyle = format::getStyle(format::DefaultFormatStyle, + Context.getFilePath(), Style); if (!InsertStyle) { llvm::errs() << llvm::toString(InsertStyle.takeError()) << "\n"; return 1; @@ -402,7 +403,8 @@ int includeFixerMain(int argc, const char **argv) { std::vector FixerReplacements; for (const auto &Context : Contexts) { StringRef FilePath = Context.getFilePath(); - auto InsertStyle = format::getStyle("file", FilePath, Style); + auto InsertStyle = + format::getStyle(format::DefaultFormatStyle, FilePath, Style); if (!InsertStyle) { llvm::errs() << llvm::toString(InsertStyle.takeError()) << "\n"; return 1; From dd33669b5b31d1c2aa486d6af3ec3c5c92fd3818 Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Thu, 2 Aug 2018 17:17:19 +0000 Subject: [PATCH 004/686] [clang-doc] Refactoring mapper to map by scope The result of this adjusted mapper pass is that all Function and Enum infos are absorbed into the info of their enclosing scope (i.e. the class or namespace in which they are defined). Namespace and Record infos are passed along to the final output, but the second pass creates a reference to each in its parent scope. As a result, the top-level final outputs are Namespaces and Records. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338738 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/BitcodeReader.cpp | 81 ++- clang-doc/BitcodeReader.h | 3 +- clang-doc/BitcodeWriter.cpp | 14 + clang-doc/BitcodeWriter.h | 26 +- clang-doc/Mapper.cpp | 12 +- clang-doc/Representation.cpp | 70 ++- clang-doc/Representation.h | 37 ++ clang-doc/Serialize.cpp | 142 +++-- clang-doc/Serialize.h | 22 +- clang-doc/YAMLGenerator.cpp | 14 +- clang-doc/gen_tests.py | 20 +- clang-doc/tool/ClangDocMain.cpp | 82 ++- test/clang-doc/bc-comment.cpp | 282 ++++----- test/clang-doc/bc-linkage.cpp | 844 ++++++++++++++++++++++++++ test/clang-doc/bc-module.cpp | 87 +++ test/clang-doc/bc-namespace.cpp | 172 +++--- test/clang-doc/bc-record.cpp | 390 ++++++------ test/clang-doc/mapper-comment.cpp | 72 +-- test/clang-doc/mapper-linkage.cpp | 402 ++++++++++++ test/clang-doc/mapper-module.cpp | 51 ++ test/clang-doc/mapper-namespace.cpp | 154 ++--- test/clang-doc/mapper-record.cpp | 327 ++++------ test/clang-doc/module.cpp | 61 -- test/clang-doc/public-comment.cpp | 138 +++++ test/clang-doc/public-linkage.cpp | 299 +++++++++ test/clang-doc/public-module.cpp | 84 ++- test/clang-doc/public-namespace.cpp | 96 +++ test/clang-doc/public-record.cpp | 208 +++++++ test/clang-doc/public-records.cpp | 341 ----------- test/clang-doc/test_cases/linkage.cpp | 95 +++ test/clang-doc/test_cases/module.cpp | 15 + test/clang-doc/yaml-comment.cpp | 176 +++--- test/clang-doc/yaml-linkage.cpp | 529 ++++++++++++++++ test/clang-doc/yaml-module.cpp | 63 ++ test/clang-doc/yaml-namespace.cpp | 123 ++-- test/clang-doc/yaml-record.cpp | 246 ++++---- 36 files changed, 4149 insertions(+), 1629 deletions(-) create mode 100644 test/clang-doc/bc-linkage.cpp create mode 100644 test/clang-doc/bc-module.cpp create mode 100644 test/clang-doc/mapper-linkage.cpp create mode 100644 test/clang-doc/mapper-module.cpp delete mode 100644 test/clang-doc/module.cpp create mode 100644 test/clang-doc/public-comment.cpp create mode 100644 test/clang-doc/public-linkage.cpp create mode 100644 test/clang-doc/public-namespace.cpp create mode 100644 test/clang-doc/public-record.cpp delete mode 100644 test/clang-doc/public-records.cpp create mode 100644 test/clang-doc/test_cases/linkage.cpp create mode 100644 test/clang-doc/test_cases/module.cpp create mode 100644 test/clang-doc/yaml-linkage.cpp create mode 100644 test/clang-doc/yaml-module.cpp diff --git a/clang-doc/BitcodeReader.cpp b/clang-doc/BitcodeReader.cpp index fa51d0135..70e92c7a6 100644 --- a/clang-doc/BitcodeReader.cpp +++ b/clang-doc/BitcodeReader.cpp @@ -100,6 +100,8 @@ bool decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { case FieldId::F_parent: case FieldId::F_vparent: case FieldId::F_type: + case FieldId::F_child_namespace: + case FieldId::F_child_record: case FieldId::F_default: Field = F; return true; @@ -372,6 +374,12 @@ template <> void addReference(NamespaceInfo *I, Reference &&R, FieldId F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); break; + case FieldId::F_child_namespace: + I->ChildNamespaces.emplace_back(std::move(R)); + break; + case FieldId::F_child_record: + I->ChildRecords.emplace_back(std::move(R)); + break; default: llvm::errs() << "Invalid field type for info.\n"; exit(1); @@ -403,12 +411,37 @@ template <> void addReference(RecordInfo *I, Reference &&R, FieldId F) { case FieldId::F_vparent: I->VirtualParents.emplace_back(std::move(R)); break; + case FieldId::F_child_record: + I->ChildRecords.emplace_back(std::move(R)); + break; default: llvm::errs() << "Invalid field type for info.\n"; exit(1); } } +template +void addChild(T I, ChildInfoType &&R) { + llvm::errs() << "Invalid child type for info.\n"; + exit(1); +} + +template <> void addChild(NamespaceInfo *I, FunctionInfo &&R) { + I->ChildFunctions.emplace_back(std::move(R)); +} + +template <> void addChild(NamespaceInfo *I, EnumInfo &&R) { + I->ChildEnums.emplace_back(std::move(R)); +} + +template <> void addChild(RecordInfo *I, FunctionInfo &&R) { + I->ChildFunctions.emplace_back(std::move(R)); +} + +template <> void addChild(RecordInfo *I, EnumInfo &&R) { + I->ChildEnums.emplace_back(std::move(R)); +} + // Read records from bitcode into a given info. template bool ClangDocBitcodeReader::readRecord(unsigned ID, T I) { Record R; @@ -455,7 +488,8 @@ template bool ClangDocBitcodeReader::readBlock(unsigned ID, T I) { template bool ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) { switch (ID) { - // Blocks can only have Comment, Reference, or TypeInfo subblocks + // Blocks can only have Comment, Reference, TypeInfo, FunctionInfo, or + // EnumInfo subblocks case BI_COMMENT_BLOCK_ID: if (readBlock(ID, getCommentInfo(I))) return true; @@ -492,6 +526,22 @@ bool ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) { } return false; } + case BI_FUNCTION_BLOCK_ID: { + FunctionInfo F; + if (readBlock(ID, &F)) { + addChild(I, std::move(F)); + return true; + } + return false; + } + case BI_ENUM_BLOCK_ID: { + EnumInfo E; + if (readBlock(ID, &E)) { + addChild(I, std::move(E)); + return true; + } + return false; + } default: llvm::errs() << "Invalid subblock type.\n"; return false; @@ -573,16 +623,21 @@ std::unique_ptr ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { } // Entry point -std::vector> ClangDocBitcodeReader::readBitcode() { +llvm::Expected>> +ClangDocBitcodeReader::readBitcode() { std::vector> Infos; if (!validateStream()) - return Infos; + return llvm::make_error("Invalid bitcode stream.\n", + llvm::inconvertibleErrorCode()); + ; // Read the top level blocks. while (!Stream.AtEndOfStream()) { unsigned Code = Stream.ReadCode(); if (Code != llvm::bitc::ENTER_SUBBLOCK) - return Infos; + return llvm::make_error( + "Missing subblock in bitcode.\n", llvm::inconvertibleErrorCode()); + ; unsigned ID = Stream.ReadSubBlockID(); switch (ID) { @@ -592,24 +647,30 @@ std::vector> ClangDocBitcodeReader::readBitcode() { case BI_MEMBER_TYPE_BLOCK_ID: case BI_COMMENT_BLOCK_ID: case BI_REFERENCE_BLOCK_ID: - llvm::errs() << "Invalid top level block.\n"; - return Infos; + return llvm::make_error( + "Invalid top level block in bitcode.\n", + llvm::inconvertibleErrorCode()); + ; case BI_NAMESPACE_BLOCK_ID: case BI_RECORD_BLOCK_ID: case BI_ENUM_BLOCK_ID: case BI_FUNCTION_BLOCK_ID: - if (std::unique_ptr I = readBlockToInfo(ID)) { + if (std::unique_ptr I = readBlockToInfo(ID)) Infos.emplace_back(std::move(I)); - } return Infos; case BI_VERSION_BLOCK_ID: if (readBlock(ID, VersionNumber)) continue; - return Infos; + return llvm::make_error( + "Invalid bitcode version in bitcode.\n", + llvm::inconvertibleErrorCode()); + ; case llvm::bitc::BLOCKINFO_BLOCK_ID: if (readBlockInfoBlock()) continue; - return Infos; + return llvm::make_error( + "Invalid BlockInfo in bitcode.\n", llvm::inconvertibleErrorCode()); + ; default: if (!Stream.SkipBlock()) continue; diff --git a/clang-doc/BitcodeReader.h b/clang-doc/BitcodeReader.h index c0cf24a17..aaf25257c 100644 --- a/clang-doc/BitcodeReader.h +++ b/clang-doc/BitcodeReader.h @@ -19,6 +19,7 @@ #include "BitcodeWriter.h" #include "Representation.h" #include "clang/AST/AST.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Bitcode/BitstreamReader.h" @@ -31,7 +32,7 @@ class ClangDocBitcodeReader { ClangDocBitcodeReader(llvm::BitstreamCursor &Stream) : Stream(Stream) {} // Main entry point, calls readBlock to read each block in the given stream. - std::vector> readBitcode(); + llvm::Expected>> readBitcode(); private: enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin }; diff --git a/clang-doc/BitcodeWriter.cpp b/clang-doc/BitcodeWriter.cpp index 623ed1a2a..f73724e4f 100644 --- a/clang-doc/BitcodeWriter.cpp +++ b/clang-doc/BitcodeWriter.cpp @@ -434,6 +434,14 @@ void ClangDocBitcodeWriter::emitBlock(const NamespaceInfo &I) { emitBlock(N, FieldId::F_namespace); for (const auto &CI : I.Description) emitBlock(CI); + for (const auto &C : I.ChildNamespaces) + emitBlock(C, FieldId::F_child_namespace); + for (const auto &C : I.ChildRecords) + emitBlock(C, FieldId::F_child_record); + for (const auto &C : I.ChildFunctions) + emitBlock(C); + for (const auto &C : I.ChildEnums) + emitBlock(C); } void ClangDocBitcodeWriter::emitBlock(const EnumInfo &I) { @@ -472,6 +480,12 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) { emitBlock(P, FieldId::F_parent); for (const auto &P : I.VirtualParents) emitBlock(P, FieldId::F_vparent); + for (const auto &C : I.ChildRecords) + emitBlock(C, FieldId::F_child_record); + for (const auto &C : I.ChildFunctions) + emitBlock(C); + for (const auto &C : I.ChildEnums) + emitBlock(C); } void ClangDocBitcodeWriter::emitBlock(const FunctionInfo &I) { diff --git a/clang-doc/BitcodeWriter.h b/clang-doc/BitcodeWriter.h index 8aa38e142..2ff46c612 100644 --- a/clang-doc/BitcodeWriter.h +++ b/clang-doc/BitcodeWriter.h @@ -68,11 +68,10 @@ enum BlockId { // New Ids need to be added to the enum here, and to the relevant IdNameMap and // initialization list in the implementation file. -#define INFORECORDS(X) X##_USR, X##_NAME - enum RecordId { VERSION = 1, - INFORECORDS(FUNCTION), + FUNCTION_USR, + FUNCTION_NAME, FUNCTION_DEFLOCATION, FUNCTION_LOCATION, FUNCTION_ACCESS, @@ -91,13 +90,16 @@ enum RecordId { FIELD_TYPE_NAME, MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS, - INFORECORDS(NAMESPACE), - INFORECORDS(ENUM), + NAMESPACE_USR, + NAMESPACE_NAME, + ENUM_USR, + ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION, ENUM_MEMBER, ENUM_SCOPED, - INFORECORDS(RECORD), + RECORD_USR, + RECORD_NAME, RECORD_DEFLOCATION, RECORD_LOCATION, RECORD_TAG_TYPE, @@ -112,10 +114,16 @@ enum RecordId { static constexpr unsigned BlockIdCount = BI_LAST - BI_FIRST; static constexpr unsigned RecordIdCount = RI_LAST - RI_FIRST; -#undef INFORECORDS - // Identifiers for differentiating between subblocks -enum class FieldId { F_default, F_namespace, F_parent, F_vparent, F_type }; +enum class FieldId { + F_default, + F_namespace, + F_parent, + F_vparent, + F_type, + F_child_namespace, + F_child_record +}; class ClangDocBitcodeWriter { public: diff --git a/clang-doc/Mapper.cpp b/clang-doc/Mapper.cpp index fb0b42af3..4456c1ed9 100644 --- a/clang-doc/Mapper.cpp +++ b/clang-doc/Mapper.cpp @@ -13,6 +13,7 @@ #include "clang/AST/Comment.h" #include "clang/Index/USRGeneration.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Error.h" using clang::comments::FullComment; @@ -33,14 +34,15 @@ template bool MapASTVisitor::mapDecl(const T *D) { if (index::generateUSRForDecl(D, USR)) return true; - std::string info = serialize::emitInfo( + auto I = serialize::emitInfo( D, getComment(D, D->getASTContext()), getLine(D, D->getASTContext()), getFile(D, D->getASTContext()), CDCtx.PublicOnly); - if (info != "") - CDCtx.ECtx->reportResult( - llvm::toHex(llvm::toStringRef(serialize::hashUSR(USR))), info); - + // A null in place of I indicates that the serializer is skipping this decl + // for some reason (e.g. we're only reporting public decls). + if (I) + CDCtx.ECtx->reportResult(llvm::toHex(llvm::toStringRef(I->USR)), + serialize::serialize(I)); return true; } diff --git a/clang-doc/Representation.cpp b/clang-doc/Representation.cpp index 6107b98ec..f4372dc5b 100644 --- a/clang-doc/Representation.cpp +++ b/clang-doc/Representation.cpp @@ -26,17 +26,70 @@ namespace clang { namespace doc { -static const SymbolID EmptySID = SymbolID(); +namespace { + +const SymbolID EmptySID = SymbolID(); template -std::unique_ptr reduce(std::vector> &Values) { - std::unique_ptr Merged = llvm::make_unique(); +llvm::Expected> +reduce(std::vector> &Values) { + if (Values.empty()) + return llvm::make_error(" No values to reduce.\n", + llvm::inconvertibleErrorCode()); + std::unique_ptr Merged = llvm::make_unique(Values[0]->USR); T *Tmp = static_cast(Merged.get()); for (auto &I : Values) Tmp->merge(std::move(*static_cast(I.get()))); return Merged; } +// Return the index of the matching child in the vector, or -1 if merge is not +// necessary. +template +int getChildIndexIfExists(std::vector &Children, T &ChildToMerge) { + for (unsigned long I = 0; I < Children.size(); I++) { + if (ChildToMerge.USR == Children[I].USR) + return I; + } + return -1; +} + +// For References, we don't need to actually merge them, we just don't want +// duplicates. +void reduceChildren(std::vector &Children, + std::vector &&ChildrenToMerge) { + for (auto &ChildToMerge : ChildrenToMerge) { + if (getChildIndexIfExists(Children, ChildToMerge) == -1) + Children.push_back(std::move(ChildToMerge)); + } +} + +void reduceChildren(std::vector &Children, + std::vector &&ChildrenToMerge) { + for (auto &ChildToMerge : ChildrenToMerge) { + int mergeIdx = getChildIndexIfExists(Children, ChildToMerge); + if (mergeIdx == -1) { + Children.push_back(std::move(ChildToMerge)); + continue; + } + Children[mergeIdx].merge(std::move(ChildToMerge)); + } +} + +void reduceChildren(std::vector &Children, + std::vector &&ChildrenToMerge) { + for (auto &ChildToMerge : ChildrenToMerge) { + int mergeIdx = getChildIndexIfExists(Children, ChildToMerge); + if (mergeIdx == -1) { + Children.push_back(std::move(ChildToMerge)); + continue; + } + Children[mergeIdx].merge(std::move(ChildToMerge)); + } +} + +} // namespace + // Dispatch function. llvm::Expected> mergeInfos(std::vector> &Values) { @@ -73,7 +126,7 @@ void Info::mergeBase(Info &&Other) { } bool Info::mergeable(const Info &Other) { - return IT == Other.IT && (USR == EmptySID || USR == Other.USR); + return IT == Other.IT && USR == Other.USR; } void SymbolInfo::merge(SymbolInfo &&Other) { @@ -87,6 +140,11 @@ void SymbolInfo::merge(SymbolInfo &&Other) { void NamespaceInfo::merge(NamespaceInfo &&Other) { assert(mergeable(Other)); + // Reduce children if necessary. + reduceChildren(ChildNamespaces, std::move(Other.ChildNamespaces)); + reduceChildren(ChildRecords, std::move(Other.ChildRecords)); + reduceChildren(ChildFunctions, std::move(Other.ChildFunctions)); + reduceChildren(ChildEnums, std::move(Other.ChildEnums)); mergeBase(std::move(Other)); } @@ -100,6 +158,10 @@ void RecordInfo::merge(RecordInfo &&Other) { Parents = std::move(Other.Parents); if (VirtualParents.empty()) VirtualParents = std::move(Other.VirtualParents); + // Reduce children if necessary. + reduceChildren(ChildRecords, std::move(Other.ChildRecords)); + reduceChildren(ChildFunctions, std::move(Other.ChildFunctions)); + reduceChildren(ChildEnums, std::move(Other.ChildEnums)); SymbolInfo::merge(std::move(Other)); } diff --git a/clang-doc/Representation.h b/clang-doc/Representation.h index f952954f9..9e88bd9c8 100644 --- a/clang-doc/Representation.h +++ b/clang-doc/Representation.h @@ -31,6 +31,9 @@ namespace doc { using SymbolID = std::array; struct Info; +struct FunctionInfo; +struct EnumInfo; + enum class InfoType { IT_default, IT_namespace, @@ -153,6 +156,9 @@ struct Location { struct Info { Info() = default; Info(InfoType IT) : IT(IT) {} + Info(InfoType IT, SymbolID USR) : USR(USR), IT(IT) {} + Info(InfoType IT, SymbolID USR, StringRef Name) + : USR(USR), IT(IT), Name(Name) {} Info(const Info &Other) = delete; Info(Info &&Other) = default; @@ -166,18 +172,36 @@ struct Info { void mergeBase(Info &&I); bool mergeable(const Info &Other); + + // Returns a reference to the parent scope (that is, the immediate parent + // namespace or class in which this decl resides). + llvm::Expected getEnclosingScope(); }; // Info for namespaces. struct NamespaceInfo : public Info { NamespaceInfo() : Info(InfoType::IT_namespace) {} + NamespaceInfo(SymbolID USR) : Info(InfoType::IT_namespace, USR) {} + NamespaceInfo(SymbolID USR, StringRef Name) + : Info(InfoType::IT_namespace, USR, Name) {} void merge(NamespaceInfo &&I); + + // Namespaces and Records are references because they will be properly + // documented in their own info, while the entirety of Functions and Enums are + // included here because they should not have separate documentation from + // their scope. + std::vector ChildNamespaces; + std::vector ChildRecords; + std::vector ChildFunctions; + std::vector ChildEnums; }; // Info for symbols. struct SymbolInfo : public Info { SymbolInfo(InfoType IT) : Info(IT) {} + SymbolInfo(InfoType IT, SymbolID USR) : Info(IT, USR) {} + SymbolInfo(InfoType IT, SymbolID USR, StringRef Name) : Info(IT, USR, Name) {} void merge(SymbolInfo &&I); @@ -189,6 +213,7 @@ struct SymbolInfo : public Info { // Info for functions. struct FunctionInfo : public SymbolInfo { FunctionInfo() : SymbolInfo(InfoType::IT_function) {} + FunctionInfo(SymbolID USR) : SymbolInfo(InfoType::IT_function, USR) {} void merge(FunctionInfo &&I); @@ -205,6 +230,9 @@ struct FunctionInfo : public SymbolInfo { // Info for types. struct RecordInfo : public SymbolInfo { RecordInfo() : SymbolInfo(InfoType::IT_record) {} + RecordInfo(SymbolID USR) : SymbolInfo(InfoType::IT_record, USR) {} + RecordInfo(SymbolID USR, StringRef Name) + : SymbolInfo(InfoType::IT_record, USR, Name) {} void merge(RecordInfo &&I); @@ -218,12 +246,21 @@ struct RecordInfo : public SymbolInfo { // parents). llvm::SmallVector VirtualParents; // List of virtual base/parent records. + + // Records are references because they will be properly + // documented in their own info, while the entirety of Functions and Enums are + // included here because they should not have separate documentation from + // their scope. + std::vector ChildRecords; + std::vector ChildFunctions; + std::vector ChildEnums; }; // TODO: Expand to allow for documenting templating. // Info for types. struct EnumInfo : public SymbolInfo { EnumInfo() : SymbolInfo(InfoType::IT_enum) {} + EnumInfo(SymbolID USR) : SymbolInfo(InfoType::IT_enum, USR) {} void merge(EnumInfo &&I); diff --git a/clang-doc/Serialize.cpp b/clang-doc/Serialize.cpp index c1e6d316f..b12961463 100644 --- a/clang-doc/Serialize.cpp +++ b/clang-doc/Serialize.cpp @@ -152,6 +152,21 @@ template static std::string serialize(T &I) { return Buffer.str().str(); } +std::string serialize(std::unique_ptr &I) { + switch (I->IT) { + case InfoType::IT_namespace: + return serialize(*static_cast(I.get())); + case InfoType::IT_record: + return serialize(*static_cast(I.get())); + case InfoType::IT_enum: + return serialize(*static_cast(I.get())); + case InfoType::IT_function: + return serialize(*static_cast(I.get())); + default: + return ""; + } +} + static void parseFullComment(const FullComment *C, CommentInfo &CI) { ClangDocCommentVisitor Visitor(CI); Visitor.parseComment(C); @@ -306,61 +321,108 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, parseParameters(I, D); } -std::string emitInfo(const NamespaceDecl *D, const FullComment *FC, - int LineNumber, llvm::StringRef File, bool PublicOnly) { +std::unique_ptr emitInfo(const NamespaceDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, + bool PublicOnly) { if (PublicOnly && ((D->isAnonymousNamespace()) || !isPublic(D->getAccess(), D->getLinkageInternal()))) - return ""; - NamespaceInfo I; - populateInfo(I, D, FC); - return serialize(I); + return nullptr; + auto I = llvm::make_unique(); + populateInfo(*I, D, FC); + return I; } -std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool PublicOnly) { +std::unique_ptr emitInfo(const RecordDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, + bool PublicOnly) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) - return ""; - RecordInfo I; - populateSymbolInfo(I, D, FC, LineNumber, File); - I.TagType = D->getTagKind(); - parseFields(I, D, PublicOnly); + return nullptr; + auto I = llvm::make_unique(); + populateSymbolInfo(*I, D, FC, LineNumber, File); + I->TagType = D->getTagKind(); + parseFields(*I, D, PublicOnly); if (const auto *C = dyn_cast(D)) - parseBases(I, C); - return serialize(I); + parseBases(*I, C); + return I; } -std::string emitInfo(const FunctionDecl *D, const FullComment *FC, - int LineNumber, llvm::StringRef File, bool PublicOnly) { +std::unique_ptr emitInfo(const FunctionDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, + bool PublicOnly) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) - return ""; - FunctionInfo I; - populateFunctionInfo(I, D, FC, LineNumber, File); - I.Access = clang::AccessSpecifier::AS_none; - return serialize(I); + return nullptr; + FunctionInfo Func; + populateFunctionInfo(Func, D, FC, LineNumber, File); + Func.Access = clang::AccessSpecifier::AS_none; + + // Wrap in enclosing scope + auto I = llvm::make_unique(); + if (!Func.Namespace.empty()) + I->USR = Func.Namespace[0].USR; + else + I->USR = SymbolID(); + I->ChildFunctions.push_back(std::move(Func)); + return I; } -std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC, - int LineNumber, llvm::StringRef File, bool PublicOnly) { +std::unique_ptr emitInfo(const CXXMethodDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, + bool PublicOnly) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) - return ""; - FunctionInfo I; - populateFunctionInfo(I, D, FC, LineNumber, File); - I.IsMethod = true; - I.Parent = Reference{getUSRForDecl(D->getParent()), - D->getParent()->getNameAsString(), InfoType::IT_record}; - I.Access = D->getAccess(); - return serialize(I); + return nullptr; + FunctionInfo Func; + populateFunctionInfo(Func, D, FC, LineNumber, File); + Func.IsMethod = true; + + SymbolID ParentUSR = getUSRForDecl(D->getParent()); + Func.Parent = Reference{ParentUSR, D->getParent()->getNameAsString(), + InfoType::IT_record}; + Func.Access = D->getAccess(); + + // Wrap in enclosing scope + auto I = llvm::make_unique(); + I->USR = ParentUSR; + I->ChildFunctions.push_back(std::move(Func)); + return I; } -std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool PublicOnly) { +std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, + bool PublicOnly) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) - return ""; - EnumInfo I; - populateSymbolInfo(I, D, FC, LineNumber, File); - I.Scoped = D->isScoped(); - parseEnumerators(I, D); - return serialize(I); + return nullptr; + EnumInfo Enum; + populateSymbolInfo(Enum, D, FC, LineNumber, File); + Enum.Scoped = D->isScoped(); + parseEnumerators(Enum, D); + + // Wrap in enclosing scope + if (!Enum.Namespace.empty()) { + switch (Enum.Namespace[0].RefType) { + case InfoType::IT_namespace: { + std::unique_ptr IPtr = llvm::make_unique(); + NamespaceInfo *I = static_cast(IPtr.get()); + I->USR = Enum.Namespace[0].USR; + I->ChildEnums.push_back(std::move(Enum)); + return IPtr; + } + case InfoType::IT_record: { + std::unique_ptr IPtr = llvm::make_unique(); + RecordInfo *I = static_cast(IPtr.get()); + I->USR = Enum.Namespace[0].USR; + I->ChildEnums.push_back(std::move(Enum)); + return IPtr; + } + default: + break; + } + } + + // Put in global namespace + auto I = llvm::make_unique(); + I->USR = SymbolID(); + I->ChildEnums.push_back(std::move(Enum)); + return I; } } // namespace serialize diff --git a/clang-doc/Serialize.h b/clang-doc/Serialize.h index 5181cf61b..d89dac809 100644 --- a/clang-doc/Serialize.h +++ b/clang-doc/Serialize.h @@ -28,16 +28,16 @@ namespace clang { namespace doc { namespace serialize { -std::string emitInfo(const NamespaceDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); -std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool PublicOnly); -std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool PublicOnly); -std::string emitInfo(const FunctionDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); -std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); +std::unique_ptr emitInfo(const NamespaceDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); +std::unique_ptr emitInfo(const RecordDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); +std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); +std::unique_ptr emitInfo(const FunctionDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); +std::unique_ptr emitInfo(const CXXMethodDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); // Function to hash a given USR value for storage. // As USRs (Unified Symbol Resolution) could be large, especially for functions @@ -46,6 +46,8 @@ std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC, // memory (vs storing USRs directly). SymbolID hashUSR(llvm::StringRef USR); +std::string serialize(std::unique_ptr &I); + } // namespace serialize } // namespace doc } // namespace clang diff --git a/clang-doc/YAMLGenerator.cpp b/clang-doc/YAMLGenerator.cpp index f29b4787d..58c1e1f36 100644 --- a/clang-doc/YAMLGenerator.cpp +++ b/clang-doc/YAMLGenerator.cpp @@ -20,6 +20,8 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(MemberTypeInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(Reference) LLVM_YAML_IS_SEQUENCE_VECTOR(Location) LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo) +LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionInfo) +LLVM_YAML_IS_SEQUENCE_VECTOR(EnumInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>) @@ -175,7 +177,14 @@ template <> struct MappingTraits { }; template <> struct MappingTraits { - static void mapping(IO &IO, NamespaceInfo &I) { InfoMapping(IO, I); } + static void mapping(IO &IO, NamespaceInfo &I) { + InfoMapping(IO, I); + IO.mapOptional("ChildNamespaces", I.ChildNamespaces, + std::vector()); + IO.mapOptional("ChildRecords", I.ChildRecords, std::vector()); + IO.mapOptional("ChildFunctions", I.ChildFunctions); + IO.mapOptional("ChildEnums", I.ChildEnums); + } }; template <> struct MappingTraits { @@ -186,6 +195,9 @@ template <> struct MappingTraits { IO.mapOptional("Parents", I.Parents, llvm::SmallVector()); IO.mapOptional("VirtualParents", I.VirtualParents, llvm::SmallVector()); + IO.mapOptional("ChildRecords", I.ChildRecords, std::vector()); + IO.mapOptional("ChildFunctions", I.ChildFunctions); + IO.mapOptional("ChildEnums", I.ChildEnums); } }; diff --git a/clang-doc/gen_tests.py b/clang-doc/gen_tests.py index 5004892e5..ccdb069c4 100644 --- a/clang-doc/gen_tests.py +++ b/clang-doc/gen_tests.py @@ -18,14 +18,17 @@ To generate all current tests: - Generate mapper tests: - gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -prefix mapper + python gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix mapper - Generate reducer tests: - gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -prefix bc - + python gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix bc + - Generate yaml tests: - gen_tests.py -flag='--format=yaml' -flag='--doxygen' -prefix yaml - + python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix yaml + +- Generate public decl tests: + python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix public + This script was written on/for Linux, and has not been tested on any other platform and so it may not work. @@ -34,6 +37,7 @@ import argparse import glob import os +import re import shutil import subprocess @@ -48,6 +52,10 @@ CHECK_NEXT = '// CHECK-{0}-NEXT: ' +BITCODE_USR = '' +BITCODE_USR_REGEX = r'' +YAML_USR = "USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}'" +YAML_USR_REGEX = r"USR: '[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]'" def clear_test_prefix_files(prefix, tests_path): if os.path.isdir(tests_path): @@ -108,6 +116,8 @@ def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer): # Format output. output = output.replace('blob data = \'test\'', 'blob data = \'{{.*}}\'') + output = re.sub(YAML_USR_REGEX, YAML_USR, output) + output = re.sub(BITCODE_USR_REGEX, BITCODE_USR, output) output = CHECK.format(checkname) + output.rstrip() output = run_cmd + output.replace('\n', '\n' + CHECK_NEXT.format(checkname)) diff --git a/clang-doc/tool/ClangDocMain.cpp b/clang-doc/tool/ClangDocMain.cpp index 9e89e8568..6e4a92d7b 100644 --- a/clang-doc/tool/ClangDocMain.cpp +++ b/clang-doc/tool/ClangDocMain.cpp @@ -121,9 +121,24 @@ bool DumpResultToFile(const Twine &DirName, const Twine &FileName, return false; } +// A function to extract the appropriate path name for a given info's +// documentation. The path returned is a composite of the parent namespaces as +// directories plus the decl name as the filename. +// +// Example: Given the below, the path for class C will be < +// root>/A/B/C. +// +// namespace A { +// namesapce B { +// +// class C {}; +// +// } +// } llvm::Expected> -getPath(StringRef Root, StringRef Ext, StringRef Name, - llvm::SmallVectorImpl &Namespaces) { +getInfoOutputFile(StringRef Root, + llvm::SmallVectorImpl &Namespaces, + StringRef Name, StringRef Ext) { std::error_code OK; llvm::SmallString<128> Path; llvm::sys::path::native(Root, Path); @@ -134,6 +149,8 @@ getPath(StringRef Root, StringRef Ext, StringRef Name, return llvm::make_error("Unable to create directory.\n", llvm::inconvertibleErrorCode()); + if (Name.empty()) + Name = "GlobalNamespace"; llvm::sys::path::append(Path, Name + Ext); return Path; } @@ -146,6 +163,30 @@ std::string getFormatString(OutputFormatTy Ty) { llvm_unreachable("Unknown OutputFormatTy"); } +// Iterate through tool results and build string map of info vectors from the +// encoded bitstreams. +bool bitcodeResultsToInfos( + tooling::ToolResults &Results, + llvm::StringMap>> &Output) { + bool Err = false; + Results.forEachResult([&](StringRef Key, StringRef Value) { + llvm::BitstreamCursor Stream(Value); + doc::ClangDocBitcodeReader Reader(Stream); + auto Infos = Reader.readBitcode(); + if (!Infos) { + llvm::errs() << toString(Infos.takeError()) << "\n"; + Err = true; + return; + } + for (auto &I : Infos.get()) { + auto R = + Output.try_emplace(Key, std::vector>()); + R.first->second.emplace_back(std::move(I)); + } + }); + return Err; +} + int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); std::error_code OK; @@ -196,30 +237,21 @@ int main(int argc, const char **argv) { } // Collect values into output by key. - llvm::outs() << "Collecting infos...\n"; - llvm::StringMap>> MapOutput; - // In ToolResults, the Key is the hashed USR and the value is the // bitcode-encoded representation of the Info object. - Exec->get()->getToolResults()->forEachResult([&](StringRef Key, - StringRef Value) { - llvm::BitstreamCursor Stream(Value); - doc::ClangDocBitcodeReader Reader(Stream); - auto Infos = Reader.readBitcode(); - for (auto &I : Infos) { - auto R = - MapOutput.try_emplace(Key, std::vector>()); - R.first->second.emplace_back(std::move(I)); - } - }); + llvm::outs() << "Collecting infos...\n"; + llvm::StringMap>> USRToInfos; + if (bitcodeResultsToInfos(*Exec->get()->getToolResults(), USRToInfos)) + return 1; - // Reducing and generation phases - llvm::outs() << "Reducing " << MapOutput.size() << " infos...\n"; - llvm::StringMap> ReduceOutput; - for (auto &Group : MapOutput) { + // First reducing phase (reduce all decls into one info per decl). + llvm::outs() << "Reducing " << USRToInfos.size() << " infos...\n"; + for (auto &Group : USRToInfos) { auto Reduced = doc::mergeInfos(Group.getValue()); - if (!Reduced) + if (!Reduced) { llvm::errs() << llvm::toString(Reduced.takeError()); + continue; + } if (DumpIntermediateResult) { SmallString<4096> Buffer; @@ -230,10 +262,10 @@ int main(int argc, const char **argv) { llvm::errs() << "Error dumping to bitcode.\n"; continue; } - - // Create the relevant ostream and emit the documentation for this decl. doc::Info *I = Reduced.get().get(); - auto InfoPath = getPath(OutDirectory, "." + Format, I->Name, I->Namespace); + + auto InfoPath = + getInfoOutputFile(OutDirectory, I->Namespace, I->Name, "." + Format); if (!InfoPath) { llvm::errs() << toString(InfoPath.takeError()) << "\n"; continue; @@ -241,7 +273,7 @@ int main(int argc, const char **argv) { std::error_code FileErr; llvm::raw_fd_ostream InfoOS(InfoPath.get(), FileErr, llvm::sys::fs::F_None); if (FileErr != OK) { - llvm::errs() << "Error opening index file: " << FileErr.message() << "\n"; + llvm::errs() << "Error opening info file: " << FileErr.message() << "\n"; continue; } diff --git a/test/clang-doc/bc-comment.cpp b/test/clang-doc/bc-comment.cpp index fa3ea7f71..3b006ab8a 100644 --- a/test/clang-doc/bc-comment.cpp +++ b/test/clang-doc/bc-comment.cpp @@ -27,176 +27,178 @@ void F(int I, int J); /// Bonus comment on definition void F(int I, int J) {} -// RUN: clang-doc --dump-intermediate --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/7574630614A535710E5A6ABCFFF98BCA2D06A4CA.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'F' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'FullComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'BlockCommandComment' -// CHECK-0-NEXT: blob data = 'brief' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'F' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'FullComment' +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Brief description.' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Extended description that' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' continues onto the next line.' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'HTMLStartTagComment' -// CHECK-0-NEXT: blob data = 'ul' -// CHECK-0-NEXT: blob data = 'class' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'HTMLStartTagComment' -// CHECK-0-NEXT: blob data = 'li' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Testing.' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'HTMLEndTagComment' -// CHECK-0-NEXT: blob data = 'ul' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'VerbatimBlockComment' -// CHECK-0-NEXT: blob data = 'verbatim' -// CHECK-0-NEXT: blob data = 'endverbatim' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'VerbatimBlockLineComment' -// CHECK-0-NEXT: blob data = ' The description continues.' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' --' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'BlockCommandComment' +// CHECK-0-NEXT: blob data = 'brief' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' Brief description.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' Extended description that' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' continues onto the next line.' +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParamCommandComment' -// CHECK-0-NEXT: blob data = '[out]' -// CHECK-0-NEXT: blob data = 'I' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'HTMLStartTagComment' +// CHECK-0-NEXT: blob data = 'ul' +// CHECK-0-NEXT: blob data = 'class' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'HTMLStartTagComment' +// CHECK-0-NEXT: blob data = 'li' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' is a parameter.' +// CHECK-0-NEXT: blob data = ' Testing.' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'HTMLEndTagComment' +// CHECK-0-NEXT: blob data = 'ul' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParamCommandComment' -// CHECK-0-NEXT: blob data = '[in]' -// CHECK-0-NEXT: blob data = 'J' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' is a parameter.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'VerbatimBlockComment' +// CHECK-0-NEXT: blob data = 'verbatim' +// CHECK-0-NEXT: blob data = 'endverbatim' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'VerbatimBlockLineComment' +// CHECK-0-NEXT: blob data = ' The description continues.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' --' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: // CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParamCommandComment' +// CHECK-0-NEXT: blob data = '[out]' +// CHECK-0-NEXT: blob data = 'I' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' is a parameter.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParamCommandComment' +// CHECK-0-NEXT: blob data = '[in]' +// CHECK-0-NEXT: blob data = 'J' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' is a parameter.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'BlockCommandComment' +// CHECK-0-NEXT: blob data = 'return' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'BlockCommandComment' -// CHECK-0-NEXT: blob data = 'return' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'FullComment' +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' void' +// CHECK-0-NEXT: blob data = ' Bonus comment on definition' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'FullComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Bonus comment on definition' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'I' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'J' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'I' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'J' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: diff --git a/test/clang-doc/bc-linkage.cpp b/test/clang-doc/bc-linkage.cpp new file mode 100644 index 000000000..8fec0d351 --- /dev/null +++ b/test/clang-doc/bc-linkage.cpp @@ -0,0 +1,844 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void function(int x); + +inline int inlinedFunction(int x); + +int functionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +inline int inlinedFunctionWithInnerClass(int x) { + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +class Class { +public: + void publicMethod(); + int publicField; + +protected: + void protectedMethod(); + int protectedField; + +private: + void privateMethod(); + int privateField; +}; + +namespace named { +class NamedClass { +public: + void namedPublicMethod(); + int namedPublicField; + +protected: + void namedProtectedMethod(); + int namedProtectedField; + +private: + void namedPrivateMethod(); + int namedPrivateField; +}; + +void namedFunction(); +static void namedStaticFunction(); +inline void namedInlineFunction(); +} // namespace named + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace { +class AnonClass { +public: + void anonPublicMethod(); + int anonPublicField; + +protected: + void anonProtectedMethod(); + int anonProtectedField; + +private: + void anonPrivateMethod(); + int anonPrivateField; +}; + +void anonFunction(); +static void anonStaticFunction(); +inline void anonInlineFunction(); +} // namespace + +// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/C9B3B71ACDD84C5BB320D34E97677715CDB3EA32.bc | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'InnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'inlinedFunctionWithInnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'innerPublicMethod' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'InnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'inlinedFunctionWithInnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'InnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/8960B5C9247D6F5C532756E53A1AD1240FA2146F.bc | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'named' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'namedFunction' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'named' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'void' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'namedStaticFunction' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'named' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'void' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'namedInlineFunction' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'named' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'void' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/74A031CBE68C101F3E83F60ED17F20C11EC19D48.bc | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'InnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'innerPublicMethod' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'InnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'InnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/7CDD73DCD6CD72F7E5CE25502810A182C66C4B45.bc | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'int' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'publicField' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'int' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'protectedField' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'int' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'privateField' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'publicMethod' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'void' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'protectedMethod' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'void' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'privateMethod' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'void' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/85427901413EC77C961019EBB3ADEF7B0BAAFE78.bc | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'functionWithInnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'innerPublicMethod' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'functionWithInnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'int' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 +// CHECK-5: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'function' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'void' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'inlinedFunction' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'functionWithInnerClass' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'inlinedFunctionWithInnerClass' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'staticFunction' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'void' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'staticFunctionWithInnerClass' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/4712C5FA37B298A25501D1033C619B65B0ECC449.bc | FileCheck %s --check-prefix CHECK-6 +// CHECK-6: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'named' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'int' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedPublicField' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'int' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedProtectedField' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'int' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedPrivateField' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedPublicMethod' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'named' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'void' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedProtectedMethod' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'named' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'void' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedPrivateMethod' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'named' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'void' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/6E8FB72A89761E77020BFCEE9A9A6E64B15CC2A9.bc | FileCheck %s --check-prefix CHECK-7 +// CHECK-7: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'int' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonPublicField' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'int' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonProtectedField' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'int' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonPrivateField' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonPublicMethod' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'void' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonProtectedMethod' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'void' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonPrivateMethod' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'void' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/83CC52D32583E0771710A7742DE81C839E953AC8.bc | FileCheck %s --check-prefix CHECK-8 +// CHECK-8: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'anonFunction' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'void' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'anonStaticFunction' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'void' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'anonInlineFunction' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'void' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: diff --git a/test/clang-doc/bc-module.cpp b/test/clang-doc/bc-module.cpp new file mode 100644 index 000000000..101d8da85 --- /dev/null +++ b/test/clang-doc/bc-module.cpp @@ -0,0 +1,87 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +export module M; + +int moduleFunction(int x); // ModuleLinkage + +static int staticModuleFunction(int x); // ModuleInternalLinkage + +export double exportedModuleFunction(double y, int z); // ExternalLinkage + +// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'moduleFunction' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'x' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'staticModuleFunction' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'x' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'exportedModuleFunction' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'double' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'double' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'y' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'z' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: diff --git a/test/clang-doc/bc-namespace.cpp b/test/clang-doc/bc-namespace.cpp index b1c03636c..79b35bd9a 100644 --- a/test/clang-doc/bc-namespace.cpp +++ b/test/clang-doc/bc-namespace.cpp @@ -25,115 +25,97 @@ E func(int i) { return X; } } // namespace B } // namespace A -// RUN: clang-doc --dump-intermediate --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'B' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'A' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: blob data = 'X' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'A' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'f' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'A' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC.bc | FileCheck %s --check-prefix CHECK-1 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'f' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'B' // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'A' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'void' -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'func' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'B' +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/9A82CB33ED0FDF81EE383D31CD0957D153C5E840.bc | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'func' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'B' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'A' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'enum A::B::E' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'int' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'i' -// CHECK-2-NEXT: -// CHECK-2-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'A' -// CHECK-3-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-4 -// CHECK-4: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'B' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'A' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'A' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'enum A::B::E' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'int' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'i' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'E' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'B' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'A' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: blob data = 'X' +// CHECK-1-NEXT: +// CHECK-1-NEXT: diff --git a/test/clang-doc/bc-record.cpp b/test/clang-doc/bc-record.cpp index 7a09118c7..a0e224485 100644 --- a/test/clang-doc/bc-record.cpp +++ b/test/clang-doc/bc-record.cpp @@ -39,7 +39,7 @@ class X { class Y {}; }; -// RUN: clang-doc --dump-intermediate --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs // RUN: llvm-bcanalyzer --dump %t/docs/bc/289584A8E0FF4178A794622A547AA622503967A1.bc | FileCheck %s --check-prefix CHECK-0 @@ -47,11 +47,84 @@ class X { // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' // CHECK-0-NEXT: blob data = '{{.*}}' // CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '~E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ProtectedMethod' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/3FB542274573CAEAD54CEBFFCAEE3D77FB9713D8.bc | FileCheck %s --check-prefix CHECK-1 @@ -60,10 +133,10 @@ class X { // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'I' // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'H' // CHECK-1-NEXT: // CHECK-1-NEXT: @@ -72,250 +145,149 @@ class X { // CHECK-1-NEXT: // CHECK-1-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/5093D428CDC62096A67547BA52566E4FB9404EEE.bc | FileCheck %s --check-prefix CHECK-2 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'ProtectedMethod' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'E' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'E' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'void' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'X' +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-3 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'X' -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'C' +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'int' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'i' +// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E.bc | FileCheck %s --check-prefix CHECK-4 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'H' -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'void' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'Y' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'X' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-5 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'C' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'i' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'H' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'void' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'B' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: blob data = 'X' +// CHECK-5-NEXT: blob data = 'Y' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'Bc' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'A' +// CHECK-5-NEXT: blob data = 'B' +// CHECK-5-NEXT: +// CHECK-5-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17.bc | FileCheck %s --check-prefix CHECK-6 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-6 // CHECK-6: // CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '~E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'void' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'D' +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4.bc | FileCheck %s --check-prefix CHECK-7 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-7 // CHECK-7: // CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'E' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'F' +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: blob data = 'E' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'E' +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'D' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'void' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-8 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-8 // CHECK-8: // CHECK-8-NEXT: // CHECK-8-NEXT: // CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'Y' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'X' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'A' +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'int' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'X' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'int' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'Y' +// CHECK-8-NEXT: +// CHECK-8-NEXT: // CHECK-8-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/FC07BD34D5E77782C263FA944447929EA8753740.bc | FileCheck %s --check-prefix CHECK-9 -// CHECK-9: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: blob data = 'B' -// CHECK-9-NEXT: blob data = '{{.*}}' -// CHECK-9-NEXT: blob data = 'X' -// CHECK-9-NEXT: blob data = 'Y' -// CHECK-9-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-10 -// CHECK-10: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: blob data = 'D' -// CHECK-10-NEXT: blob data = '{{.*}}' -// CHECK-10-NEXT: -// CHECK-10-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-11 -// CHECK-11: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: blob data = 'F' -// CHECK-11-NEXT: blob data = '{{.*}}' -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: blob data = 'E' -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: blob data = 'D' -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-12 -// CHECK-12: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'A' -// CHECK-12-NEXT: blob data = '{{.*}}' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'int' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'X' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'int' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'Y' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/1E3438A08BA22025C0B46289FF0686F92C8924C5.bc | FileCheck %s --check-prefix CHECK-13 -// CHECK-13: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: blob data = 'Bc' -// CHECK-13-NEXT: blob data = '{{.*}}' -// CHECK-13-NEXT: -// CHECK-13-NEXT: blob data = 'A' -// CHECK-13-NEXT: blob data = 'B' -// CHECK-13-NEXT: diff --git a/test/clang-doc/mapper-comment.cpp b/test/clang-doc/mapper-comment.cpp index da691b156..efd3dc54c 100644 --- a/test/clang-doc/mapper-comment.cpp +++ b/test/clang-doc/mapper-comment.cpp @@ -27,46 +27,48 @@ void F(int I, int J); /// Bonus comment on definition void F(int I, int J) {} -// RUN: clang-doc --dump-mapper --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/7574630614A535710E5A6ABCFFF98BCA2D06A4CA.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'F' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'FullComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Bonus comment on definition' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'F' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'FullComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' Bonus comment on definition' +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'I' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'J' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'I' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'J' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: diff --git a/test/clang-doc/mapper-linkage.cpp b/test/clang-doc/mapper-linkage.cpp new file mode 100644 index 000000000..5b4fe7df3 --- /dev/null +++ b/test/clang-doc/mapper-linkage.cpp @@ -0,0 +1,402 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void function(int x); + +inline int inlinedFunction(int x); + +int functionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +inline int inlinedFunctionWithInnerClass(int x) { + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +class Class { +public: + void publicMethod(); + int publicField; + +protected: + void protectedMethod(); + int protectedField; + +private: + void privateMethod(); + int privateField; +}; + +namespace named { +class NamedClass { +public: + void namedPublicMethod(); + int namedPublicField; + +protected: + void namedProtectedMethod(); + int namedProtectedField; + +private: + void namedPrivateMethod(); + int namedPrivateField; +}; + +void namedFunction(); +static void namedStaticFunction(); +inline void namedInlineFunction(); +} // namespace named + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace { +class AnonClass { +public: + void anonPublicMethod(); + int anonPublicField; + +protected: + void anonProtectedMethod(); + int anonProtectedField; + +private: + void anonPrivateMethod(); + int anonPrivateField; +}; + +void anonFunction(); +static void anonStaticFunction(); +inline void anonInlineFunction(); +} // namespace + +// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/C9B3B71ACDD84C5BB320D34E97677715CDB3EA32.bc | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'innerPublicMethod' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'InnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'inlinedFunctionWithInnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'InnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/8960B5C9247D6F5C532756E53A1AD1240FA2146F.bc | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'namedInlineFunction' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'named' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'void' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/74A031CBE68C101F3E83F60ED17F20C11EC19D48.bc | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'innerPublicMethod' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'InnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'InnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/7CDD73DCD6CD72F7E5CE25502810A182C66C4B45.bc | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'privateMethod' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'void' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/85427901413EC77C961019EBB3ADEF7B0BAAFE78.bc | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'innerPublicMethod' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'functionWithInnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'int' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 +// CHECK-5: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'staticFunctionWithInnerClass' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/4712C5FA37B298A25501D1033C619B65B0ECC449.bc | FileCheck %s --check-prefix CHECK-6 +// CHECK-6: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedPrivateMethod' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'named' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'void' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/6E8FB72A89761E77020BFCEE9A9A6E64B15CC2A9.bc | FileCheck %s --check-prefix CHECK-7 +// CHECK-7: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonPrivateMethod' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'void' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/83CC52D32583E0771710A7742DE81C839E953AC8.bc | FileCheck %s --check-prefix CHECK-8 +// CHECK-8: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'anonInlineFunction' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'void' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: diff --git a/test/clang-doc/mapper-module.cpp b/test/clang-doc/mapper-module.cpp new file mode 100644 index 000000000..04a34c68d --- /dev/null +++ b/test/clang-doc/mapper-module.cpp @@ -0,0 +1,51 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +export module M; + +int moduleFunction(int x); // ModuleLinkage + +static int staticModuleFunction(int x); // ModuleInternalLinkage + +export double exportedModuleFunction(double y, int z); // ExternalLinkage + +// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'exportedModuleFunction' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'double' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'double' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'y' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'z' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: diff --git a/test/clang-doc/mapper-namespace.cpp b/test/clang-doc/mapper-namespace.cpp index aeda90816..d00082331 100644 --- a/test/clang-doc/mapper-namespace.cpp +++ b/test/clang-doc/mapper-namespace.cpp @@ -25,114 +25,70 @@ E func(int i) { return X; } } // namespace B } // namespace A -// RUN: clang-doc --dump-mapper --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'B' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'A' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: blob data = 'X' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'f' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'A' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC.bc | FileCheck %s --check-prefix CHECK-1 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'f' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'A' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'void' -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'func' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'B' +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/9A82CB33ED0FDF81EE383D31CD0957D153C5E840.bc | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'func' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'B' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'A' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'enum A::B::E' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'int' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'i' -// CHECK-2-NEXT: -// CHECK-2-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'A' -// CHECK-3-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-4 -// CHECK-4: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'B' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'A' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'A' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'enum A::B::E' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'int' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'i' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: diff --git a/test/clang-doc/mapper-record.cpp b/test/clang-doc/mapper-record.cpp index 82a5e2f01..dbabd8fdd 100644 --- a/test/clang-doc/mapper-record.cpp +++ b/test/clang-doc/mapper-record.cpp @@ -39,7 +39,7 @@ class X { class Y {}; }; -// RUN: clang-doc --dump-mapper --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs // RUN: llvm-bcanalyzer --dump %t/docs/bc/289584A8E0FF4178A794622A547AA622503967A1.bc | FileCheck %s --check-prefix CHECK-0 @@ -47,11 +47,32 @@ class X { // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ProtectedMethod' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/3FB542274573CAEAD54CEBFFCAEE3D77FB9713D8.bc | FileCheck %s --check-prefix CHECK-1 @@ -60,10 +81,10 @@ class X { // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'I' // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'H' // CHECK-1-NEXT: // CHECK-1-NEXT: @@ -72,246 +93,128 @@ class X { // CHECK-1-NEXT: // CHECK-1-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/5093D428CDC62096A67547BA52566E4FB9404EEE.bc | FileCheck %s --check-prefix CHECK-2 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'ProtectedMethod' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'E' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'E' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'void' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-3 +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'X' +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'X' -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'C' +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'int' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'i' +// CHECK-3-NEXT: // CHECK-3-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E.bc | FileCheck %s --check-prefix CHECK-4 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'H' -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'void' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-5 +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'Y' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'X' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'C' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'i' -// CHECK-5-NEXT: -// CHECK-5-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17.bc | FileCheck %s --check-prefix CHECK-6 +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'Bc' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'A' +// CHECK-5-NEXT: blob data = 'B' +// CHECK-5-NEXT: +// CHECK-5-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-6 // CHECK-6: // CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '~E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'void' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4.bc | FileCheck %s --check-prefix CHECK-7 +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'D' +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-7 // CHECK-7: // CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'E' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'F' +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: blob data = 'E' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'E' +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'D' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'void' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-8 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-8 // CHECK-8: // CHECK-8-NEXT: // CHECK-8-NEXT: // CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'Y' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'X' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'A' +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'int' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'X' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'int' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'Y' +// CHECK-8-NEXT: // CHECK-8-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/FC07BD34D5E77782C263FA944447929EA8753740.bc | FileCheck %s --check-prefix CHECK-9 -// CHECK-9: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: blob data = 'B' -// CHECK-9-NEXT: blob data = '{{.*}}' -// CHECK-9-NEXT: blob data = 'X' -// CHECK-9-NEXT: blob data = 'Y' -// CHECK-9-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-10 -// CHECK-10: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: blob data = 'D' -// CHECK-10-NEXT: blob data = '{{.*}}' -// CHECK-10-NEXT: -// CHECK-10-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-11 -// CHECK-11: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: blob data = 'F' -// CHECK-11-NEXT: blob data = '{{.*}}' -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: blob data = 'E' -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: blob data = 'D' -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-12 -// CHECK-12: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'A' -// CHECK-12-NEXT: blob data = '{{.*}}' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'int' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'X' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'int' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'Y' -// CHECK-12-NEXT: -// CHECK-12-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/1E3438A08BA22025C0B46289FF0686F92C8924C5.bc | FileCheck %s --check-prefix CHECK-13 -// CHECK-13: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: blob data = 'Bc' -// CHECK-13-NEXT: blob data = '{{.*}}' -// CHECK-13-NEXT: -// CHECK-13-NEXT: blob data = 'A' -// CHECK-13-NEXT: blob data = 'B' -// CHECK-13-NEXT: diff --git a/test/clang-doc/module.cpp b/test/clang-doc/module.cpp deleted file mode 100644 index a2b594597..000000000 --- a/test/clang-doc/module.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --extra-arg=-fmodules-ts --doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: cat %t/docs/moduleFunction.yaml | FileCheck %s --check-prefix=CHECK-A -// RUN: cat %t/docs/staticModuleFunction.yaml | FileCheck %s --check-prefix=CHECK-B -// RUN: cat %t/docs/exportedModuleFunction.yaml | FileCheck %s --check-prefix=CHECK-C - -export module M; - -int moduleFunction(int x); //ModuleLinkage -// CHECK-A: --- -// CHECK-A-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-A-NEXT: Name: 'moduleFunction' -// CHECK-A-NEXT: Location: -// CHECK-A-NEXT: - LineNumber: 12 -// CHECK-A-NEXT: Filename: {{.*}} -// CHECK-A-NEXT: Params: -// CHECK-A-NEXT: - Type: -// CHECK-A-NEXT: Name: 'int' -// CHECK-A-NEXT: Name: 'x' -// CHECK-A-NEXT: ReturnType: -// CHECK-A-NEXT: Type: -// CHECK-A-NEXT: Name: 'int' -// CHECK-A-NEXT: ... - -static int staticModuleFunction(int x); //ModuleInternalLinkage -// CHECK-B: --- -// CHECK-B-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-B-NEXT: Name: 'staticModuleFunction' -// CHECK-B-NEXT: Location: -// CHECK-B-NEXT: - LineNumber: 28 -// CHECK-B-NEXT: Filename: {{.*}} -// CHECK-B-NEXT: Params: -// CHECK-B-NEXT: - Type: -// CHECK-B-NEXT: Name: 'int' -// CHECK-B-NEXT: Name: 'x' -// CHECK-B-NEXT: ReturnType: -// CHECK-B-NEXT: Type: -// CHECK-B-NEXT: Name: 'int' -// CHECK-B-NEXT: ... - -export double exportedModuleFunction(double y, int z); //ExternalLinkage -// CHECK-C: --- -// CHECK-C-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-C-NEXT: Name: 'exportedModuleFunction' -// CHECK-C-NEXT: Location: -// CHECK-C-NEXT: - LineNumber: 44 -// CHECK-C-NEXT: Filename: {{.*}} -// CHECK-C-NEXT: Params: -// CHECK-C-NEXT: - Type: -// CHECK-C-NEXT: Name: 'double' -// CHECK-C-NEXT: Name: 'y' -// CHECK-C-NEXT: - Type: -// CHECK-C-NEXT: Name: 'int' -// CHECK-C-NEXT: Name: 'z' -// CHECK-C-NEXT: ReturnType: -// CHECK-C-NEXT: Type: -// CHECK-C-NEXT: Name: 'double' -// CHECK-C-NEXT: ... diff --git a/test/clang-doc/public-comment.cpp b/test/clang-doc/public-comment.cpp new file mode 100644 index 000000000..6c5545e8e --- /dev/null +++ b/test/clang-doc/public-comment.cpp @@ -0,0 +1,138 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +/// \brief Brief description. +/// +/// Extended description that +/// continues onto the next line. +/// +///
    +///
  • Testing. +///
+/// +/// \verbatim +/// The description continues. +/// \endverbatim +/// -- +/// \param [out] I is a parameter. +/// \param J is a parameter. +/// \return void +void F(int I, int J); + +/// Bonus comment on definition +void F(int I, int J) {} + +// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'F' +// CHECK-0-NEXT: Description: +// CHECK-0-NEXT: - Kind: 'FullComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'BlockCommandComment' +// CHECK-0-NEXT: Name: 'brief' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Brief description.' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Extended description that' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' continues onto the next line.' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' +// CHECK-0-NEXT: Name: 'ul' +// CHECK-0-NEXT: AttrKeys: +// CHECK-0-NEXT: - 'class' +// CHECK-0-NEXT: AttrValues: +// CHECK-0-NEXT: - 'test' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' +// CHECK-0-NEXT: Name: 'li' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Testing.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'HTMLEndTagComment' +// CHECK-0-NEXT: Name: 'ul' +// CHECK-0-NEXT: SelfClosing: true +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'VerbatimBlockComment' +// CHECK-0-NEXT: Name: 'verbatim' +// CHECK-0-NEXT: CloseName: 'endverbatim' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'VerbatimBlockLineComment' +// CHECK-0-NEXT: Text: ' The description continues.' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' --' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'ParamCommandComment' +// CHECK-0-NEXT: Direction: '[out]' +// CHECK-0-NEXT: ParamName: 'I' +// CHECK-0-NEXT: Explicit: true +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' is a parameter.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'ParamCommandComment' +// CHECK-0-NEXT: Direction: '[in]' +// CHECK-0-NEXT: ParamName: 'J' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' is a parameter.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'BlockCommandComment' +// CHECK-0-NEXT: Name: 'return' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' void' +// CHECK-0-NEXT: - Kind: 'FullComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Bonus comment on definition' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 28 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 25 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'I' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'J' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: ... diff --git a/test/clang-doc/public-linkage.cpp b/test/clang-doc/public-linkage.cpp new file mode 100644 index 000000000..c33e08ce6 --- /dev/null +++ b/test/clang-doc/public-linkage.cpp @@ -0,0 +1,299 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void function(int x); + +inline int inlinedFunction(int x); + +int functionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +inline int inlinedFunctionWithInnerClass(int x) { + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +class Class { +public: + void publicMethod(); + int publicField; + +protected: + void protectedMethod(); + int protectedField; + +private: + void privateMethod(); + int privateField; +}; + +namespace named { +class NamedClass { +public: + void namedPublicMethod(); + int namedPublicField; + +protected: + void namedProtectedMethod(); + int namedProtectedField; + +private: + void namedPrivateMethod(); + int namedPrivateField; +}; + +void namedFunction(); +static void namedStaticFunction(); +inline void namedInlineFunction(); +} // namespace named + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace { +class AnonClass { +public: + void anonPublicMethod(); + int anonPublicField; + +protected: + void anonProtectedMethod(); + int anonProtectedField; + +private: + void anonPrivateMethod(); + int anonPrivateField; +}; + +void anonFunction(); +static void anonStaticFunction(); +inline void anonInlineFunction(); +} // namespace + +// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./Class.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 32 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: TagType: Class +// CHECK-0-NEXT: Members: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'publicField' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'protectedField' +// CHECK-0-NEXT: Access: Protected +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'publicMethod' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 34 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: IsMethod: true +// CHECK-0-NEXT: Parent: +// CHECK-0-NEXT: Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'protectedMethod' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 38 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: IsMethod: true +// CHECK-0-NEXT: Parent: +// CHECK-0-NEXT: Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: ... + +// RUN: cat %t/docs/./named.yaml | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: --- +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: ChildFunctions: +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'namedFunction' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Location: +// CHECK-1-NEXT: - LineNumber: 61 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'void' +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'namedInlineFunction' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Location: +// CHECK-1-NEXT: - LineNumber: 63 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'void' +// CHECK-1-NEXT: ... + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: --- +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: ChildFunctions: +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'function' +// CHECK-2-NEXT: Location: +// CHECK-2-NEXT: - LineNumber: 10 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: Params: +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'x' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'void' +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'inlinedFunction' +// CHECK-2-NEXT: Location: +// CHECK-2-NEXT: - LineNumber: 12 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: Params: +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'x' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'functionWithInnerClass' +// CHECK-2-NEXT: DefLocation: +// CHECK-2-NEXT: LineNumber: 14 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: Params: +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'x' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'inlinedFunctionWithInnerClass' +// CHECK-2-NEXT: DefLocation: +// CHECK-2-NEXT: LineNumber: 23 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: Params: +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'x' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: ... + +// RUN: cat %t/docs/named/NamedClass.yaml | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: --- +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'NamedClass' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: Name: 'named' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 47 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: TagType: Class +// CHECK-3-NEXT: Members: +// CHECK-3-NEXT: - Type: +// CHECK-3-NEXT: Name: 'int' +// CHECK-3-NEXT: Name: 'namedPublicField' +// CHECK-3-NEXT: - Type: +// CHECK-3-NEXT: Name: 'int' +// CHECK-3-NEXT: Name: 'namedProtectedField' +// CHECK-3-NEXT: Access: Protected +// CHECK-3-NEXT: ChildFunctions: +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'namedPublicMethod' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'NamedClass' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: Name: 'named' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 49 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'NamedClass' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'namedProtectedMethod' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'NamedClass' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: Name: 'named' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 53 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'NamedClass' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: ... diff --git a/test/clang-doc/public-module.cpp b/test/clang-doc/public-module.cpp index c7ebadda7..0c93d6884 100644 --- a/test/clang-doc/public-module.cpp +++ b/test/clang-doc/public-module.cpp @@ -1,53 +1,51 @@ -// This test requires linux because it uses `diff` and compares filepaths -// REQUIRES: system-linux +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// // RUN: rm -rf %t // RUN: mkdir %t // RUN: echo "" > %t/compile_flags.txt // RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --public --extra-arg=-fmodules-ts --doxygen -p %t %t/test.cpp -output=%t/docs-with-public-flag -// RUN: clang-doc --extra-arg=-fmodules-ts --doxygen -p %t %t/test.cpp -output=%t/docs-without -// RUN: cat %t/docs-with-public-flag/moduleFunction.yaml | FileCheck %s --check-prefix=CHECK-A -// RUN: cat %t/docs-with-public-flag/exportedModuleFunction.yaml | FileCheck %s --check-prefix=CHECK-B -// RUN: (diff -qry %t/docs-with-public-flag %t/docs-without | sed 's:.*/::' > %t/public.diff) || true -// RUN: cat %t/public.diff | FileCheck %s --check-prefix=CHECK-C export module M; -int moduleFunction(int x); //ModuleLinkage -// CHECK-A: --- -// CHECK-A-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-A-NEXT: Name: 'moduleFunction' -// CHECK-A-NEXT: Location: -// CHECK-A-NEXT: - LineNumber: 16 -// CHECK-A-NEXT: Filename: {{.*}} -// CHECK-A-NEXT: Params: -// CHECK-A-NEXT: - Type: -// CHECK-A-NEXT: Name: 'int' -// CHECK-A-NEXT: Name: 'x' -// CHECK-A-NEXT: ReturnType: -// CHECK-A-NEXT: Type: -// CHECK-A-NEXT: Name: 'int' -// CHECK-A-NEXT: ... +int moduleFunction(int x); // ModuleLinkage -static int staticModuleFunction(int x); //ModuleInternalLinkage +static int staticModuleFunction(int x); // ModuleInternalLinkage -export double exportedModuleFunction(double y, int z); //ExternalLinkage -// CHECK-B: --- -// CHECK-B-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-B-NEXT: Name: 'exportedModuleFunction' -// CHECK-B-NEXT: Location: -// CHECK-B-NEXT: - LineNumber: 34 -// CHECK-B-NEXT: Filename: {{.*}} -// CHECK-B-NEXT: Params: -// CHECK-B-NEXT: - Type: -// CHECK-B-NEXT: Name: 'double' -// CHECK-B-NEXT: Name: 'y' -// CHECK-B-NEXT: - Type: -// CHECK-B-NEXT: Name: 'int' -// CHECK-B-NEXT: Name: 'z' -// CHECK-B-NEXT: ReturnType: -// CHECK-B-NEXT: Type: -// CHECK-B-NEXT: Name: 'double' -// CHECK-B-NEXT: ... +export double exportedModuleFunction(double y, int z); // ExternalLinkage -// CHECK-C: docs-without: staticModuleFunction.yaml +// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'moduleFunction' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 11 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'x' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'exportedModuleFunction' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 15 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'double' +// CHECK-0-NEXT: Name: 'y' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'z' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'double' +// CHECK-0-NEXT: ... diff --git a/test/clang-doc/public-namespace.cpp b/test/clang-doc/public-namespace.cpp new file mode 100644 index 000000000..d104ff2c7 --- /dev/null +++ b/test/clang-doc/public-namespace.cpp @@ -0,0 +1,96 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +namespace A { + +void f(); + +} // namespace A + +namespace A { + +void f(){}; + +namespace B { + +enum E { X }; + +E func(int i) { return X; } + +} // namespace B +} // namespace A + +// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./A.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'A' +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'f' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Namespace +// CHECK-0-NEXT: Name: 'A' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 17 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 11 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: ... + +// RUN: cat %t/docs/A/B.yaml | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: --- +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'B' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'A' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: ChildFunctions: +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'func' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'B' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'A' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: DefLocation: +// CHECK-1-NEXT: LineNumber: 23 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: Params: +// CHECK-1-NEXT: - Type: +// CHECK-1-NEXT: Name: 'int' +// CHECK-1-NEXT: Name: 'i' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'enum A::B::E' +// CHECK-1-NEXT: ChildEnums: +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'E' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'B' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'A' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: DefLocation: +// CHECK-1-NEXT: LineNumber: 21 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: Members: +// CHECK-1-NEXT: - 'X' +// CHECK-1-NEXT: ... diff --git a/test/clang-doc/public-record.cpp b/test/clang-doc/public-record.cpp new file mode 100644 index 000000000..d3302193f --- /dev/null +++ b/test/clang-doc/public-record.cpp @@ -0,0 +1,208 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// This test requires Linux due to system-dependent USR for the inner class. +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void H() { + class I {}; +} + +union A { int X; int Y; }; + +enum B { X, Y }; + +enum class Bc { A, B }; + +struct C { int i; }; + +class D {}; + +class E { +public: + E() {} + ~E() {} + +protected: + void ProtectedMethod(); +}; + +void E::ProtectedMethod() {} + +class F : virtual private D, public E {}; + +class X { + class Y {}; +}; + +// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./C.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'C' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 21 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Members: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'i' +// CHECK-0-NEXT: ... + +// RUN: cat %t/docs/./A.yaml | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: --- +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'A' +// CHECK-1-NEXT: DefLocation: +// CHECK-1-NEXT: LineNumber: 15 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: TagType: Union +// CHECK-1-NEXT: Members: +// CHECK-1-NEXT: - Type: +// CHECK-1-NEXT: Name: 'int' +// CHECK-1-NEXT: Name: 'X' +// CHECK-1-NEXT: - Type: +// CHECK-1-NEXT: Name: 'int' +// CHECK-1-NEXT: Name: 'Y' +// CHECK-1-NEXT: ... + +// RUN: cat %t/docs/./F.yaml | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: --- +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'F' +// CHECK-2-NEXT: DefLocation: +// CHECK-2-NEXT: LineNumber: 36 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: TagType: Class +// CHECK-2-NEXT: Parents: +// CHECK-2-NEXT: - Type: Record +// CHECK-2-NEXT: Name: 'E' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: VirtualParents: +// CHECK-2-NEXT: - Type: Record +// CHECK-2-NEXT: Name: 'D' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: ... + +// RUN: cat %t/docs/./E.yaml | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: --- +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 25 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: TagType: Class +// CHECK-3-NEXT: ChildFunctions: +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 27 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: '~E' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 28 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'ProtectedMethod' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 34 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 31 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: ... + +// RUN: cat %t/docs/./D.yaml | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: --- +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: 'D' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 23 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: TagType: Class +// CHECK-4-NEXT: ... + +// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-5 +// CHECK-5: --- +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Name: 'X' +// CHECK-5-NEXT: DefLocation: +// CHECK-5-NEXT: LineNumber: 38 +// CHECK-5-NEXT: Filename: 'test' +// CHECK-5-NEXT: TagType: Class +// CHECK-5-NEXT: ... + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-6 +// CHECK-6: --- +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: ChildFunctions: +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'H' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 11 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: ReturnType: +// CHECK-6-NEXT: Type: +// CHECK-6-NEXT: Name: 'void' +// CHECK-6-NEXT: ChildEnums: +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'B' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 17 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: Members: +// CHECK-6-NEXT: - 'X' +// CHECK-6-NEXT: - 'Y' +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'Bc' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 19 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: Scoped: true +// CHECK-6-NEXT: Members: +// CHECK-6-NEXT: - 'A' +// CHECK-6-NEXT: - 'B' +// CHECK-6-NEXT: ... diff --git a/test/clang-doc/public-records.cpp b/test/clang-doc/public-records.cpp deleted file mode 100644 index a00a23b0d..000000000 --- a/test/clang-doc/public-records.cpp +++ /dev/null @@ -1,341 +0,0 @@ -// This test requires linux because it uses `diff` and compares filepaths -// REQUIRES: system-linux -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --public --doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: clang-doc --doxygen -p %t %t/test.cpp -output=%t/docs-without-flag -// RUN: cat %t/docs/function.yaml | FileCheck %s --check-prefix=CHECK-A -// RUN: cat %t/docs/inlinedFunction.yaml | FileCheck %s --check-prefix=CHECK-B -// RUN: cat %t/docs/functionWithInnerClass.yaml | FileCheck %s --check-prefix=CHECK-C -// RUN: cat %t/docs/inlinedFunctionWithInnerClass.yaml | FileCheck %s --check-prefix=CHECK-D -// RUN: cat %t/docs/Class/publicMethod.yaml| FileCheck %s --check-prefix=CHECK-E -// RUN: cat %t/docs/Class.yaml| FileCheck %s --check-prefix=CHECK-F -// RUN: cat %t/docs/Class/protectedMethod.yaml| FileCheck %s --check-prefix=CHECK-G -// RUN: cat %t/docs/named.yaml| FileCheck %s --check-prefix=CHECK-H -// RUN: cat %t/docs/named/NamedClass.yaml| FileCheck %s --check-prefix=CHECK-I -// RUN: cat %t/docs/named/namedFunction.yaml| FileCheck %s --check-prefix=CHECK-J -// RUN: cat %t/docs/named/namedInlineFunction.yaml| FileCheck %s --check-prefix=CHECK-K -// RUN: cat %t/docs/named/NamedClass/namedPublicMethod.yaml| FileCheck %s --check-prefix=CHECK-L -// RUN: cat %t/docs/named/NamedClass/namedProtectedMethod.yaml| FileCheck %s --check-prefix=CHECK-M -// RUN: (diff -qry %t/docs-without-flag %t/docs | sed 's:.*/::' > %t/public.diff) || true -// RUN: cat %t/public.diff | FileCheck %s --check-prefix=CHECK-N - -void function(int x); - -// CHECK-A: --- -// CHECK-A-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-A-NEXT: Name: 'function' -// CHECK-A-NEXT: Location: -// CHECK-A-NEXT: - LineNumber: 25 -// CHECK-A-NEXT: Filename: {{.*}} -// CHECK-A-NEXT: Params: -// CHECK-A-NEXT: - Type: -// CHECK-A-NEXT: Name: 'int' -// CHECK-A-NEXT: Name: 'x' -// CHECK-A-NEXT: ReturnType: -// CHECK-A-NEXT: Type: -// CHECK-A-NEXT: Name: 'void' -// CHECK-A-NEXT: ... - -inline int inlinedFunction(int x); - -// CHECK-B: --- -// CHECK-B-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-B-NEXT: Name: 'inlinedFunction' -// CHECK-B-NEXT: Location: -// CHECK-B-NEXT: - LineNumber: 42 -// CHECK-B-NEXT: Filename: {{.*}} -// CHECK-B-NEXT: Params: -// CHECK-B-NEXT: - Type: -// CHECK-B-NEXT: Name: 'int' -// CHECK-B-NEXT: Name: 'x' -// CHECK-B-NEXT: ReturnType: -// CHECK-B-NEXT: Type: -// CHECK-B-NEXT: Name: 'int' -// CHECK-B-NEXT: ... - -int functionWithInnerClass(int x){ - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -// CHECK-C: --- -// CHECK-C-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-C-NEXT: Name: 'functionWithInnerClass' -// CHECK-C-NEXT: DefLocation: -// CHECK-C-NEXT: LineNumber: 59 -// CHECK-C-NEXT: Filename: {{.*}} -// CHECK-C-NEXT: Params: -// CHECK-C-NEXT: - Type: -// CHECK-C-NEXT: Name: 'int' -// CHECK-C-NEXT: Name: 'x' -// CHECK-C-NEXT: ReturnType: -// CHECK-C-NEXT: Type: -// CHECK-C-NEXT: Name: 'int' -// CHECK-C-NEXT: ... - -inline int inlinedFunctionWithInnerClass(int x){ - class InnerClass { //VisibleNoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -// CHECK-D: --- -// CHECK-D-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-D-NEXT: Name: 'inlinedFunctionWithInnerClass' -// CHECK-D-NEXT: DefLocation: -// CHECK-D-NEXT: LineNumber: 83 -// CHECK-D-NEXT: Filename: {{.*}} -// CHECK-D-NEXT: Params: -// CHECK-D-NEXT: - Type: -// CHECK-D-NEXT: Name: 'int' -// CHECK-D-NEXT: Name: 'x' -// CHECK-D-NEXT: ReturnType: -// CHECK-D-NEXT: Type: -// CHECK-D-NEXT: Name: 'int' -// CHECK-D-NEXT: ... - -class Class { - public: - void publicMethod(); - int publicField; - protected: - void protectedMethod(); - int protectedField; - private: - void privateMethod(); - int privateField; -}; - -// CHECK-E: --- -// CHECK-E-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-E-NEXT: Name: 'publicMethod' -// CHECK-E-NEXT: Namespace: -// CHECK-E-NEXT: - Type: Record -// CHECK-E-NEXT: Name: 'Class' -// CHECK-E-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-E-NEXT: Location: -// CHECK-E-NEXT: - LineNumber: 109 -// CHECK-E-NEXT: Filename: {{.*}} -// CHECK-E-NEXT: IsMethod: true -// CHECK-E-NEXT: Parent: -// CHECK-E-NEXT: Type: Record -// CHECK-E-NEXT: Name: 'Class' -// CHECK-E-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-E-NEXT: ReturnType: -// CHECK-E-NEXT: Type: -// CHECK-E-NEXT: Name: 'void' -// CHECK-E-NEXT: ... - -// CHECK-F: --- -// CHECK-F-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-F-NEXT: Name: 'Class' -// CHECK-F-NEXT: DefLocation: -// CHECK-F-NEXT: LineNumber: 107 -// CHECK-F-NEXT: Filename: {{.*}} -// CHECK-F-NEXT: TagType: Class -// CHECK-F-NEXT: Members: -// CHECK-F-NEXT: - Type: -// CHECK-F-NEXT: Name: 'int' -// CHECK-F-NEXT: Name: 'publicField' -// CHECK-F-NEXT: - Type: -// CHECK-F-NEXT: Name: 'int' -// CHECK-F-NEXT: Name: 'protectedField' -// CHECK-F-NEXT: Access: Protected -// CHECK-F-NEXT: ... - -// CHECK-G: --- -// CHECK-G-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-G-NEXT: Name: 'protectedMethod' -// CHECK-G-NEXT: Namespace: -// CHECK-G-NEXT: - Type: Record -// CHECK-G-NEXT: Name: 'Class' -// CHECK-G-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-G-NEXT: Location: -// CHECK-G-NEXT: - LineNumber: 112 -// CHECK-G-NEXT: Filename: {{.*}} -// CHECK-G-NEXT: IsMethod: true -// CHECK-G-NEXT: Parent: -// CHECK-G-NEXT: Type: Record -// CHECK-G-NEXT: Name: 'Class' -// CHECK-G-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-G-NEXT: ReturnType: -// CHECK-G-NEXT: Type: -// CHECK-G-NEXT: Name: 'void' -// CHECK-G-NEXT: ... - -namespace named{ - class NamedClass { - public: - void namedPublicMethod(); - int namedPublicField; - protected: - void namedProtectedMethod(); - int namedProtectedField; - private: - void namedPrivateMethod(); - int namedPrivateField; - }; - - void namedFunction(); - static void namedStaticFunction(); - inline void namedInlineFunction(); -} - -// CHECK-H: --- -// CHECK-H-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-H-NEXT: Name: 'named' -// CHECK-H-NEXT: ... - -// CHECK-I: --- -// CHECK-I-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-I-NEXT: Name: 'NamedClass' -// CHECK-I-NEXT: Namespace: -// CHECK-I-NEXT: - Type: Namespace -// CHECK-I-NEXT: Name: 'named' -// CHECK-I-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-I-NEXT: DefLocation: -// CHECK-I-NEXT: LineNumber: 177 -// CHECK-I-NEXT: Filename: {{.*}} -// CHECK-I-NEXT: TagType: Class -// CHECK-I-NEXT: Members: -// CHECK-I-NEXT: - Type: -// CHECK-I-NEXT: Name: 'int' -// CHECK-I-NEXT: Name: 'namedPublicField' -// CHECK-I-NEXT: - Type: -// CHECK-I-NEXT: Name: 'int' -// CHECK-I-NEXT: Name: 'namedProtectedField' -// CHECK-I-NEXT: Access: Protected -// CHECK-I-NEXT: ... - -// CHECK-J: --- -// CHECK-J-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-J-NEXT: Name: 'namedFunction' -// CHECK-J-NEXT: Namespace: -// CHECK-J-NEXT: - Type: Namespace -// CHECK-J-NEXT: Name: 'named' -// CHECK-J-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-J-NEXT: Location: -// CHECK-J-NEXT: - LineNumber: 189 -// CHECK-J-NEXT: Filename: {{.*}} -// CHECK-J-NEXT: ReturnType: -// CHECK-J-NEXT: Type: -// CHECK-J-NEXT: Name: 'void' -// CHECK-J-NEXT: ... - -// CHECK-K: --- -// CHECK-K-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-K-NEXT: Name: 'namedInlineFunction' -// CHECK-K-NEXT: Namespace: -// CHECK-K-NEXT: - Type: Namespace -// CHECK-K-NEXT: Name: 'named' -// CHECK-K-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-K-NEXT: Location: -// CHECK-K-NEXT: - LineNumber: 191 -// CHECK-K-NEXT: Filename: {{.*}} -// CHECK-K-NEXT: ReturnType: -// CHECK-K-NEXT: Type: -// CHECK-K-NEXT: Name: 'void' -// CHECK-K-NEXT: ... - -// CHECK-L: --- -// CHECK-L-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-L-NEXT: Name: 'namedPublicMethod' -// CHECK-L-NEXT: Namespace: -// CHECK-L-NEXT: - Type: Record -// CHECK-L-NEXT: Name: 'NamedClass' -// CHECK-L-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-L-NEXT: - Type: Namespace -// CHECK-L-NEXT: Name: 'named' -// CHECK-L-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-L-NEXT: Location: -// CHECK-L-NEXT: - LineNumber: 179 -// CHECK-L-NEXT: Filename: {{.*}} -// CHECK-L-NEXT: IsMethod: true -// CHECK-L-NEXT: Parent: -// CHECK-L-NEXT: Type: Record -// CHECK-L-NEXT: Name: 'NamedClass' -// CHECK-L-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-L-NEXT: ReturnType: -// CHECK-L-NEXT: Type: -// CHECK-L-NEXT: Name: 'void' -// CHECK-L-NEXT: ... - -// CHECK-M: --- -// CHECK-M-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-M-NEXT: Name: 'namedProtectedMethod' -// CHECK-M-NEXT: Namespace: -// CHECK-M-NEXT: - Type: Record -// CHECK-M-NEXT: Name: 'NamedClass' -// CHECK-M-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-M-NEXT: - Type: Namespace -// CHECK-M-NEXT: Name: 'named' -// CHECK-M-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-M-NEXT: Location: -// CHECK-M-NEXT: - LineNumber: 182 -// CHECK-M-NEXT: Filename: {{.*}} -// CHECK-M-NEXT: IsMethod: true -// CHECK-M-NEXT: Parent: -// CHECK-M-NEXT: Type: Record -// CHECK-M-NEXT: Name: 'NamedClass' -// CHECK-M-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-M-NEXT: ReturnType: -// CHECK-M-NEXT: Type: -// CHECK-M-NEXT: Name: 'void' -// CHECK-M-NEXT: ... - - -static void staticFunction(int x); //Internal Linkage - -static int staticFunctionWithInnerClass(int x){ - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -namespace{ - class AnonClass { - public: - void anonPublicMethod(); - int anonPublicField; - protected: - void anonProtectedMethod(); - int anonProtectedField; - private: - void anonPrivateMethod(); - int anonPrivateField; - }; - - void anonFunction(); - static void anonStaticFunction(); - inline void anonInlineFunction(); -} - -// CHECK-N: docs-without-flag: .yaml -// CHECK-N-NEXT: docs-without-flag: AnonClass -// CHECK-N-NEXT: docs-without-flag: AnonClass.yaml -// CHECK-N-NEXT: Class: privateMethod.yaml -// CHECK-N-NEXT: Class.yaml differ -// CHECK-N-NEXT: docs-without-flag: anonFunction.yaml -// CHECK-N-NEXT: docs-without-flag: anonInlineFunction.yaml -// CHECK-N-NEXT: docs-without-flag: anonStaticFunction.yaml -// CHECK-N-NEXT: docs-without-flag: functionWithInnerClass -// CHECK-N-NEXT: docs-without-flag: inlinedFunctionWithInnerClass -// CHECK-N-NEXT: NamedClass: namedPrivateMethod.yaml -// CHECK-N-NEXT: NamedClass.yaml differ -// CHECK-N-NEXT: named: namedStaticFunction.yaml -// CHECK-N-NEXT: docs-without-flag: staticFunction.yaml -// CHECK-N-NEXT: docs-without-flag: staticFunctionWithInnerClass -// CHECK-N-NEXT: docs-without-flag: staticFunctionWithInnerClass.yaml diff --git a/test/clang-doc/test_cases/linkage.cpp b/test/clang-doc/test_cases/linkage.cpp new file mode 100644 index 000000000..ed4b4a303 --- /dev/null +++ b/test/clang-doc/test_cases/linkage.cpp @@ -0,0 +1,95 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void function(int x); + +inline int inlinedFunction(int x); + +int functionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +inline int inlinedFunctionWithInnerClass(int x) { + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +class Class { +public: + void publicMethod(); + int publicField; + +protected: + void protectedMethod(); + int protectedField; + +private: + void privateMethod(); + int privateField; +}; + +namespace named { +class NamedClass { +public: + void namedPublicMethod(); + int namedPublicField; + +protected: + void namedProtectedMethod(); + int namedProtectedField; + +private: + void namedPrivateMethod(); + int namedPrivateField; +}; + +void namedFunction(); +static void namedStaticFunction(); +inline void namedInlineFunction(); +} // namespace named + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace { +class AnonClass { +public: + void anonPublicMethod(); + int anonPublicField; + +protected: + void anonProtectedMethod(); + int anonProtectedField; + +private: + void anonPrivateMethod(); + int anonPrivateField; +}; + +void anonFunction(); +static void anonStaticFunction(); +inline void anonInlineFunction(); +} // namespace diff --git a/test/clang-doc/test_cases/module.cpp b/test/clang-doc/test_cases/module.cpp new file mode 100644 index 000000000..3c30a5476 --- /dev/null +++ b/test/clang-doc/test_cases/module.cpp @@ -0,0 +1,15 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +export module M; + +int moduleFunction(int x); // ModuleLinkage + +static int staticModuleFunction(int x); // ModuleInternalLinkage + +export double exportedModuleFunction(double y, int z); // ExternalLinkage diff --git a/test/clang-doc/yaml-comment.cpp b/test/clang-doc/yaml-comment.cpp index 445e2be19..7aa8e64d5 100644 --- a/test/clang-doc/yaml-comment.cpp +++ b/test/clang-doc/yaml-comment.cpp @@ -27,110 +27,112 @@ void F(int I, int J); /// Bonus comment on definition void F(int I, int J) {} -// RUN: clang-doc --format=yaml --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs -// RUN: cat %t/docs/./F.yaml | FileCheck %s --check-prefix CHECK-0 +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-0 // CHECK-0: --- -// CHECK-0-NEXT: USR: '7574630614A535710E5A6ABCFFF98BCA2D06A4CA' -// CHECK-0-NEXT: Name: 'F' -// CHECK-0-NEXT: Description: -// CHECK-0-NEXT: - Kind: 'FullComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'BlockCommandComment' -// CHECK-0-NEXT: Name: 'brief' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'F' +// CHECK-0-NEXT: Description: +// CHECK-0-NEXT: - Kind: 'FullComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'ParagraphComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Brief description.' -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Extended description that' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' continues onto the next line.' -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' -// CHECK-0-NEXT: Name: 'ul' -// CHECK-0-NEXT: AttrKeys: -// CHECK-0-NEXT: - 'class' -// CHECK-0-NEXT: AttrValues: -// CHECK-0-NEXT: - 'test' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' -// CHECK-0-NEXT: Name: 'li' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Testing.' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'HTMLEndTagComment' -// CHECK-0-NEXT: Name: 'ul' -// CHECK-0-NEXT: SelfClosing: true -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'VerbatimBlockComment' -// CHECK-0-NEXT: Name: 'verbatim' -// CHECK-0-NEXT: CloseName: 'endverbatim' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'VerbatimBlockLineComment' -// CHECK-0-NEXT: Text: ' The description continues.' -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' --' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'ParamCommandComment' -// CHECK-0-NEXT: Direction: '[out]' -// CHECK-0-NEXT: ParamName: 'I' -// CHECK-0-NEXT: Explicit: true -// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'BlockCommandComment' +// CHECK-0-NEXT: Name: 'brief' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Brief description.' // CHECK-0-NEXT: - Kind: 'ParagraphComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' is a parameter.' +// CHECK-0-NEXT: Text: ' Extended description that' // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'ParamCommandComment' -// CHECK-0-NEXT: Direction: '[in]' -// CHECK-0-NEXT: ParamName: 'J' -// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: Text: ' continues onto the next line.' // CHECK-0-NEXT: - Kind: 'ParagraphComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' is a parameter.' +// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' +// CHECK-0-NEXT: Name: 'ul' +// CHECK-0-NEXT: AttrKeys: +// CHECK-0-NEXT: - 'class' +// CHECK-0-NEXT: AttrValues: +// CHECK-0-NEXT: - 'test' // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'BlockCommandComment' -// CHECK-0-NEXT: Name: 'return' -// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' +// CHECK-0-NEXT: Name: 'li' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Testing.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'HTMLEndTagComment' +// CHECK-0-NEXT: Name: 'ul' +// CHECK-0-NEXT: SelfClosing: true // CHECK-0-NEXT: - Kind: 'ParagraphComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' void' -// CHECK-0-NEXT: - Kind: 'FullComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: - Kind: 'VerbatimBlockComment' +// CHECK-0-NEXT: Name: 'verbatim' +// CHECK-0-NEXT: CloseName: 'endverbatim' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'VerbatimBlockLineComment' +// CHECK-0-NEXT: Text: ' The description continues.' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' --' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'ParamCommandComment' +// CHECK-0-NEXT: Direction: '[out]' +// CHECK-0-NEXT: ParamName: 'I' +// CHECK-0-NEXT: Explicit: true +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' is a parameter.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'ParamCommandComment' +// CHECK-0-NEXT: Direction: '[in]' +// CHECK-0-NEXT: ParamName: 'J' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' is a parameter.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'BlockCommandComment' +// CHECK-0-NEXT: Name: 'return' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' void' +// CHECK-0-NEXT: - Kind: 'FullComment' // CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Bonus comment on definition' -// CHECK-0-NEXT: DefLocation: -// CHECK-0-NEXT: LineNumber: 28 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 25 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Params: -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'I' -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'J' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Bonus comment on definition' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 28 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 25 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'I' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'J' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' // CHECK-0-NEXT: ... diff --git a/test/clang-doc/yaml-linkage.cpp b/test/clang-doc/yaml-linkage.cpp new file mode 100644 index 000000000..3a0aa5bf9 --- /dev/null +++ b/test/clang-doc/yaml-linkage.cpp @@ -0,0 +1,529 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void function(int x); + +inline int inlinedFunction(int x); + +int functionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +inline int inlinedFunctionWithInnerClass(int x) { + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +class Class { +public: + void publicMethod(); + int publicField; + +protected: + void protectedMethod(); + int protectedField; + +private: + void privateMethod(); + int privateField; +}; + +namespace named { +class NamedClass { +public: + void namedPublicMethod(); + int namedPublicField; + +protected: + void namedProtectedMethod(); + int namedProtectedField; + +private: + void namedPrivateMethod(); + int namedPrivateField; +}; + +void namedFunction(); +static void namedStaticFunction(); +inline void namedInlineFunction(); +} // namespace named + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace { +class AnonClass { +public: + void anonPublicMethod(); + int anonPublicField; + +protected: + void anonProtectedMethod(); + int anonProtectedField; + +private: + void anonPrivateMethod(); + int anonPrivateField; +}; + +void anonFunction(); +static void anonStaticFunction(); +inline void anonInlineFunction(); +} // namespace + +// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./Class.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 32 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: TagType: Class +// CHECK-0-NEXT: Members: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'publicField' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'protectedField' +// CHECK-0-NEXT: Access: Protected +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'privateField' +// CHECK-0-NEXT: Access: Private +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'publicMethod' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 34 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: IsMethod: true +// CHECK-0-NEXT: Parent: +// CHECK-0-NEXT: Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'protectedMethod' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 38 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: IsMethod: true +// CHECK-0-NEXT: Parent: +// CHECK-0-NEXT: Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'privateMethod' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 42 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: IsMethod: true +// CHECK-0-NEXT: Parent: +// CHECK-0-NEXT: Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: ... + +// RUN: cat %t/docs/./named.yaml | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: --- +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: ChildFunctions: +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'namedFunction' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Location: +// CHECK-1-NEXT: - LineNumber: 61 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'void' +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'namedStaticFunction' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Location: +// CHECK-1-NEXT: - LineNumber: 62 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'void' +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'namedInlineFunction' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Location: +// CHECK-1-NEXT: - LineNumber: 63 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'void' +// CHECK-1-NEXT: ... + +// RUN: cat %t/docs/./AnonClass.yaml | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: --- +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: Namespace: +// CHECK-2-NEXT: - Type: Namespace +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: DefLocation: +// CHECK-2-NEXT: LineNumber: 78 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: TagType: Class +// CHECK-2-NEXT: Members: +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'anonPublicField' +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'anonProtectedField' +// CHECK-2-NEXT: Access: Protected +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'anonPrivateField' +// CHECK-2-NEXT: Access: Private +// CHECK-2-NEXT: ChildFunctions: +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'anonPublicMethod' +// CHECK-2-NEXT: Namespace: +// CHECK-2-NEXT: - Type: Record +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: - Type: Namespace +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Location: +// CHECK-2-NEXT: - LineNumber: 80 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: IsMethod: true +// CHECK-2-NEXT: Parent: +// CHECK-2-NEXT: Type: Record +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'void' +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'anonProtectedMethod' +// CHECK-2-NEXT: Namespace: +// CHECK-2-NEXT: - Type: Record +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: - Type: Namespace +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Location: +// CHECK-2-NEXT: - LineNumber: 84 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: IsMethod: true +// CHECK-2-NEXT: Parent: +// CHECK-2-NEXT: Type: Record +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'void' +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'anonPrivateMethod' +// CHECK-2-NEXT: Namespace: +// CHECK-2-NEXT: - Type: Record +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: - Type: Namespace +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Location: +// CHECK-2-NEXT: - LineNumber: 88 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: IsMethod: true +// CHECK-2-NEXT: Parent: +// CHECK-2-NEXT: Type: Record +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'void' +// CHECK-2-NEXT: ... + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: --- +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ChildFunctions: +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'anonFunction' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 92 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'anonStaticFunction' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 93 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'anonInlineFunction' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 94 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: ... + +// RUN: cat %t/docs/staticFunctionWithInnerClass/InnerClass.yaml | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: --- +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: 'InnerClass' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Function +// CHECK-4-NEXT: Name: 'staticFunctionWithInnerClass' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 69 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: TagType: Class +// CHECK-4-NEXT: ChildFunctions: +// CHECK-4-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: 'innerPublicMethod' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Record +// CHECK-4-NEXT: Name: 'InnerClass' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: - Type: Function +// CHECK-4-NEXT: Name: 'staticFunctionWithInnerClass' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 71 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: IsMethod: true +// CHECK-4-NEXT: Parent: +// CHECK-4-NEXT: Type: Record +// CHECK-4-NEXT: Name: 'InnerClass' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: ReturnType: +// CHECK-4-NEXT: Type: +// CHECK-4-NEXT: Name: 'int' +// CHECK-4-NEXT: ... + +// RUN: cat %t/docs/named/NamedClass.yaml | FileCheck %s --check-prefix CHECK-5 +// CHECK-5: --- +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: Namespace: +// CHECK-5-NEXT: - Type: Namespace +// CHECK-5-NEXT: Name: 'named' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: DefLocation: +// CHECK-5-NEXT: LineNumber: 47 +// CHECK-5-NEXT: Filename: 'test' +// CHECK-5-NEXT: TagType: Class +// CHECK-5-NEXT: Members: +// CHECK-5-NEXT: - Type: +// CHECK-5-NEXT: Name: 'int' +// CHECK-5-NEXT: Name: 'namedPublicField' +// CHECK-5-NEXT: - Type: +// CHECK-5-NEXT: Name: 'int' +// CHECK-5-NEXT: Name: 'namedProtectedField' +// CHECK-5-NEXT: Access: Protected +// CHECK-5-NEXT: - Type: +// CHECK-5-NEXT: Name: 'int' +// CHECK-5-NEXT: Name: 'namedPrivateField' +// CHECK-5-NEXT: Access: Private +// CHECK-5-NEXT: ChildFunctions: +// CHECK-5-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Name: 'namedPublicMethod' +// CHECK-5-NEXT: Namespace: +// CHECK-5-NEXT: - Type: Record +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: - Type: Namespace +// CHECK-5-NEXT: Name: 'named' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Location: +// CHECK-5-NEXT: - LineNumber: 49 +// CHECK-5-NEXT: Filename: 'test' +// CHECK-5-NEXT: IsMethod: true +// CHECK-5-NEXT: Parent: +// CHECK-5-NEXT: Type: Record +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: ReturnType: +// CHECK-5-NEXT: Type: +// CHECK-5-NEXT: Name: 'void' +// CHECK-5-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Name: 'namedProtectedMethod' +// CHECK-5-NEXT: Namespace: +// CHECK-5-NEXT: - Type: Record +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: - Type: Namespace +// CHECK-5-NEXT: Name: 'named' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Location: +// CHECK-5-NEXT: - LineNumber: 53 +// CHECK-5-NEXT: Filename: 'test' +// CHECK-5-NEXT: IsMethod: true +// CHECK-5-NEXT: Parent: +// CHECK-5-NEXT: Type: Record +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: ReturnType: +// CHECK-5-NEXT: Type: +// CHECK-5-NEXT: Name: 'void' +// CHECK-5-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Name: 'namedPrivateMethod' +// CHECK-5-NEXT: Namespace: +// CHECK-5-NEXT: - Type: Record +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: - Type: Namespace +// CHECK-5-NEXT: Name: 'named' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Location: +// CHECK-5-NEXT: - LineNumber: 57 +// CHECK-5-NEXT: Filename: 'test' +// CHECK-5-NEXT: IsMethod: true +// CHECK-5-NEXT: Parent: +// CHECK-5-NEXT: Type: Record +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: ReturnType: +// CHECK-5-NEXT: Type: +// CHECK-5-NEXT: Name: 'void' +// CHECK-5-NEXT: ... + +// RUN: cat %t/docs/functionWithInnerClass/InnerClass.yaml | FileCheck %s --check-prefix CHECK-6 +// CHECK-6: --- +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'InnerClass' +// CHECK-6-NEXT: Namespace: +// CHECK-6-NEXT: - Type: Function +// CHECK-6-NEXT: Name: 'functionWithInnerClass' +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 15 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: TagType: Class +// CHECK-6-NEXT: ChildFunctions: +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'innerPublicMethod' +// CHECK-6-NEXT: Namespace: +// CHECK-6-NEXT: - Type: Record +// CHECK-6-NEXT: Name: 'InnerClass' +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: - Type: Function +// CHECK-6-NEXT: Name: 'functionWithInnerClass' +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 17 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: IsMethod: true +// CHECK-6-NEXT: Parent: +// CHECK-6-NEXT: Type: Record +// CHECK-6-NEXT: Name: 'InnerClass' +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: ReturnType: +// CHECK-6-NEXT: Type: +// CHECK-6-NEXT: Name: 'int' +// CHECK-6-NEXT: ... + +// RUN: cat %t/docs/inlinedFunctionWithInnerClass/InnerClass.yaml | FileCheck %s --check-prefix CHECK-7 +// CHECK-7: --- +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: Name: 'InnerClass' +// CHECK-7-NEXT: Namespace: +// CHECK-7-NEXT: - Type: Function +// CHECK-7-NEXT: Name: 'inlinedFunctionWithInnerClass' +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: DefLocation: +// CHECK-7-NEXT: LineNumber: 24 +// CHECK-7-NEXT: Filename: 'test' +// CHECK-7-NEXT: TagType: Class +// CHECK-7-NEXT: ChildFunctions: +// CHECK-7-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: Name: 'innerPublicMethod' +// CHECK-7-NEXT: Namespace: +// CHECK-7-NEXT: - Type: Record +// CHECK-7-NEXT: Name: 'InnerClass' +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: - Type: Function +// CHECK-7-NEXT: Name: 'inlinedFunctionWithInnerClass' +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: DefLocation: +// CHECK-7-NEXT: LineNumber: 26 +// CHECK-7-NEXT: Filename: 'test' +// CHECK-7-NEXT: IsMethod: true +// CHECK-7-NEXT: Parent: +// CHECK-7-NEXT: Type: Record +// CHECK-7-NEXT: Name: 'InnerClass' +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: ReturnType: +// CHECK-7-NEXT: Type: +// CHECK-7-NEXT: Name: 'int' +// CHECK-7-NEXT: ... diff --git a/test/clang-doc/yaml-module.cpp b/test/clang-doc/yaml-module.cpp new file mode 100644 index 000000000..80602aca8 --- /dev/null +++ b/test/clang-doc/yaml-module.cpp @@ -0,0 +1,63 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +export module M; + +int moduleFunction(int x); // ModuleLinkage + +static int staticModuleFunction(int x); // ModuleInternalLinkage + +export double exportedModuleFunction(double y, int z); // ExternalLinkage + +// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'moduleFunction' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 11 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'x' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'staticModuleFunction' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 13 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'x' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'exportedModuleFunction' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 15 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'double' +// CHECK-0-NEXT: Name: 'y' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'z' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'double' +// CHECK-0-NEXT: ... diff --git a/test/clang-doc/yaml-namespace.cpp b/test/clang-doc/yaml-namespace.cpp index c547c5c49..d187f7e39 100644 --- a/test/clang-doc/yaml-namespace.cpp +++ b/test/clang-doc/yaml-namespace.cpp @@ -25,81 +25,72 @@ E func(int i) { return X; } } // namespace B } // namespace A -// RUN: clang-doc --format=yaml --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs // RUN: cat %t/docs/./A.yaml | FileCheck %s --check-prefix CHECK-0 // CHECK-0: --- -// CHECK-0-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-0-NEXT: Name: 'A' +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'f' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Namespace +// CHECK-0-NEXT: Name: 'A' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 17 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 11 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' // CHECK-0-NEXT: ... -// RUN: cat %t/docs/A/f.yaml | FileCheck %s --check-prefix CHECK-1 +// RUN: cat %t/docs/A/B.yaml | FileCheck %s --check-prefix CHECK-1 // CHECK-1: --- -// CHECK-1-NEXT: USR: '39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC' -// CHECK-1-NEXT: Name: 'f' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'B' // CHECK-1-NEXT: Namespace: // CHECK-1-NEXT: - Type: Namespace // CHECK-1-NEXT: Name: 'A' -// CHECK-1-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' -// CHECK-1-NEXT: DefLocation: -// CHECK-1-NEXT: LineNumber: 17 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: Location: -// CHECK-1-NEXT: - LineNumber: 11 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: ReturnType: -// CHECK-1-NEXT: Type: -// CHECK-1-NEXT: Name: 'void' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: ChildFunctions: +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'func' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'B' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'A' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: DefLocation: +// CHECK-1-NEXT: LineNumber: 23 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: Params: +// CHECK-1-NEXT: - Type: +// CHECK-1-NEXT: Name: 'int' +// CHECK-1-NEXT: Name: 'i' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'enum A::B::E' +// CHECK-1-NEXT: ChildEnums: +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'E' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'B' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'A' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: DefLocation: +// CHECK-1-NEXT: LineNumber: 21 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: Members: +// CHECK-1-NEXT: - 'X' // CHECK-1-NEXT: ... - -// RUN: cat %t/docs/A/B.yaml | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: --- -// CHECK-2-NEXT: USR: 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' -// CHECK-2-NEXT: Name: 'B' -// CHECK-2-NEXT: Namespace: -// CHECK-2-NEXT: - Type: Namespace -// CHECK-2-NEXT: Name: 'A' -// CHECK-2-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' -// CHECK-2-NEXT: ... - -// RUN: cat %t/docs/A/B/E.yaml | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: --- -// CHECK-3-NEXT: USR: 'E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775' -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Namespace -// CHECK-3-NEXT: Name: 'B' -// CHECK-3-NEXT: USR: 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' -// CHECK-3-NEXT: - Type: Namespace -// CHECK-3-NEXT: Name: 'A' -// CHECK-3-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 21 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: Members: -// CHECK-3-NEXT: - 'X' -// CHECK-3-NEXT: ... - -// RUN: cat %t/docs/A/B/func.yaml | FileCheck %s --check-prefix CHECK-4 -// CHECK-4: --- -// CHECK-4-NEXT: USR: '9A82CB33ED0FDF81EE383D31CD0957D153C5E840' -// CHECK-4-NEXT: Name: 'func' -// CHECK-4-NEXT: Namespace: -// CHECK-4-NEXT: - Type: Namespace -// CHECK-4-NEXT: Name: 'B' -// CHECK-4-NEXT: USR: 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' -// CHECK-4-NEXT: - Type: Namespace -// CHECK-4-NEXT: Name: 'A' -// CHECK-4-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' -// CHECK-4-NEXT: DefLocation: -// CHECK-4-NEXT: LineNumber: 23 -// CHECK-4-NEXT: Filename: 'test' -// CHECK-4-NEXT: Params: -// CHECK-4-NEXT: - Type: -// CHECK-4-NEXT: Name: 'int' -// CHECK-4-NEXT: Name: 'i' -// CHECK-4-NEXT: ReturnType: -// CHECK-4-NEXT: Type: -// CHECK-4-NEXT: Name: 'enum A::B::E' -// CHECK-4-NEXT: ... diff --git a/test/clang-doc/yaml-record.cpp b/test/clang-doc/yaml-record.cpp index 1d5235f5c..8fad22017 100644 --- a/test/clang-doc/yaml-record.cpp +++ b/test/clang-doc/yaml-record.cpp @@ -39,12 +39,12 @@ class X { class Y {}; }; -// RUN: clang-doc --format=yaml --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs // RUN: cat %t/docs/./C.yaml | FileCheck %s --check-prefix CHECK-0 // CHECK-0: --- -// CHECK-0-NEXT: USR: '06B5F6A19BA9F6A832E127C9968282B94619B210' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-0-NEXT: Name: 'C' // CHECK-0-NEXT: DefLocation: // CHECK-0-NEXT: LineNumber: 21 @@ -57,7 +57,7 @@ class X { // RUN: cat %t/docs/./A.yaml | FileCheck %s --check-prefix CHECK-1 // CHECK-1: --- -// CHECK-1-NEXT: USR: 'ACE81AFA6627B4CEF2B456FB6E1252925674AF7E' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-1-NEXT: Name: 'A' // CHECK-1-NEXT: DefLocation: // CHECK-1-NEXT: LineNumber: 15 @@ -74,7 +74,7 @@ class X { // RUN: cat %t/docs/./F.yaml | FileCheck %s --check-prefix CHECK-2 // CHECK-2: --- -// CHECK-2-NEXT: USR: 'E3B54702FABFF4037025BA194FC27C47006330B5' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-2-NEXT: Name: 'F' // CHECK-2-NEXT: DefLocation: // CHECK-2-NEXT: LineNumber: 36 @@ -83,26 +83,81 @@ class X { // CHECK-2-NEXT: Parents: // CHECK-2-NEXT: - Type: Record // CHECK-2-NEXT: Name: 'E' -// CHECK-2-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-2-NEXT: VirtualParents: // CHECK-2-NEXT: - Type: Record // CHECK-2-NEXT: Name: 'D' -// CHECK-2-NEXT: USR: '0921737541208B8FA9BB42B60F78AC1D779AA054' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-2-NEXT: ... // RUN: cat %t/docs/./E.yaml | FileCheck %s --check-prefix CHECK-3 // CHECK-3: --- -// CHECK-3-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-3-NEXT: Name: 'E' // CHECK-3-NEXT: DefLocation: // CHECK-3-NEXT: LineNumber: 25 // CHECK-3-NEXT: Filename: 'test' // CHECK-3-NEXT: TagType: Class +// CHECK-3-NEXT: ChildFunctions: +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 27 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: '~E' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 28 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'ProtectedMethod' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 34 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 31 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' // CHECK-3-NEXT: ... // RUN: cat %t/docs/./D.yaml | FileCheck %s --check-prefix CHECK-4 // CHECK-4: --- -// CHECK-4-NEXT: USR: '0921737541208B8FA9BB42B60F78AC1D779AA054' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-4-NEXT: Name: 'D' // CHECK-4-NEXT: DefLocation: // CHECK-4-NEXT: LineNumber: 23 @@ -110,143 +165,72 @@ class X { // CHECK-4-NEXT: TagType: Class // CHECK-4-NEXT: ... -// RUN: cat %t/docs/./B.yaml | FileCheck %s --check-prefix CHECK-5 +// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-5 // CHECK-5: --- -// CHECK-5-NEXT: USR: 'FC07BD34D5E77782C263FA944447929EA8753740' -// CHECK-5-NEXT: Name: 'B' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Name: 'X' // CHECK-5-NEXT: DefLocation: -// CHECK-5-NEXT: LineNumber: 17 +// CHECK-5-NEXT: LineNumber: 38 // CHECK-5-NEXT: Filename: 'test' -// CHECK-5-NEXT: Members: -// CHECK-5-NEXT: - 'X' -// CHECK-5-NEXT: - 'Y' +// CHECK-5-NEXT: TagType: Class // CHECK-5-NEXT: ... -// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-6 +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-6 // CHECK-6: --- -// CHECK-6-NEXT: USR: 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E' -// CHECK-6-NEXT: Name: 'X' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 38 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: TagType: Class +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: ChildFunctions: +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'H' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 11 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: ReturnType: +// CHECK-6-NEXT: Type: +// CHECK-6-NEXT: Name: 'void' +// CHECK-6-NEXT: ChildEnums: +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'B' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 17 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: Members: +// CHECK-6-NEXT: - 'X' +// CHECK-6-NEXT: - 'Y' +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'Bc' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 19 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: Scoped: true +// CHECK-6-NEXT: Members: +// CHECK-6-NEXT: - 'A' +// CHECK-6-NEXT: - 'B' // CHECK-6-NEXT: ... -// RUN: cat %t/docs/./H.yaml | FileCheck %s --check-prefix CHECK-7 +// RUN: cat %t/docs/H/I.yaml | FileCheck %s --check-prefix CHECK-7 // CHECK-7: --- -// CHECK-7-NEXT: USR: 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E' -// CHECK-7-NEXT: Name: 'H' +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: Name: 'I' +// CHECK-7-NEXT: Namespace: +// CHECK-7-NEXT: - Type: Function +// CHECK-7-NEXT: Name: 'H' +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-7-NEXT: DefLocation: -// CHECK-7-NEXT: LineNumber: 11 +// CHECK-7-NEXT: LineNumber: 12 // CHECK-7-NEXT: Filename: 'test' -// CHECK-7-NEXT: ReturnType: -// CHECK-7-NEXT: Type: -// CHECK-7-NEXT: Name: 'void' +// CHECK-7-NEXT: TagType: Class // CHECK-7-NEXT: ... -// RUN: cat %t/docs/./Bc.yaml | FileCheck %s --check-prefix CHECK-8 +// RUN: cat %t/docs/X/Y.yaml | FileCheck %s --check-prefix CHECK-8 // CHECK-8: --- -// CHECK-8-NEXT: USR: '1E3438A08BA22025C0B46289FF0686F92C8924C5' -// CHECK-8-NEXT: Name: 'Bc' +// CHECK-8-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-8-NEXT: Name: 'Y' +// CHECK-8-NEXT: Namespace: +// CHECK-8-NEXT: - Type: Record +// CHECK-8-NEXT: Name: 'X' +// CHECK-8-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-8-NEXT: DefLocation: -// CHECK-8-NEXT: LineNumber: 19 +// CHECK-8-NEXT: LineNumber: 39 // CHECK-8-NEXT: Filename: 'test' -// CHECK-8-NEXT: Scoped: true -// CHECK-8-NEXT: Members: -// CHECK-8-NEXT: - 'A' -// CHECK-8-NEXT: - 'B' +// CHECK-8-NEXT: TagType: Class // CHECK-8-NEXT: ... - -// RUN: cat %t/docs/H/I.yaml | FileCheck %s --check-prefix CHECK-9 -// CHECK-9: --- -// CHECK-9-NEXT: USR: '3FB542274573CAEAD54CEBFFCAEE3D77FB9713D8' -// CHECK-9-NEXT: Name: 'I' -// CHECK-9-NEXT: Namespace: -// CHECK-9-NEXT: - Type: Function -// CHECK-9-NEXT: Name: 'H' -// CHECK-9-NEXT: USR: 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E' -// CHECK-9-NEXT: DefLocation: -// CHECK-9-NEXT: LineNumber: 12 -// CHECK-9-NEXT: Filename: 'test' -// CHECK-9-NEXT: TagType: Class -// CHECK-9-NEXT: ... - -// RUN: cat %t/docs/X/Y.yaml | FileCheck %s --check-prefix CHECK-10 -// CHECK-10: --- -// CHECK-10-NEXT: USR: '641AB4A3D36399954ACDE29C7A8833032BF40472' -// CHECK-10-NEXT: Name: 'Y' -// CHECK-10-NEXT: Namespace: -// CHECK-10-NEXT: - Type: Record -// CHECK-10-NEXT: Name: 'X' -// CHECK-10-NEXT: USR: 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E' -// CHECK-10-NEXT: DefLocation: -// CHECK-10-NEXT: LineNumber: 39 -// CHECK-10-NEXT: Filename: 'test' -// CHECK-10-NEXT: TagType: Class -// CHECK-10-NEXT: ... - -// RUN: cat %t/docs/E/ProtectedMethod.yaml | FileCheck %s --check-prefix CHECK-11 -// CHECK-11: --- -// CHECK-11-NEXT: USR: '5093D428CDC62096A67547BA52566E4FB9404EEE' -// CHECK-11-NEXT: Name: 'ProtectedMethod' -// CHECK-11-NEXT: Namespace: -// CHECK-11-NEXT: - Type: Record -// CHECK-11-NEXT: Name: 'E' -// CHECK-11-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' -// CHECK-11-NEXT: DefLocation: -// CHECK-11-NEXT: LineNumber: 34 -// CHECK-11-NEXT: Filename: 'test' -// CHECK-11-NEXT: Location: -// CHECK-11-NEXT: - LineNumber: 31 -// CHECK-11-NEXT: Filename: 'test' -// CHECK-11-NEXT: IsMethod: true -// CHECK-11-NEXT: Parent: -// CHECK-11-NEXT: Type: Record -// CHECK-11-NEXT: Name: 'E' -// CHECK-11-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' -// CHECK-11-NEXT: ReturnType: -// CHECK-11-NEXT: Type: -// CHECK-11-NEXT: Name: 'void' -// CHECK-11-NEXT: ... - -// RUN: cat %t/docs/E/E.yaml | FileCheck %s --check-prefix CHECK-12 -// CHECK-12: --- -// CHECK-12-NEXT: USR: 'DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4' -// CHECK-12-NEXT: Name: 'E' -// CHECK-12-NEXT: Namespace: -// CHECK-12-NEXT: - Type: Record -// CHECK-12-NEXT: Name: 'E' -// CHECK-12-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' -// CHECK-12-NEXT: DefLocation: -// CHECK-12-NEXT: LineNumber: 27 -// CHECK-12-NEXT: Filename: 'test' -// CHECK-12-NEXT: IsMethod: true -// CHECK-12-NEXT: Parent: -// CHECK-12-NEXT: Type: Record -// CHECK-12-NEXT: Name: 'E' -// CHECK-12-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' -// CHECK-12-NEXT: ReturnType: -// CHECK-12-NEXT: Type: -// CHECK-12-NEXT: Name: 'void' -// CHECK-12-NEXT: ... - -// RUN: cat %t/docs/E/~E.yaml | FileCheck %s --check-prefix CHECK-13 -// CHECK-13: --- -// CHECK-13-NEXT: USR: 'BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17' -// CHECK-13-NEXT: Name: '~E' -// CHECK-13-NEXT: Namespace: -// CHECK-13-NEXT: - Type: Record -// CHECK-13-NEXT: Name: 'E' -// CHECK-13-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' -// CHECK-13-NEXT: DefLocation: -// CHECK-13-NEXT: LineNumber: 28 -// CHECK-13-NEXT: Filename: 'test' -// CHECK-13-NEXT: IsMethod: true -// CHECK-13-NEXT: Parent: -// CHECK-13-NEXT: Type: Record -// CHECK-13-NEXT: Name: 'E' -// CHECK-13-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' -// CHECK-13-NEXT: ReturnType: -// CHECK-13-NEXT: Type: -// CHECK-13-NEXT: Name: 'void' -// CHECK-13-NEXT: ... From c86555712a2c072480e6aed8886ae70e6bcf4304 Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Thu, 2 Aug 2018 18:01:37 +0000 Subject: [PATCH 005/686] Revert "[clang-doc] Refactoring mapper to map by scope" This reverts commit r338738 as it's breaking the bots. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338748 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/BitcodeReader.cpp | 81 +-- clang-doc/BitcodeReader.h | 3 +- clang-doc/BitcodeWriter.cpp | 14 - clang-doc/BitcodeWriter.h | 26 +- clang-doc/Mapper.cpp | 12 +- clang-doc/Representation.cpp | 70 +-- clang-doc/Representation.h | 37 -- clang-doc/Serialize.cpp | 142 ++--- clang-doc/Serialize.h | 22 +- clang-doc/YAMLGenerator.cpp | 14 +- clang-doc/gen_tests.py | 20 +- clang-doc/tool/ClangDocMain.cpp | 82 +-- test/clang-doc/bc-comment.cpp | 282 +++++---- test/clang-doc/bc-linkage.cpp | 844 -------------------------- test/clang-doc/bc-module.cpp | 87 --- test/clang-doc/bc-namespace.cpp | 172 +++--- test/clang-doc/bc-record.cpp | 390 ++++++------ test/clang-doc/mapper-comment.cpp | 72 ++- test/clang-doc/mapper-linkage.cpp | 402 ------------ test/clang-doc/mapper-module.cpp | 51 -- test/clang-doc/mapper-namespace.cpp | 154 +++-- test/clang-doc/mapper-record.cpp | 327 ++++++---- test/clang-doc/module.cpp | 61 ++ test/clang-doc/public-comment.cpp | 138 ----- test/clang-doc/public-linkage.cpp | 299 --------- test/clang-doc/public-module.cpp | 84 +-- test/clang-doc/public-namespace.cpp | 96 --- test/clang-doc/public-record.cpp | 208 ------- test/clang-doc/public-records.cpp | 341 +++++++++++ test/clang-doc/test_cases/linkage.cpp | 95 --- test/clang-doc/test_cases/module.cpp | 15 - test/clang-doc/yaml-comment.cpp | 176 +++--- test/clang-doc/yaml-linkage.cpp | 529 ---------------- test/clang-doc/yaml-module.cpp | 63 -- test/clang-doc/yaml-namespace.cpp | 123 ++-- test/clang-doc/yaml-record.cpp | 246 ++++---- 36 files changed, 1629 insertions(+), 4149 deletions(-) delete mode 100644 test/clang-doc/bc-linkage.cpp delete mode 100644 test/clang-doc/bc-module.cpp delete mode 100644 test/clang-doc/mapper-linkage.cpp delete mode 100644 test/clang-doc/mapper-module.cpp create mode 100644 test/clang-doc/module.cpp delete mode 100644 test/clang-doc/public-comment.cpp delete mode 100644 test/clang-doc/public-linkage.cpp delete mode 100644 test/clang-doc/public-namespace.cpp delete mode 100644 test/clang-doc/public-record.cpp create mode 100644 test/clang-doc/public-records.cpp delete mode 100644 test/clang-doc/test_cases/linkage.cpp delete mode 100644 test/clang-doc/test_cases/module.cpp delete mode 100644 test/clang-doc/yaml-linkage.cpp delete mode 100644 test/clang-doc/yaml-module.cpp diff --git a/clang-doc/BitcodeReader.cpp b/clang-doc/BitcodeReader.cpp index 70e92c7a6..fa51d0135 100644 --- a/clang-doc/BitcodeReader.cpp +++ b/clang-doc/BitcodeReader.cpp @@ -100,8 +100,6 @@ bool decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { case FieldId::F_parent: case FieldId::F_vparent: case FieldId::F_type: - case FieldId::F_child_namespace: - case FieldId::F_child_record: case FieldId::F_default: Field = F; return true; @@ -374,12 +372,6 @@ template <> void addReference(NamespaceInfo *I, Reference &&R, FieldId F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); break; - case FieldId::F_child_namespace: - I->ChildNamespaces.emplace_back(std::move(R)); - break; - case FieldId::F_child_record: - I->ChildRecords.emplace_back(std::move(R)); - break; default: llvm::errs() << "Invalid field type for info.\n"; exit(1); @@ -411,37 +403,12 @@ template <> void addReference(RecordInfo *I, Reference &&R, FieldId F) { case FieldId::F_vparent: I->VirtualParents.emplace_back(std::move(R)); break; - case FieldId::F_child_record: - I->ChildRecords.emplace_back(std::move(R)); - break; default: llvm::errs() << "Invalid field type for info.\n"; exit(1); } } -template -void addChild(T I, ChildInfoType &&R) { - llvm::errs() << "Invalid child type for info.\n"; - exit(1); -} - -template <> void addChild(NamespaceInfo *I, FunctionInfo &&R) { - I->ChildFunctions.emplace_back(std::move(R)); -} - -template <> void addChild(NamespaceInfo *I, EnumInfo &&R) { - I->ChildEnums.emplace_back(std::move(R)); -} - -template <> void addChild(RecordInfo *I, FunctionInfo &&R) { - I->ChildFunctions.emplace_back(std::move(R)); -} - -template <> void addChild(RecordInfo *I, EnumInfo &&R) { - I->ChildEnums.emplace_back(std::move(R)); -} - // Read records from bitcode into a given info. template bool ClangDocBitcodeReader::readRecord(unsigned ID, T I) { Record R; @@ -488,8 +455,7 @@ template bool ClangDocBitcodeReader::readBlock(unsigned ID, T I) { template bool ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) { switch (ID) { - // Blocks can only have Comment, Reference, TypeInfo, FunctionInfo, or - // EnumInfo subblocks + // Blocks can only have Comment, Reference, or TypeInfo subblocks case BI_COMMENT_BLOCK_ID: if (readBlock(ID, getCommentInfo(I))) return true; @@ -526,22 +492,6 @@ bool ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) { } return false; } - case BI_FUNCTION_BLOCK_ID: { - FunctionInfo F; - if (readBlock(ID, &F)) { - addChild(I, std::move(F)); - return true; - } - return false; - } - case BI_ENUM_BLOCK_ID: { - EnumInfo E; - if (readBlock(ID, &E)) { - addChild(I, std::move(E)); - return true; - } - return false; - } default: llvm::errs() << "Invalid subblock type.\n"; return false; @@ -623,21 +573,16 @@ std::unique_ptr ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { } // Entry point -llvm::Expected>> -ClangDocBitcodeReader::readBitcode() { +std::vector> ClangDocBitcodeReader::readBitcode() { std::vector> Infos; if (!validateStream()) - return llvm::make_error("Invalid bitcode stream.\n", - llvm::inconvertibleErrorCode()); - ; + return Infos; // Read the top level blocks. while (!Stream.AtEndOfStream()) { unsigned Code = Stream.ReadCode(); if (Code != llvm::bitc::ENTER_SUBBLOCK) - return llvm::make_error( - "Missing subblock in bitcode.\n", llvm::inconvertibleErrorCode()); - ; + return Infos; unsigned ID = Stream.ReadSubBlockID(); switch (ID) { @@ -647,30 +592,24 @@ ClangDocBitcodeReader::readBitcode() { case BI_MEMBER_TYPE_BLOCK_ID: case BI_COMMENT_BLOCK_ID: case BI_REFERENCE_BLOCK_ID: - return llvm::make_error( - "Invalid top level block in bitcode.\n", - llvm::inconvertibleErrorCode()); - ; + llvm::errs() << "Invalid top level block.\n"; + return Infos; case BI_NAMESPACE_BLOCK_ID: case BI_RECORD_BLOCK_ID: case BI_ENUM_BLOCK_ID: case BI_FUNCTION_BLOCK_ID: - if (std::unique_ptr I = readBlockToInfo(ID)) + if (std::unique_ptr I = readBlockToInfo(ID)) { Infos.emplace_back(std::move(I)); + } return Infos; case BI_VERSION_BLOCK_ID: if (readBlock(ID, VersionNumber)) continue; - return llvm::make_error( - "Invalid bitcode version in bitcode.\n", - llvm::inconvertibleErrorCode()); - ; + return Infos; case llvm::bitc::BLOCKINFO_BLOCK_ID: if (readBlockInfoBlock()) continue; - return llvm::make_error( - "Invalid BlockInfo in bitcode.\n", llvm::inconvertibleErrorCode()); - ; + return Infos; default: if (!Stream.SkipBlock()) continue; diff --git a/clang-doc/BitcodeReader.h b/clang-doc/BitcodeReader.h index aaf25257c..c0cf24a17 100644 --- a/clang-doc/BitcodeReader.h +++ b/clang-doc/BitcodeReader.h @@ -19,7 +19,6 @@ #include "BitcodeWriter.h" #include "Representation.h" #include "clang/AST/AST.h" -#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Bitcode/BitstreamReader.h" @@ -32,7 +31,7 @@ class ClangDocBitcodeReader { ClangDocBitcodeReader(llvm::BitstreamCursor &Stream) : Stream(Stream) {} // Main entry point, calls readBlock to read each block in the given stream. - llvm::Expected>> readBitcode(); + std::vector> readBitcode(); private: enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin }; diff --git a/clang-doc/BitcodeWriter.cpp b/clang-doc/BitcodeWriter.cpp index f73724e4f..623ed1a2a 100644 --- a/clang-doc/BitcodeWriter.cpp +++ b/clang-doc/BitcodeWriter.cpp @@ -434,14 +434,6 @@ void ClangDocBitcodeWriter::emitBlock(const NamespaceInfo &I) { emitBlock(N, FieldId::F_namespace); for (const auto &CI : I.Description) emitBlock(CI); - for (const auto &C : I.ChildNamespaces) - emitBlock(C, FieldId::F_child_namespace); - for (const auto &C : I.ChildRecords) - emitBlock(C, FieldId::F_child_record); - for (const auto &C : I.ChildFunctions) - emitBlock(C); - for (const auto &C : I.ChildEnums) - emitBlock(C); } void ClangDocBitcodeWriter::emitBlock(const EnumInfo &I) { @@ -480,12 +472,6 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) { emitBlock(P, FieldId::F_parent); for (const auto &P : I.VirtualParents) emitBlock(P, FieldId::F_vparent); - for (const auto &C : I.ChildRecords) - emitBlock(C, FieldId::F_child_record); - for (const auto &C : I.ChildFunctions) - emitBlock(C); - for (const auto &C : I.ChildEnums) - emitBlock(C); } void ClangDocBitcodeWriter::emitBlock(const FunctionInfo &I) { diff --git a/clang-doc/BitcodeWriter.h b/clang-doc/BitcodeWriter.h index 2ff46c612..8aa38e142 100644 --- a/clang-doc/BitcodeWriter.h +++ b/clang-doc/BitcodeWriter.h @@ -68,10 +68,11 @@ enum BlockId { // New Ids need to be added to the enum here, and to the relevant IdNameMap and // initialization list in the implementation file. +#define INFORECORDS(X) X##_USR, X##_NAME + enum RecordId { VERSION = 1, - FUNCTION_USR, - FUNCTION_NAME, + INFORECORDS(FUNCTION), FUNCTION_DEFLOCATION, FUNCTION_LOCATION, FUNCTION_ACCESS, @@ -90,16 +91,13 @@ enum RecordId { FIELD_TYPE_NAME, MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS, - NAMESPACE_USR, - NAMESPACE_NAME, - ENUM_USR, - ENUM_NAME, + INFORECORDS(NAMESPACE), + INFORECORDS(ENUM), ENUM_DEFLOCATION, ENUM_LOCATION, ENUM_MEMBER, ENUM_SCOPED, - RECORD_USR, - RECORD_NAME, + INFORECORDS(RECORD), RECORD_DEFLOCATION, RECORD_LOCATION, RECORD_TAG_TYPE, @@ -114,16 +112,10 @@ enum RecordId { static constexpr unsigned BlockIdCount = BI_LAST - BI_FIRST; static constexpr unsigned RecordIdCount = RI_LAST - RI_FIRST; +#undef INFORECORDS + // Identifiers for differentiating between subblocks -enum class FieldId { - F_default, - F_namespace, - F_parent, - F_vparent, - F_type, - F_child_namespace, - F_child_record -}; +enum class FieldId { F_default, F_namespace, F_parent, F_vparent, F_type }; class ClangDocBitcodeWriter { public: diff --git a/clang-doc/Mapper.cpp b/clang-doc/Mapper.cpp index 4456c1ed9..fb0b42af3 100644 --- a/clang-doc/Mapper.cpp +++ b/clang-doc/Mapper.cpp @@ -13,7 +13,6 @@ #include "clang/AST/Comment.h" #include "clang/Index/USRGeneration.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Support/Error.h" using clang::comments::FullComment; @@ -34,15 +33,14 @@ template bool MapASTVisitor::mapDecl(const T *D) { if (index::generateUSRForDecl(D, USR)) return true; - auto I = serialize::emitInfo( + std::string info = serialize::emitInfo( D, getComment(D, D->getASTContext()), getLine(D, D->getASTContext()), getFile(D, D->getASTContext()), CDCtx.PublicOnly); - // A null in place of I indicates that the serializer is skipping this decl - // for some reason (e.g. we're only reporting public decls). - if (I) - CDCtx.ECtx->reportResult(llvm::toHex(llvm::toStringRef(I->USR)), - serialize::serialize(I)); + if (info != "") + CDCtx.ECtx->reportResult( + llvm::toHex(llvm::toStringRef(serialize::hashUSR(USR))), info); + return true; } diff --git a/clang-doc/Representation.cpp b/clang-doc/Representation.cpp index f4372dc5b..6107b98ec 100644 --- a/clang-doc/Representation.cpp +++ b/clang-doc/Representation.cpp @@ -26,70 +26,17 @@ namespace clang { namespace doc { -namespace { - -const SymbolID EmptySID = SymbolID(); +static const SymbolID EmptySID = SymbolID(); template -llvm::Expected> -reduce(std::vector> &Values) { - if (Values.empty()) - return llvm::make_error(" No values to reduce.\n", - llvm::inconvertibleErrorCode()); - std::unique_ptr Merged = llvm::make_unique(Values[0]->USR); +std::unique_ptr reduce(std::vector> &Values) { + std::unique_ptr Merged = llvm::make_unique(); T *Tmp = static_cast(Merged.get()); for (auto &I : Values) Tmp->merge(std::move(*static_cast(I.get()))); return Merged; } -// Return the index of the matching child in the vector, or -1 if merge is not -// necessary. -template -int getChildIndexIfExists(std::vector &Children, T &ChildToMerge) { - for (unsigned long I = 0; I < Children.size(); I++) { - if (ChildToMerge.USR == Children[I].USR) - return I; - } - return -1; -} - -// For References, we don't need to actually merge them, we just don't want -// duplicates. -void reduceChildren(std::vector &Children, - std::vector &&ChildrenToMerge) { - for (auto &ChildToMerge : ChildrenToMerge) { - if (getChildIndexIfExists(Children, ChildToMerge) == -1) - Children.push_back(std::move(ChildToMerge)); - } -} - -void reduceChildren(std::vector &Children, - std::vector &&ChildrenToMerge) { - for (auto &ChildToMerge : ChildrenToMerge) { - int mergeIdx = getChildIndexIfExists(Children, ChildToMerge); - if (mergeIdx == -1) { - Children.push_back(std::move(ChildToMerge)); - continue; - } - Children[mergeIdx].merge(std::move(ChildToMerge)); - } -} - -void reduceChildren(std::vector &Children, - std::vector &&ChildrenToMerge) { - for (auto &ChildToMerge : ChildrenToMerge) { - int mergeIdx = getChildIndexIfExists(Children, ChildToMerge); - if (mergeIdx == -1) { - Children.push_back(std::move(ChildToMerge)); - continue; - } - Children[mergeIdx].merge(std::move(ChildToMerge)); - } -} - -} // namespace - // Dispatch function. llvm::Expected> mergeInfos(std::vector> &Values) { @@ -126,7 +73,7 @@ void Info::mergeBase(Info &&Other) { } bool Info::mergeable(const Info &Other) { - return IT == Other.IT && USR == Other.USR; + return IT == Other.IT && (USR == EmptySID || USR == Other.USR); } void SymbolInfo::merge(SymbolInfo &&Other) { @@ -140,11 +87,6 @@ void SymbolInfo::merge(SymbolInfo &&Other) { void NamespaceInfo::merge(NamespaceInfo &&Other) { assert(mergeable(Other)); - // Reduce children if necessary. - reduceChildren(ChildNamespaces, std::move(Other.ChildNamespaces)); - reduceChildren(ChildRecords, std::move(Other.ChildRecords)); - reduceChildren(ChildFunctions, std::move(Other.ChildFunctions)); - reduceChildren(ChildEnums, std::move(Other.ChildEnums)); mergeBase(std::move(Other)); } @@ -158,10 +100,6 @@ void RecordInfo::merge(RecordInfo &&Other) { Parents = std::move(Other.Parents); if (VirtualParents.empty()) VirtualParents = std::move(Other.VirtualParents); - // Reduce children if necessary. - reduceChildren(ChildRecords, std::move(Other.ChildRecords)); - reduceChildren(ChildFunctions, std::move(Other.ChildFunctions)); - reduceChildren(ChildEnums, std::move(Other.ChildEnums)); SymbolInfo::merge(std::move(Other)); } diff --git a/clang-doc/Representation.h b/clang-doc/Representation.h index 9e88bd9c8..f952954f9 100644 --- a/clang-doc/Representation.h +++ b/clang-doc/Representation.h @@ -31,9 +31,6 @@ namespace doc { using SymbolID = std::array; struct Info; -struct FunctionInfo; -struct EnumInfo; - enum class InfoType { IT_default, IT_namespace, @@ -156,9 +153,6 @@ struct Location { struct Info { Info() = default; Info(InfoType IT) : IT(IT) {} - Info(InfoType IT, SymbolID USR) : USR(USR), IT(IT) {} - Info(InfoType IT, SymbolID USR, StringRef Name) - : USR(USR), IT(IT), Name(Name) {} Info(const Info &Other) = delete; Info(Info &&Other) = default; @@ -172,36 +166,18 @@ struct Info { void mergeBase(Info &&I); bool mergeable(const Info &Other); - - // Returns a reference to the parent scope (that is, the immediate parent - // namespace or class in which this decl resides). - llvm::Expected getEnclosingScope(); }; // Info for namespaces. struct NamespaceInfo : public Info { NamespaceInfo() : Info(InfoType::IT_namespace) {} - NamespaceInfo(SymbolID USR) : Info(InfoType::IT_namespace, USR) {} - NamespaceInfo(SymbolID USR, StringRef Name) - : Info(InfoType::IT_namespace, USR, Name) {} void merge(NamespaceInfo &&I); - - // Namespaces and Records are references because they will be properly - // documented in their own info, while the entirety of Functions and Enums are - // included here because they should not have separate documentation from - // their scope. - std::vector ChildNamespaces; - std::vector ChildRecords; - std::vector ChildFunctions; - std::vector ChildEnums; }; // Info for symbols. struct SymbolInfo : public Info { SymbolInfo(InfoType IT) : Info(IT) {} - SymbolInfo(InfoType IT, SymbolID USR) : Info(IT, USR) {} - SymbolInfo(InfoType IT, SymbolID USR, StringRef Name) : Info(IT, USR, Name) {} void merge(SymbolInfo &&I); @@ -213,7 +189,6 @@ struct SymbolInfo : public Info { // Info for functions. struct FunctionInfo : public SymbolInfo { FunctionInfo() : SymbolInfo(InfoType::IT_function) {} - FunctionInfo(SymbolID USR) : SymbolInfo(InfoType::IT_function, USR) {} void merge(FunctionInfo &&I); @@ -230,9 +205,6 @@ struct FunctionInfo : public SymbolInfo { // Info for types. struct RecordInfo : public SymbolInfo { RecordInfo() : SymbolInfo(InfoType::IT_record) {} - RecordInfo(SymbolID USR) : SymbolInfo(InfoType::IT_record, USR) {} - RecordInfo(SymbolID USR, StringRef Name) - : SymbolInfo(InfoType::IT_record, USR, Name) {} void merge(RecordInfo &&I); @@ -246,21 +218,12 @@ struct RecordInfo : public SymbolInfo { // parents). llvm::SmallVector VirtualParents; // List of virtual base/parent records. - - // Records are references because they will be properly - // documented in their own info, while the entirety of Functions and Enums are - // included here because they should not have separate documentation from - // their scope. - std::vector ChildRecords; - std::vector ChildFunctions; - std::vector ChildEnums; }; // TODO: Expand to allow for documenting templating. // Info for types. struct EnumInfo : public SymbolInfo { EnumInfo() : SymbolInfo(InfoType::IT_enum) {} - EnumInfo(SymbolID USR) : SymbolInfo(InfoType::IT_enum, USR) {} void merge(EnumInfo &&I); diff --git a/clang-doc/Serialize.cpp b/clang-doc/Serialize.cpp index b12961463..c1e6d316f 100644 --- a/clang-doc/Serialize.cpp +++ b/clang-doc/Serialize.cpp @@ -152,21 +152,6 @@ template static std::string serialize(T &I) { return Buffer.str().str(); } -std::string serialize(std::unique_ptr &I) { - switch (I->IT) { - case InfoType::IT_namespace: - return serialize(*static_cast(I.get())); - case InfoType::IT_record: - return serialize(*static_cast(I.get())); - case InfoType::IT_enum: - return serialize(*static_cast(I.get())); - case InfoType::IT_function: - return serialize(*static_cast(I.get())); - default: - return ""; - } -} - static void parseFullComment(const FullComment *C, CommentInfo &CI) { ClangDocCommentVisitor Visitor(CI); Visitor.parseComment(C); @@ -321,108 +306,61 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, parseParameters(I, D); } -std::unique_ptr emitInfo(const NamespaceDecl *D, const FullComment *FC, - int LineNumber, llvm::StringRef File, - bool PublicOnly) { +std::string emitInfo(const NamespaceDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, bool PublicOnly) { if (PublicOnly && ((D->isAnonymousNamespace()) || !isPublic(D->getAccess(), D->getLinkageInternal()))) - return nullptr; - auto I = llvm::make_unique(); - populateInfo(*I, D, FC); - return I; + return ""; + NamespaceInfo I; + populateInfo(I, D, FC); + return serialize(I); } -std::unique_ptr emitInfo(const RecordDecl *D, const FullComment *FC, - int LineNumber, llvm::StringRef File, - bool PublicOnly) { +std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, + llvm::StringRef File, bool PublicOnly) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) - return nullptr; - auto I = llvm::make_unique(); - populateSymbolInfo(*I, D, FC, LineNumber, File); - I->TagType = D->getTagKind(); - parseFields(*I, D, PublicOnly); + return ""; + RecordInfo I; + populateSymbolInfo(I, D, FC, LineNumber, File); + I.TagType = D->getTagKind(); + parseFields(I, D, PublicOnly); if (const auto *C = dyn_cast(D)) - parseBases(*I, C); - return I; + parseBases(I, C); + return serialize(I); } -std::unique_ptr emitInfo(const FunctionDecl *D, const FullComment *FC, - int LineNumber, llvm::StringRef File, - bool PublicOnly) { +std::string emitInfo(const FunctionDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, bool PublicOnly) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) - return nullptr; - FunctionInfo Func; - populateFunctionInfo(Func, D, FC, LineNumber, File); - Func.Access = clang::AccessSpecifier::AS_none; - - // Wrap in enclosing scope - auto I = llvm::make_unique(); - if (!Func.Namespace.empty()) - I->USR = Func.Namespace[0].USR; - else - I->USR = SymbolID(); - I->ChildFunctions.push_back(std::move(Func)); - return I; + return ""; + FunctionInfo I; + populateFunctionInfo(I, D, FC, LineNumber, File); + I.Access = clang::AccessSpecifier::AS_none; + return serialize(I); } -std::unique_ptr emitInfo(const CXXMethodDecl *D, const FullComment *FC, - int LineNumber, llvm::StringRef File, - bool PublicOnly) { +std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, bool PublicOnly) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) - return nullptr; - FunctionInfo Func; - populateFunctionInfo(Func, D, FC, LineNumber, File); - Func.IsMethod = true; - - SymbolID ParentUSR = getUSRForDecl(D->getParent()); - Func.Parent = Reference{ParentUSR, D->getParent()->getNameAsString(), - InfoType::IT_record}; - Func.Access = D->getAccess(); - - // Wrap in enclosing scope - auto I = llvm::make_unique(); - I->USR = ParentUSR; - I->ChildFunctions.push_back(std::move(Func)); - return I; + return ""; + FunctionInfo I; + populateFunctionInfo(I, D, FC, LineNumber, File); + I.IsMethod = true; + I.Parent = Reference{getUSRForDecl(D->getParent()), + D->getParent()->getNameAsString(), InfoType::IT_record}; + I.Access = D->getAccess(); + return serialize(I); } -std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, - int LineNumber, llvm::StringRef File, - bool PublicOnly) { +std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, + llvm::StringRef File, bool PublicOnly) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) - return nullptr; - EnumInfo Enum; - populateSymbolInfo(Enum, D, FC, LineNumber, File); - Enum.Scoped = D->isScoped(); - parseEnumerators(Enum, D); - - // Wrap in enclosing scope - if (!Enum.Namespace.empty()) { - switch (Enum.Namespace[0].RefType) { - case InfoType::IT_namespace: { - std::unique_ptr IPtr = llvm::make_unique(); - NamespaceInfo *I = static_cast(IPtr.get()); - I->USR = Enum.Namespace[0].USR; - I->ChildEnums.push_back(std::move(Enum)); - return IPtr; - } - case InfoType::IT_record: { - std::unique_ptr IPtr = llvm::make_unique(); - RecordInfo *I = static_cast(IPtr.get()); - I->USR = Enum.Namespace[0].USR; - I->ChildEnums.push_back(std::move(Enum)); - return IPtr; - } - default: - break; - } - } - - // Put in global namespace - auto I = llvm::make_unique(); - I->USR = SymbolID(); - I->ChildEnums.push_back(std::move(Enum)); - return I; + return ""; + EnumInfo I; + populateSymbolInfo(I, D, FC, LineNumber, File); + I.Scoped = D->isScoped(); + parseEnumerators(I, D); + return serialize(I); } } // namespace serialize diff --git a/clang-doc/Serialize.h b/clang-doc/Serialize.h index d89dac809..5181cf61b 100644 --- a/clang-doc/Serialize.h +++ b/clang-doc/Serialize.h @@ -28,16 +28,16 @@ namespace clang { namespace doc { namespace serialize { -std::unique_ptr emitInfo(const NamespaceDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); -std::unique_ptr emitInfo(const RecordDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); -std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); -std::unique_ptr emitInfo(const FunctionDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); -std::unique_ptr emitInfo(const CXXMethodDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); +std::string emitInfo(const NamespaceDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); +std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, + StringRef File, bool PublicOnly); +std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, + StringRef File, bool PublicOnly); +std::string emitInfo(const FunctionDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); +std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); // Function to hash a given USR value for storage. // As USRs (Unified Symbol Resolution) could be large, especially for functions @@ -46,8 +46,6 @@ std::unique_ptr emitInfo(const CXXMethodDecl *D, const FullComment *FC, // memory (vs storing USRs directly). SymbolID hashUSR(llvm::StringRef USR); -std::string serialize(std::unique_ptr &I); - } // namespace serialize } // namespace doc } // namespace clang diff --git a/clang-doc/YAMLGenerator.cpp b/clang-doc/YAMLGenerator.cpp index 58c1e1f36..f29b4787d 100644 --- a/clang-doc/YAMLGenerator.cpp +++ b/clang-doc/YAMLGenerator.cpp @@ -20,8 +20,6 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(MemberTypeInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(Reference) LLVM_YAML_IS_SEQUENCE_VECTOR(Location) LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo) -LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionInfo) -LLVM_YAML_IS_SEQUENCE_VECTOR(EnumInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>) @@ -177,14 +175,7 @@ template <> struct MappingTraits { }; template <> struct MappingTraits { - static void mapping(IO &IO, NamespaceInfo &I) { - InfoMapping(IO, I); - IO.mapOptional("ChildNamespaces", I.ChildNamespaces, - std::vector()); - IO.mapOptional("ChildRecords", I.ChildRecords, std::vector()); - IO.mapOptional("ChildFunctions", I.ChildFunctions); - IO.mapOptional("ChildEnums", I.ChildEnums); - } + static void mapping(IO &IO, NamespaceInfo &I) { InfoMapping(IO, I); } }; template <> struct MappingTraits { @@ -195,9 +186,6 @@ template <> struct MappingTraits { IO.mapOptional("Parents", I.Parents, llvm::SmallVector()); IO.mapOptional("VirtualParents", I.VirtualParents, llvm::SmallVector()); - IO.mapOptional("ChildRecords", I.ChildRecords, std::vector()); - IO.mapOptional("ChildFunctions", I.ChildFunctions); - IO.mapOptional("ChildEnums", I.ChildEnums); } }; diff --git a/clang-doc/gen_tests.py b/clang-doc/gen_tests.py index ccdb069c4..5004892e5 100644 --- a/clang-doc/gen_tests.py +++ b/clang-doc/gen_tests.py @@ -18,17 +18,14 @@ To generate all current tests: - Generate mapper tests: - python gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix mapper + gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -prefix mapper - Generate reducer tests: - python gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix bc - + gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -prefix bc + - Generate yaml tests: - python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix yaml - -- Generate public decl tests: - python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix public - + gen_tests.py -flag='--format=yaml' -flag='--doxygen' -prefix yaml + This script was written on/for Linux, and has not been tested on any other platform and so it may not work. @@ -37,7 +34,6 @@ import argparse import glob import os -import re import shutil import subprocess @@ -52,10 +48,6 @@ CHECK_NEXT = '// CHECK-{0}-NEXT: ' -BITCODE_USR = '' -BITCODE_USR_REGEX = r'' -YAML_USR = "USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}'" -YAML_USR_REGEX = r"USR: '[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]'" def clear_test_prefix_files(prefix, tests_path): if os.path.isdir(tests_path): @@ -116,8 +108,6 @@ def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer): # Format output. output = output.replace('blob data = \'test\'', 'blob data = \'{{.*}}\'') - output = re.sub(YAML_USR_REGEX, YAML_USR, output) - output = re.sub(BITCODE_USR_REGEX, BITCODE_USR, output) output = CHECK.format(checkname) + output.rstrip() output = run_cmd + output.replace('\n', '\n' + CHECK_NEXT.format(checkname)) diff --git a/clang-doc/tool/ClangDocMain.cpp b/clang-doc/tool/ClangDocMain.cpp index 6e4a92d7b..9e89e8568 100644 --- a/clang-doc/tool/ClangDocMain.cpp +++ b/clang-doc/tool/ClangDocMain.cpp @@ -121,24 +121,9 @@ bool DumpResultToFile(const Twine &DirName, const Twine &FileName, return false; } -// A function to extract the appropriate path name for a given info's -// documentation. The path returned is a composite of the parent namespaces as -// directories plus the decl name as the filename. -// -// Example: Given the below, the path for class C will be < -// root>/A/B/C. -// -// namespace A { -// namesapce B { -// -// class C {}; -// -// } -// } llvm::Expected> -getInfoOutputFile(StringRef Root, - llvm::SmallVectorImpl &Namespaces, - StringRef Name, StringRef Ext) { +getPath(StringRef Root, StringRef Ext, StringRef Name, + llvm::SmallVectorImpl &Namespaces) { std::error_code OK; llvm::SmallString<128> Path; llvm::sys::path::native(Root, Path); @@ -149,8 +134,6 @@ getInfoOutputFile(StringRef Root, return llvm::make_error("Unable to create directory.\n", llvm::inconvertibleErrorCode()); - if (Name.empty()) - Name = "GlobalNamespace"; llvm::sys::path::append(Path, Name + Ext); return Path; } @@ -163,30 +146,6 @@ std::string getFormatString(OutputFormatTy Ty) { llvm_unreachable("Unknown OutputFormatTy"); } -// Iterate through tool results and build string map of info vectors from the -// encoded bitstreams. -bool bitcodeResultsToInfos( - tooling::ToolResults &Results, - llvm::StringMap>> &Output) { - bool Err = false; - Results.forEachResult([&](StringRef Key, StringRef Value) { - llvm::BitstreamCursor Stream(Value); - doc::ClangDocBitcodeReader Reader(Stream); - auto Infos = Reader.readBitcode(); - if (!Infos) { - llvm::errs() << toString(Infos.takeError()) << "\n"; - Err = true; - return; - } - for (auto &I : Infos.get()) { - auto R = - Output.try_emplace(Key, std::vector>()); - R.first->second.emplace_back(std::move(I)); - } - }); - return Err; -} - int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); std::error_code OK; @@ -237,21 +196,30 @@ int main(int argc, const char **argv) { } // Collect values into output by key. + llvm::outs() << "Collecting infos...\n"; + llvm::StringMap>> MapOutput; + // In ToolResults, the Key is the hashed USR and the value is the // bitcode-encoded representation of the Info object. - llvm::outs() << "Collecting infos...\n"; - llvm::StringMap>> USRToInfos; - if (bitcodeResultsToInfos(*Exec->get()->getToolResults(), USRToInfos)) - return 1; + Exec->get()->getToolResults()->forEachResult([&](StringRef Key, + StringRef Value) { + llvm::BitstreamCursor Stream(Value); + doc::ClangDocBitcodeReader Reader(Stream); + auto Infos = Reader.readBitcode(); + for (auto &I : Infos) { + auto R = + MapOutput.try_emplace(Key, std::vector>()); + R.first->second.emplace_back(std::move(I)); + } + }); - // First reducing phase (reduce all decls into one info per decl). - llvm::outs() << "Reducing " << USRToInfos.size() << " infos...\n"; - for (auto &Group : USRToInfos) { + // Reducing and generation phases + llvm::outs() << "Reducing " << MapOutput.size() << " infos...\n"; + llvm::StringMap> ReduceOutput; + for (auto &Group : MapOutput) { auto Reduced = doc::mergeInfos(Group.getValue()); - if (!Reduced) { + if (!Reduced) llvm::errs() << llvm::toString(Reduced.takeError()); - continue; - } if (DumpIntermediateResult) { SmallString<4096> Buffer; @@ -262,10 +230,10 @@ int main(int argc, const char **argv) { llvm::errs() << "Error dumping to bitcode.\n"; continue; } - doc::Info *I = Reduced.get().get(); - auto InfoPath = - getInfoOutputFile(OutDirectory, I->Namespace, I->Name, "." + Format); + // Create the relevant ostream and emit the documentation for this decl. + doc::Info *I = Reduced.get().get(); + auto InfoPath = getPath(OutDirectory, "." + Format, I->Name, I->Namespace); if (!InfoPath) { llvm::errs() << toString(InfoPath.takeError()) << "\n"; continue; @@ -273,7 +241,7 @@ int main(int argc, const char **argv) { std::error_code FileErr; llvm::raw_fd_ostream InfoOS(InfoPath.get(), FileErr, llvm::sys::fs::F_None); if (FileErr != OK) { - llvm::errs() << "Error opening info file: " << FileErr.message() << "\n"; + llvm::errs() << "Error opening index file: " << FileErr.message() << "\n"; continue; } diff --git a/test/clang-doc/bc-comment.cpp b/test/clang-doc/bc-comment.cpp index 3b006ab8a..fa3ea7f71 100644 --- a/test/clang-doc/bc-comment.cpp +++ b/test/clang-doc/bc-comment.cpp @@ -27,178 +27,176 @@ void F(int I, int J); /// Bonus comment on definition void F(int I, int J) {} -// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-intermediate --doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/7574630614A535710E5A6ABCFFF98BCA2D06A4CA.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'F' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'FullComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'F' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'FullComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'BlockCommandComment' +// CHECK-0-NEXT: blob data = 'brief' +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' Brief description.' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'BlockCommandComment' -// CHECK-0-NEXT: blob data = 'brief' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Brief description.' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' Extended description that' // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Extended description that' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' continues onto the next line.' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' continues onto the next line.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'HTMLStartTagComment' +// CHECK-0-NEXT: blob data = 'ul' +// CHECK-0-NEXT: blob data = 'class' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'HTMLStartTagComment' +// CHECK-0-NEXT: blob data = 'li' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' Testing.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'HTMLEndTagComment' +// CHECK-0-NEXT: blob data = 'ul' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'VerbatimBlockComment' +// CHECK-0-NEXT: blob data = 'verbatim' +// CHECK-0-NEXT: blob data = 'endverbatim' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'VerbatimBlockLineComment' +// CHECK-0-NEXT: blob data = ' The description continues.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' --' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParamCommandComment' +// CHECK-0-NEXT: blob data = '[out]' +// CHECK-0-NEXT: blob data = 'I' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'HTMLStartTagComment' -// CHECK-0-NEXT: blob data = 'ul' -// CHECK-0-NEXT: blob data = 'class' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'HTMLStartTagComment' -// CHECK-0-NEXT: blob data = 'li' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Testing.' +// CHECK-0-NEXT: blob data = ' is a parameter.' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'HTMLEndTagComment' -// CHECK-0-NEXT: blob data = 'ul' -// CHECK-0-NEXT: -// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'VerbatimBlockComment' -// CHECK-0-NEXT: blob data = 'verbatim' -// CHECK-0-NEXT: blob data = 'endverbatim' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'VerbatimBlockLineComment' -// CHECK-0-NEXT: blob data = ' The description continues.' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParamCommandComment' +// CHECK-0-NEXT: blob data = '[in]' +// CHECK-0-NEXT: blob data = 'J' +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' --' +// CHECK-0-NEXT: blob data = ' is a parameter.' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParamCommandComment' -// CHECK-0-NEXT: blob data = '[out]' -// CHECK-0-NEXT: blob data = 'I' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' is a parameter.' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParamCommandComment' -// CHECK-0-NEXT: blob data = '[in]' -// CHECK-0-NEXT: blob data = 'J' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' is a parameter.' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'BlockCommandComment' -// CHECK-0-NEXT: blob data = 'return' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'FullComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'BlockCommandComment' +// CHECK-0-NEXT: blob data = 'return' +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Bonus comment on definition' +// CHECK-0-NEXT: blob data = ' void' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'I' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'J' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'FullComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' Bonus comment on definition' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'I' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'J' +// CHECK-0-NEXT: +// CHECK-0-NEXT: diff --git a/test/clang-doc/bc-linkage.cpp b/test/clang-doc/bc-linkage.cpp deleted file mode 100644 index 8fec0d351..000000000 --- a/test/clang-doc/bc-linkage.cpp +++ /dev/null @@ -1,844 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// REQUIRES: system-linux -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -void function(int x); - -inline int inlinedFunction(int x); - -int functionWithInnerClass(int x) { - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -inline int inlinedFunctionWithInnerClass(int x) { - class InnerClass { //VisibleNoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -class Class { -public: - void publicMethod(); - int publicField; - -protected: - void protectedMethod(); - int protectedField; - -private: - void privateMethod(); - int privateField; -}; - -namespace named { -class NamedClass { -public: - void namedPublicMethod(); - int namedPublicField; - -protected: - void namedProtectedMethod(); - int namedProtectedField; - -private: - void namedPrivateMethod(); - int namedPrivateField; -}; - -void namedFunction(); -static void namedStaticFunction(); -inline void namedInlineFunction(); -} // namespace named - -static void staticFunction(int x); //Internal Linkage - -static int staticFunctionWithInnerClass(int x) { - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -namespace { -class AnonClass { -public: - void anonPublicMethod(); - int anonPublicField; - -protected: - void anonProtectedMethod(); - int anonProtectedField; - -private: - void anonPrivateMethod(); - int anonPrivateField; -}; - -void anonFunction(); -static void anonStaticFunction(); -inline void anonInlineFunction(); -} // namespace - -// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/C9B3B71ACDD84C5BB320D34E97677715CDB3EA32.bc | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'InnerClass' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'inlinedFunctionWithInnerClass' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'innerPublicMethod' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'InnerClass' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'inlinedFunctionWithInnerClass' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'InnerClass' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/8960B5C9247D6F5C532756E53A1AD1240FA2146F.bc | FileCheck %s --check-prefix CHECK-1 -// CHECK-1: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'named' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'namedFunction' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'named' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'void' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'namedStaticFunction' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'named' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'void' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'namedInlineFunction' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'named' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'void' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/74A031CBE68C101F3E83F60ED17F20C11EC19D48.bc | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'InnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'innerPublicMethod' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'InnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'InnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'int' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/7CDD73DCD6CD72F7E5CE25502810A182C66C4B45.bc | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'int' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'publicField' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'int' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'protectedField' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'int' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'privateField' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'publicMethod' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'void' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'protectedMethod' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'void' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'privateMethod' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'void' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/85427901413EC77C961019EBB3ADEF7B0BAAFE78.bc | FileCheck %s --check-prefix CHECK-4 -// CHECK-4: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'InnerClass' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'functionWithInnerClass' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'innerPublicMethod' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'InnerClass' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'functionWithInnerClass' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'InnerClass' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'int' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 -// CHECK-5: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'function' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'void' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'inlinedFunction' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'functionWithInnerClass' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'inlinedFunctionWithInnerClass' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'staticFunction' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'void' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'staticFunctionWithInnerClass' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/4712C5FA37B298A25501D1033C619B65B0ECC449.bc | FileCheck %s --check-prefix CHECK-6 -// CHECK-6: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'named' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'int' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedPublicField' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'int' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedProtectedField' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'int' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedPrivateField' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedPublicMethod' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'named' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'void' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedProtectedMethod' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'named' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'void' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedPrivateMethod' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'named' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'void' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/6E8FB72A89761E77020BFCEE9A9A6E64B15CC2A9.bc | FileCheck %s --check-prefix CHECK-7 -// CHECK-7: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'int' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonPublicField' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'int' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonProtectedField' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'int' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonPrivateField' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonPublicMethod' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'void' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonProtectedMethod' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'void' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonPrivateMethod' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'void' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/83CC52D32583E0771710A7742DE81C839E953AC8.bc | FileCheck %s --check-prefix CHECK-8 -// CHECK-8: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'anonFunction' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'void' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'anonStaticFunction' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'void' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'anonInlineFunction' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'void' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: diff --git a/test/clang-doc/bc-module.cpp b/test/clang-doc/bc-module.cpp deleted file mode 100644 index 101d8da85..000000000 --- a/test/clang-doc/bc-module.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -export module M; - -int moduleFunction(int x); // ModuleLinkage - -static int staticModuleFunction(int x); // ModuleInternalLinkage - -export double exportedModuleFunction(double y, int z); // ExternalLinkage - -// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'moduleFunction' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'x' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'staticModuleFunction' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'x' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'exportedModuleFunction' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'double' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'double' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'y' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'z' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: diff --git a/test/clang-doc/bc-namespace.cpp b/test/clang-doc/bc-namespace.cpp index 79b35bd9a..b1c03636c 100644 --- a/test/clang-doc/bc-namespace.cpp +++ b/test/clang-doc/bc-namespace.cpp @@ -25,97 +25,115 @@ E func(int i) { return X; } } // namespace B } // namespace A -// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-intermediate --doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'A' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'f' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'A' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'B' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'A' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = 'X' +// CHECK-0-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-1 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'B' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'f' // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'A' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'func' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'B' -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'void' +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'A' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'enum A::B::E' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'int' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'i' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'E' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'B' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'A' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: blob data = 'X' -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/9A82CB33ED0FDF81EE383D31CD0957D153C5E840.bc | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'func' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'B' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'A' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'enum A::B::E' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'i' +// CHECK-2-NEXT: +// CHECK-2-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'A' +// CHECK-3-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'B' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'A' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: diff --git a/test/clang-doc/bc-record.cpp b/test/clang-doc/bc-record.cpp index a0e224485..7a09118c7 100644 --- a/test/clang-doc/bc-record.cpp +++ b/test/clang-doc/bc-record.cpp @@ -39,7 +39,7 @@ class X { class Y {}; }; -// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-intermediate --doxygen -p %t %t/test.cpp -output=%t/docs // RUN: llvm-bcanalyzer --dump %t/docs/bc/289584A8E0FF4178A794622A547AA622503967A1.bc | FileCheck %s --check-prefix CHECK-0 @@ -47,84 +47,11 @@ class X { // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' // CHECK-0-NEXT: blob data = '{{.*}}' // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '~E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ProtectedMethod' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: // CHECK-0-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/3FB542274573CAEAD54CEBFFCAEE3D77FB9713D8.bc | FileCheck %s --check-prefix CHECK-1 @@ -133,10 +60,10 @@ class X { // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'I' // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'H' // CHECK-1-NEXT: // CHECK-1-NEXT: @@ -145,149 +72,250 @@ class X { // CHECK-1-NEXT: // CHECK-1-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-2 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/5093D428CDC62096A67547BA52566E4FB9404EEE.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'X' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'ProtectedMethod' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'E' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'E' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'void' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-3 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'C' -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'int' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'i' -// CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'X' +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: // CHECK-3-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-4 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'Y' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'X' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'H' +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'void' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'H' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'void' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'B' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: blob data = 'X' -// CHECK-5-NEXT: blob data = 'Y' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'Bc' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'A' -// CHECK-5-NEXT: blob data = 'B' -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'C' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'i' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-6 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17.bc | FileCheck %s --check-prefix CHECK-6 // CHECK-6: // CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'D' -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '~E' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'E' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'E' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'void' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-7 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4.bc | FileCheck %s --check-prefix CHECK-7 // CHECK-7: // CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'F' -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'E' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: blob data = 'E' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = '{{.*}}' // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'D' +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'E' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'void' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-8 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-8 // CHECK-8: // CHECK-8-NEXT: // CHECK-8-NEXT: // CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'A' -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'int' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'X' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'int' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'Y' -// CHECK-8-NEXT: -// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'Y' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'X' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: // CHECK-8-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/FC07BD34D5E77782C263FA944447929EA8753740.bc | FileCheck %s --check-prefix CHECK-9 +// CHECK-9: +// CHECK-9-NEXT: +// CHECK-9-NEXT: +// CHECK-9-NEXT: +// CHECK-9-NEXT: +// CHECK-9-NEXT: +// CHECK-9-NEXT: blob data = 'B' +// CHECK-9-NEXT: blob data = '{{.*}}' +// CHECK-9-NEXT: blob data = 'X' +// CHECK-9-NEXT: blob data = 'Y' +// CHECK-9-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-10 +// CHECK-10: +// CHECK-10-NEXT: +// CHECK-10-NEXT: +// CHECK-10-NEXT: +// CHECK-10-NEXT: +// CHECK-10-NEXT: +// CHECK-10-NEXT: blob data = 'D' +// CHECK-10-NEXT: blob data = '{{.*}}' +// CHECK-10-NEXT: +// CHECK-10-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-11 +// CHECK-11: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: blob data = 'F' +// CHECK-11-NEXT: blob data = '{{.*}}' +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: blob data = 'E' +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: blob data = 'D' +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-12 +// CHECK-12: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: blob data = 'A' +// CHECK-12-NEXT: blob data = '{{.*}}' +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: blob data = 'int' +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: blob data = 'X' +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: blob data = 'int' +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: blob data = 'Y' +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/1E3438A08BA22025C0B46289FF0686F92C8924C5.bc | FileCheck %s --check-prefix CHECK-13 +// CHECK-13: +// CHECK-13-NEXT: +// CHECK-13-NEXT: +// CHECK-13-NEXT: +// CHECK-13-NEXT: +// CHECK-13-NEXT: +// CHECK-13-NEXT: blob data = 'Bc' +// CHECK-13-NEXT: blob data = '{{.*}}' +// CHECK-13-NEXT: +// CHECK-13-NEXT: blob data = 'A' +// CHECK-13-NEXT: blob data = 'B' +// CHECK-13-NEXT: diff --git a/test/clang-doc/mapper-comment.cpp b/test/clang-doc/mapper-comment.cpp index efd3dc54c..da691b156 100644 --- a/test/clang-doc/mapper-comment.cpp +++ b/test/clang-doc/mapper-comment.cpp @@ -27,48 +27,46 @@ void F(int I, int J); /// Bonus comment on definition void F(int I, int J) {} -// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-mapper --doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/7574630614A535710E5A6ABCFFF98BCA2D06A4CA.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'F' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'FullComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Bonus comment on definition' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'F' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'FullComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' Bonus comment on definition' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'I' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'J' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'I' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'J' +// CHECK-0-NEXT: +// CHECK-0-NEXT: diff --git a/test/clang-doc/mapper-linkage.cpp b/test/clang-doc/mapper-linkage.cpp deleted file mode 100644 index 5b4fe7df3..000000000 --- a/test/clang-doc/mapper-linkage.cpp +++ /dev/null @@ -1,402 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// REQUIRES: system-linux -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -void function(int x); - -inline int inlinedFunction(int x); - -int functionWithInnerClass(int x) { - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -inline int inlinedFunctionWithInnerClass(int x) { - class InnerClass { //VisibleNoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -class Class { -public: - void publicMethod(); - int publicField; - -protected: - void protectedMethod(); - int protectedField; - -private: - void privateMethod(); - int privateField; -}; - -namespace named { -class NamedClass { -public: - void namedPublicMethod(); - int namedPublicField; - -protected: - void namedProtectedMethod(); - int namedProtectedField; - -private: - void namedPrivateMethod(); - int namedPrivateField; -}; - -void namedFunction(); -static void namedStaticFunction(); -inline void namedInlineFunction(); -} // namespace named - -static void staticFunction(int x); //Internal Linkage - -static int staticFunctionWithInnerClass(int x) { - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -namespace { -class AnonClass { -public: - void anonPublicMethod(); - int anonPublicField; - -protected: - void anonProtectedMethod(); - int anonProtectedField; - -private: - void anonPrivateMethod(); - int anonPrivateField; -}; - -void anonFunction(); -static void anonStaticFunction(); -inline void anonInlineFunction(); -} // namespace - -// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/C9B3B71ACDD84C5BB320D34E97677715CDB3EA32.bc | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'innerPublicMethod' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'InnerClass' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'inlinedFunctionWithInnerClass' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'InnerClass' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/8960B5C9247D6F5C532756E53A1AD1240FA2146F.bc | FileCheck %s --check-prefix CHECK-1 -// CHECK-1: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'namedInlineFunction' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'named' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'void' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/74A031CBE68C101F3E83F60ED17F20C11EC19D48.bc | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'innerPublicMethod' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'InnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'InnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'int' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/7CDD73DCD6CD72F7E5CE25502810A182C66C4B45.bc | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'privateMethod' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'void' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/85427901413EC77C961019EBB3ADEF7B0BAAFE78.bc | FileCheck %s --check-prefix CHECK-4 -// CHECK-4: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'innerPublicMethod' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'InnerClass' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'functionWithInnerClass' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'InnerClass' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'int' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 -// CHECK-5: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'staticFunctionWithInnerClass' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/4712C5FA37B298A25501D1033C619B65B0ECC449.bc | FileCheck %s --check-prefix CHECK-6 -// CHECK-6: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedPrivateMethod' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'named' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'void' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/6E8FB72A89761E77020BFCEE9A9A6E64B15CC2A9.bc | FileCheck %s --check-prefix CHECK-7 -// CHECK-7: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonPrivateMethod' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'void' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/83CC52D32583E0771710A7742DE81C839E953AC8.bc | FileCheck %s --check-prefix CHECK-8 -// CHECK-8: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'anonInlineFunction' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'void' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: diff --git a/test/clang-doc/mapper-module.cpp b/test/clang-doc/mapper-module.cpp deleted file mode 100644 index 04a34c68d..000000000 --- a/test/clang-doc/mapper-module.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -export module M; - -int moduleFunction(int x); // ModuleLinkage - -static int staticModuleFunction(int x); // ModuleInternalLinkage - -export double exportedModuleFunction(double y, int z); // ExternalLinkage - -// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'exportedModuleFunction' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'double' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'double' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'y' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'z' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: diff --git a/test/clang-doc/mapper-namespace.cpp b/test/clang-doc/mapper-namespace.cpp index d00082331..aeda90816 100644 --- a/test/clang-doc/mapper-namespace.cpp +++ b/test/clang-doc/mapper-namespace.cpp @@ -25,70 +25,114 @@ E func(int i) { return X; } } // namespace B } // namespace A -// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-mapper --doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'f' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'A' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'B' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'A' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = 'X' +// CHECK-0-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-1 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'func' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'B' -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'f' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'A' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'void' +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'A' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'enum A::B::E' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'int' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'i' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/9A82CB33ED0FDF81EE383D31CD0957D153C5E840.bc | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'func' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'B' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'A' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'enum A::B::E' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'i' +// CHECK-2-NEXT: +// CHECK-2-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'A' +// CHECK-3-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'B' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'A' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: diff --git a/test/clang-doc/mapper-record.cpp b/test/clang-doc/mapper-record.cpp index dbabd8fdd..82a5e2f01 100644 --- a/test/clang-doc/mapper-record.cpp +++ b/test/clang-doc/mapper-record.cpp @@ -39,7 +39,7 @@ class X { class Y {}; }; -// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-mapper --doxygen -p %t %t/test.cpp -output=%t/docs // RUN: llvm-bcanalyzer --dump %t/docs/bc/289584A8E0FF4178A794622A547AA622503967A1.bc | FileCheck %s --check-prefix CHECK-0 @@ -47,32 +47,11 @@ class X { // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ProtectedMethod' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: // CHECK-0-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/3FB542274573CAEAD54CEBFFCAEE3D77FB9713D8.bc | FileCheck %s --check-prefix CHECK-1 @@ -81,10 +60,10 @@ class X { // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'I' // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'H' // CHECK-1-NEXT: // CHECK-1-NEXT: @@ -93,128 +72,246 @@ class X { // CHECK-1-NEXT: // CHECK-1-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-2 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/5093D428CDC62096A67547BA52566E4FB9404EEE.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'X' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-3 +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'ProtectedMethod' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'E' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'E' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'void' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'C' -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'int' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'i' -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'X' +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: // CHECK-3-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-4 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'Y' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'X' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'H' +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'void' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'Bc' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'A' -// CHECK-5-NEXT: blob data = 'B' -// CHECK-5-NEXT: -// CHECK-5-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-6 +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'C' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'i' +// CHECK-5-NEXT: +// CHECK-5-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17.bc | FileCheck %s --check-prefix CHECK-6 // CHECK-6: // CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'D' -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-7 +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '~E' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'E' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'E' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'void' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4.bc | FileCheck %s --check-prefix CHECK-7 // CHECK-7: // CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'F' -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'E' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: blob data = 'E' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = '{{.*}}' // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'D' +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'E' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'void' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-8 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-8 // CHECK-8: // CHECK-8-NEXT: // CHECK-8-NEXT: // CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'A' -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'int' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'X' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'int' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'Y' -// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'Y' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'X' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: // CHECK-8-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/FC07BD34D5E77782C263FA944447929EA8753740.bc | FileCheck %s --check-prefix CHECK-9 +// CHECK-9: +// CHECK-9-NEXT: +// CHECK-9-NEXT: +// CHECK-9-NEXT: +// CHECK-9-NEXT: +// CHECK-9-NEXT: +// CHECK-9-NEXT: blob data = 'B' +// CHECK-9-NEXT: blob data = '{{.*}}' +// CHECK-9-NEXT: blob data = 'X' +// CHECK-9-NEXT: blob data = 'Y' +// CHECK-9-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-10 +// CHECK-10: +// CHECK-10-NEXT: +// CHECK-10-NEXT: +// CHECK-10-NEXT: +// CHECK-10-NEXT: +// CHECK-10-NEXT: +// CHECK-10-NEXT: blob data = 'D' +// CHECK-10-NEXT: blob data = '{{.*}}' +// CHECK-10-NEXT: +// CHECK-10-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-11 +// CHECK-11: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: blob data = 'F' +// CHECK-11-NEXT: blob data = '{{.*}}' +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: blob data = 'E' +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: blob data = 'D' +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: +// CHECK-11-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-12 +// CHECK-12: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: blob data = 'A' +// CHECK-12-NEXT: blob data = '{{.*}}' +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: blob data = 'int' +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: blob data = 'X' +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: blob data = 'int' +// CHECK-12-NEXT: +// CHECK-12-NEXT: +// CHECK-12-NEXT: blob data = 'Y' +// CHECK-12-NEXT: +// CHECK-12-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/1E3438A08BA22025C0B46289FF0686F92C8924C5.bc | FileCheck %s --check-prefix CHECK-13 +// CHECK-13: +// CHECK-13-NEXT: +// CHECK-13-NEXT: +// CHECK-13-NEXT: +// CHECK-13-NEXT: +// CHECK-13-NEXT: +// CHECK-13-NEXT: blob data = 'Bc' +// CHECK-13-NEXT: blob data = '{{.*}}' +// CHECK-13-NEXT: +// CHECK-13-NEXT: blob data = 'A' +// CHECK-13-NEXT: blob data = 'B' +// CHECK-13-NEXT: diff --git a/test/clang-doc/module.cpp b/test/clang-doc/module.cpp new file mode 100644 index 000000000..a2b594597 --- /dev/null +++ b/test/clang-doc/module.cpp @@ -0,0 +1,61 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" +// RUN: clang-doc --extra-arg=-fmodules-ts --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: cat %t/docs/moduleFunction.yaml | FileCheck %s --check-prefix=CHECK-A +// RUN: cat %t/docs/staticModuleFunction.yaml | FileCheck %s --check-prefix=CHECK-B +// RUN: cat %t/docs/exportedModuleFunction.yaml | FileCheck %s --check-prefix=CHECK-C + +export module M; + +int moduleFunction(int x); //ModuleLinkage +// CHECK-A: --- +// CHECK-A-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-A-NEXT: Name: 'moduleFunction' +// CHECK-A-NEXT: Location: +// CHECK-A-NEXT: - LineNumber: 12 +// CHECK-A-NEXT: Filename: {{.*}} +// CHECK-A-NEXT: Params: +// CHECK-A-NEXT: - Type: +// CHECK-A-NEXT: Name: 'int' +// CHECK-A-NEXT: Name: 'x' +// CHECK-A-NEXT: ReturnType: +// CHECK-A-NEXT: Type: +// CHECK-A-NEXT: Name: 'int' +// CHECK-A-NEXT: ... + +static int staticModuleFunction(int x); //ModuleInternalLinkage +// CHECK-B: --- +// CHECK-B-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-B-NEXT: Name: 'staticModuleFunction' +// CHECK-B-NEXT: Location: +// CHECK-B-NEXT: - LineNumber: 28 +// CHECK-B-NEXT: Filename: {{.*}} +// CHECK-B-NEXT: Params: +// CHECK-B-NEXT: - Type: +// CHECK-B-NEXT: Name: 'int' +// CHECK-B-NEXT: Name: 'x' +// CHECK-B-NEXT: ReturnType: +// CHECK-B-NEXT: Type: +// CHECK-B-NEXT: Name: 'int' +// CHECK-B-NEXT: ... + +export double exportedModuleFunction(double y, int z); //ExternalLinkage +// CHECK-C: --- +// CHECK-C-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-C-NEXT: Name: 'exportedModuleFunction' +// CHECK-C-NEXT: Location: +// CHECK-C-NEXT: - LineNumber: 44 +// CHECK-C-NEXT: Filename: {{.*}} +// CHECK-C-NEXT: Params: +// CHECK-C-NEXT: - Type: +// CHECK-C-NEXT: Name: 'double' +// CHECK-C-NEXT: Name: 'y' +// CHECK-C-NEXT: - Type: +// CHECK-C-NEXT: Name: 'int' +// CHECK-C-NEXT: Name: 'z' +// CHECK-C-NEXT: ReturnType: +// CHECK-C-NEXT: Type: +// CHECK-C-NEXT: Name: 'double' +// CHECK-C-NEXT: ... diff --git a/test/clang-doc/public-comment.cpp b/test/clang-doc/public-comment.cpp deleted file mode 100644 index 6c5545e8e..000000000 --- a/test/clang-doc/public-comment.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -/// \brief Brief description. -/// -/// Extended description that -/// continues onto the next line. -/// -///
    -///
  • Testing. -///
-/// -/// \verbatim -/// The description continues. -/// \endverbatim -/// -- -/// \param [out] I is a parameter. -/// \param J is a parameter. -/// \return void -void F(int I, int J); - -/// Bonus comment on definition -void F(int I, int J) {} - -// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: --- -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: ChildFunctions: -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'F' -// CHECK-0-NEXT: Description: -// CHECK-0-NEXT: - Kind: 'FullComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'BlockCommandComment' -// CHECK-0-NEXT: Name: 'brief' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Brief description.' -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Extended description that' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' continues onto the next line.' -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' -// CHECK-0-NEXT: Name: 'ul' -// CHECK-0-NEXT: AttrKeys: -// CHECK-0-NEXT: - 'class' -// CHECK-0-NEXT: AttrValues: -// CHECK-0-NEXT: - 'test' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' -// CHECK-0-NEXT: Name: 'li' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Testing.' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'HTMLEndTagComment' -// CHECK-0-NEXT: Name: 'ul' -// CHECK-0-NEXT: SelfClosing: true -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'VerbatimBlockComment' -// CHECK-0-NEXT: Name: 'verbatim' -// CHECK-0-NEXT: CloseName: 'endverbatim' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'VerbatimBlockLineComment' -// CHECK-0-NEXT: Text: ' The description continues.' -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' --' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'ParamCommandComment' -// CHECK-0-NEXT: Direction: '[out]' -// CHECK-0-NEXT: ParamName: 'I' -// CHECK-0-NEXT: Explicit: true -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' is a parameter.' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'ParamCommandComment' -// CHECK-0-NEXT: Direction: '[in]' -// CHECK-0-NEXT: ParamName: 'J' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' is a parameter.' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'BlockCommandComment' -// CHECK-0-NEXT: Name: 'return' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' void' -// CHECK-0-NEXT: - Kind: 'FullComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Bonus comment on definition' -// CHECK-0-NEXT: DefLocation: -// CHECK-0-NEXT: LineNumber: 28 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 25 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Params: -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'I' -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'J' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'void' -// CHECK-0-NEXT: ... diff --git a/test/clang-doc/public-linkage.cpp b/test/clang-doc/public-linkage.cpp deleted file mode 100644 index c33e08ce6..000000000 --- a/test/clang-doc/public-linkage.cpp +++ /dev/null @@ -1,299 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// REQUIRES: system-linux -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -void function(int x); - -inline int inlinedFunction(int x); - -int functionWithInnerClass(int x) { - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -inline int inlinedFunctionWithInnerClass(int x) { - class InnerClass { //VisibleNoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -class Class { -public: - void publicMethod(); - int publicField; - -protected: - void protectedMethod(); - int protectedField; - -private: - void privateMethod(); - int privateField; -}; - -namespace named { -class NamedClass { -public: - void namedPublicMethod(); - int namedPublicField; - -protected: - void namedProtectedMethod(); - int namedProtectedField; - -private: - void namedPrivateMethod(); - int namedPrivateField; -}; - -void namedFunction(); -static void namedStaticFunction(); -inline void namedInlineFunction(); -} // namespace named - -static void staticFunction(int x); //Internal Linkage - -static int staticFunctionWithInnerClass(int x) { - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -namespace { -class AnonClass { -public: - void anonPublicMethod(); - int anonPublicField; - -protected: - void anonProtectedMethod(); - int anonProtectedField; - -private: - void anonPrivateMethod(); - int anonPrivateField; -}; - -void anonFunction(); -static void anonStaticFunction(); -inline void anonInlineFunction(); -} // namespace - -// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: cat %t/docs/./Class.yaml | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: --- -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'Class' -// CHECK-0-NEXT: DefLocation: -// CHECK-0-NEXT: LineNumber: 32 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: TagType: Class -// CHECK-0-NEXT: Members: -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'publicField' -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'protectedField' -// CHECK-0-NEXT: Access: Protected -// CHECK-0-NEXT: ChildFunctions: -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'publicMethod' -// CHECK-0-NEXT: Namespace: -// CHECK-0-NEXT: - Type: Record -// CHECK-0-NEXT: Name: 'Class' -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 34 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: IsMethod: true -// CHECK-0-NEXT: Parent: -// CHECK-0-NEXT: Type: Record -// CHECK-0-NEXT: Name: 'Class' -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'void' -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'protectedMethod' -// CHECK-0-NEXT: Namespace: -// CHECK-0-NEXT: - Type: Record -// CHECK-0-NEXT: Name: 'Class' -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 38 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: IsMethod: true -// CHECK-0-NEXT: Parent: -// CHECK-0-NEXT: Type: Record -// CHECK-0-NEXT: Name: 'Class' -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'void' -// CHECK-0-NEXT: ... - -// RUN: cat %t/docs/./named.yaml | FileCheck %s --check-prefix CHECK-1 -// CHECK-1: --- -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'named' -// CHECK-1-NEXT: ChildFunctions: -// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'namedFunction' -// CHECK-1-NEXT: Namespace: -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'named' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Location: -// CHECK-1-NEXT: - LineNumber: 61 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: ReturnType: -// CHECK-1-NEXT: Type: -// CHECK-1-NEXT: Name: 'void' -// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'namedInlineFunction' -// CHECK-1-NEXT: Namespace: -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'named' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Location: -// CHECK-1-NEXT: - LineNumber: 63 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: ReturnType: -// CHECK-1-NEXT: Type: -// CHECK-1-NEXT: Name: 'void' -// CHECK-1-NEXT: ... - -// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: --- -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: ChildFunctions: -// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: Name: 'function' -// CHECK-2-NEXT: Location: -// CHECK-2-NEXT: - LineNumber: 10 -// CHECK-2-NEXT: Filename: 'test' -// CHECK-2-NEXT: Params: -// CHECK-2-NEXT: - Type: -// CHECK-2-NEXT: Name: 'int' -// CHECK-2-NEXT: Name: 'x' -// CHECK-2-NEXT: ReturnType: -// CHECK-2-NEXT: Type: -// CHECK-2-NEXT: Name: 'void' -// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: Name: 'inlinedFunction' -// CHECK-2-NEXT: Location: -// CHECK-2-NEXT: - LineNumber: 12 -// CHECK-2-NEXT: Filename: 'test' -// CHECK-2-NEXT: Params: -// CHECK-2-NEXT: - Type: -// CHECK-2-NEXT: Name: 'int' -// CHECK-2-NEXT: Name: 'x' -// CHECK-2-NEXT: ReturnType: -// CHECK-2-NEXT: Type: -// CHECK-2-NEXT: Name: 'int' -// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: Name: 'functionWithInnerClass' -// CHECK-2-NEXT: DefLocation: -// CHECK-2-NEXT: LineNumber: 14 -// CHECK-2-NEXT: Filename: 'test' -// CHECK-2-NEXT: Params: -// CHECK-2-NEXT: - Type: -// CHECK-2-NEXT: Name: 'int' -// CHECK-2-NEXT: Name: 'x' -// CHECK-2-NEXT: ReturnType: -// CHECK-2-NEXT: Type: -// CHECK-2-NEXT: Name: 'int' -// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: Name: 'inlinedFunctionWithInnerClass' -// CHECK-2-NEXT: DefLocation: -// CHECK-2-NEXT: LineNumber: 23 -// CHECK-2-NEXT: Filename: 'test' -// CHECK-2-NEXT: Params: -// CHECK-2-NEXT: - Type: -// CHECK-2-NEXT: Name: 'int' -// CHECK-2-NEXT: Name: 'x' -// CHECK-2-NEXT: ReturnType: -// CHECK-2-NEXT: Type: -// CHECK-2-NEXT: Name: 'int' -// CHECK-2-NEXT: ... - -// RUN: cat %t/docs/named/NamedClass.yaml | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: --- -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'NamedClass' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Namespace -// CHECK-3-NEXT: Name: 'named' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 47 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: TagType: Class -// CHECK-3-NEXT: Members: -// CHECK-3-NEXT: - Type: -// CHECK-3-NEXT: Name: 'int' -// CHECK-3-NEXT: Name: 'namedPublicField' -// CHECK-3-NEXT: - Type: -// CHECK-3-NEXT: Name: 'int' -// CHECK-3-NEXT: Name: 'namedProtectedField' -// CHECK-3-NEXT: Access: Protected -// CHECK-3-NEXT: ChildFunctions: -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'namedPublicMethod' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'NamedClass' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: - Type: Namespace -// CHECK-3-NEXT: Name: 'named' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Location: -// CHECK-3-NEXT: - LineNumber: 49 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'NamedClass' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'namedProtectedMethod' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'NamedClass' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: - Type: Namespace -// CHECK-3-NEXT: Name: 'named' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Location: -// CHECK-3-NEXT: - LineNumber: 53 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'NamedClass' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: ... diff --git a/test/clang-doc/public-module.cpp b/test/clang-doc/public-module.cpp index 0c93d6884..c7ebadda7 100644 --- a/test/clang-doc/public-module.cpp +++ b/test/clang-doc/public-module.cpp @@ -1,51 +1,53 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// +// This test requires linux because it uses `diff` and compares filepaths +// REQUIRES: system-linux // RUN: rm -rf %t // RUN: mkdir %t // RUN: echo "" > %t/compile_flags.txt // RUN: cp "%s" "%t/test.cpp" +// RUN: clang-doc --public --extra-arg=-fmodules-ts --doxygen -p %t %t/test.cpp -output=%t/docs-with-public-flag +// RUN: clang-doc --extra-arg=-fmodules-ts --doxygen -p %t %t/test.cpp -output=%t/docs-without +// RUN: cat %t/docs-with-public-flag/moduleFunction.yaml | FileCheck %s --check-prefix=CHECK-A +// RUN: cat %t/docs-with-public-flag/exportedModuleFunction.yaml | FileCheck %s --check-prefix=CHECK-B +// RUN: (diff -qry %t/docs-with-public-flag %t/docs-without | sed 's:.*/::' > %t/public.diff) || true +// RUN: cat %t/public.diff | FileCheck %s --check-prefix=CHECK-C export module M; -int moduleFunction(int x); // ModuleLinkage +int moduleFunction(int x); //ModuleLinkage +// CHECK-A: --- +// CHECK-A-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-A-NEXT: Name: 'moduleFunction' +// CHECK-A-NEXT: Location: +// CHECK-A-NEXT: - LineNumber: 16 +// CHECK-A-NEXT: Filename: {{.*}} +// CHECK-A-NEXT: Params: +// CHECK-A-NEXT: - Type: +// CHECK-A-NEXT: Name: 'int' +// CHECK-A-NEXT: Name: 'x' +// CHECK-A-NEXT: ReturnType: +// CHECK-A-NEXT: Type: +// CHECK-A-NEXT: Name: 'int' +// CHECK-A-NEXT: ... -static int staticModuleFunction(int x); // ModuleInternalLinkage +static int staticModuleFunction(int x); //ModuleInternalLinkage -export double exportedModuleFunction(double y, int z); // ExternalLinkage +export double exportedModuleFunction(double y, int z); //ExternalLinkage +// CHECK-B: --- +// CHECK-B-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-B-NEXT: Name: 'exportedModuleFunction' +// CHECK-B-NEXT: Location: +// CHECK-B-NEXT: - LineNumber: 34 +// CHECK-B-NEXT: Filename: {{.*}} +// CHECK-B-NEXT: Params: +// CHECK-B-NEXT: - Type: +// CHECK-B-NEXT: Name: 'double' +// CHECK-B-NEXT: Name: 'y' +// CHECK-B-NEXT: - Type: +// CHECK-B-NEXT: Name: 'int' +// CHECK-B-NEXT: Name: 'z' +// CHECK-B-NEXT: ReturnType: +// CHECK-B-NEXT: Type: +// CHECK-B-NEXT: Name: 'double' +// CHECK-B-NEXT: ... -// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: --- -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: ChildFunctions: -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'moduleFunction' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 11 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Params: -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'x' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'exportedModuleFunction' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 15 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Params: -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'double' -// CHECK-0-NEXT: Name: 'y' -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'z' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'double' -// CHECK-0-NEXT: ... +// CHECK-C: docs-without: staticModuleFunction.yaml diff --git a/test/clang-doc/public-namespace.cpp b/test/clang-doc/public-namespace.cpp deleted file mode 100644 index d104ff2c7..000000000 --- a/test/clang-doc/public-namespace.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -namespace A { - -void f(); - -} // namespace A - -namespace A { - -void f(){}; - -namespace B { - -enum E { X }; - -E func(int i) { return X; } - -} // namespace B -} // namespace A - -// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: cat %t/docs/./A.yaml | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: --- -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'A' -// CHECK-0-NEXT: ChildFunctions: -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'f' -// CHECK-0-NEXT: Namespace: -// CHECK-0-NEXT: - Type: Namespace -// CHECK-0-NEXT: Name: 'A' -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: DefLocation: -// CHECK-0-NEXT: LineNumber: 17 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 11 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'void' -// CHECK-0-NEXT: ... - -// RUN: cat %t/docs/A/B.yaml | FileCheck %s --check-prefix CHECK-1 -// CHECK-1: --- -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'B' -// CHECK-1-NEXT: Namespace: -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'A' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: ChildFunctions: -// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'func' -// CHECK-1-NEXT: Namespace: -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'B' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'A' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: DefLocation: -// CHECK-1-NEXT: LineNumber: 23 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: Params: -// CHECK-1-NEXT: - Type: -// CHECK-1-NEXT: Name: 'int' -// CHECK-1-NEXT: Name: 'i' -// CHECK-1-NEXT: ReturnType: -// CHECK-1-NEXT: Type: -// CHECK-1-NEXT: Name: 'enum A::B::E' -// CHECK-1-NEXT: ChildEnums: -// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'E' -// CHECK-1-NEXT: Namespace: -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'B' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'A' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: DefLocation: -// CHECK-1-NEXT: LineNumber: 21 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: Members: -// CHECK-1-NEXT: - 'X' -// CHECK-1-NEXT: ... diff --git a/test/clang-doc/public-record.cpp b/test/clang-doc/public-record.cpp deleted file mode 100644 index d3302193f..000000000 --- a/test/clang-doc/public-record.cpp +++ /dev/null @@ -1,208 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// This test requires Linux due to system-dependent USR for the inner class. -// REQUIRES: system-linux -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -void H() { - class I {}; -} - -union A { int X; int Y; }; - -enum B { X, Y }; - -enum class Bc { A, B }; - -struct C { int i; }; - -class D {}; - -class E { -public: - E() {} - ~E() {} - -protected: - void ProtectedMethod(); -}; - -void E::ProtectedMethod() {} - -class F : virtual private D, public E {}; - -class X { - class Y {}; -}; - -// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: cat %t/docs/./C.yaml | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: --- -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'C' -// CHECK-0-NEXT: DefLocation: -// CHECK-0-NEXT: LineNumber: 21 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Members: -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'i' -// CHECK-0-NEXT: ... - -// RUN: cat %t/docs/./A.yaml | FileCheck %s --check-prefix CHECK-1 -// CHECK-1: --- -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'A' -// CHECK-1-NEXT: DefLocation: -// CHECK-1-NEXT: LineNumber: 15 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: TagType: Union -// CHECK-1-NEXT: Members: -// CHECK-1-NEXT: - Type: -// CHECK-1-NEXT: Name: 'int' -// CHECK-1-NEXT: Name: 'X' -// CHECK-1-NEXT: - Type: -// CHECK-1-NEXT: Name: 'int' -// CHECK-1-NEXT: Name: 'Y' -// CHECK-1-NEXT: ... - -// RUN: cat %t/docs/./F.yaml | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: --- -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: Name: 'F' -// CHECK-2-NEXT: DefLocation: -// CHECK-2-NEXT: LineNumber: 36 -// CHECK-2-NEXT: Filename: 'test' -// CHECK-2-NEXT: TagType: Class -// CHECK-2-NEXT: Parents: -// CHECK-2-NEXT: - Type: Record -// CHECK-2-NEXT: Name: 'E' -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: VirtualParents: -// CHECK-2-NEXT: - Type: Record -// CHECK-2-NEXT: Name: 'D' -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: ... - -// RUN: cat %t/docs/./E.yaml | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: --- -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 25 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: TagType: Class -// CHECK-3-NEXT: ChildFunctions: -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 27 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: '~E' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 28 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'ProtectedMethod' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 34 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: Location: -// CHECK-3-NEXT: - LineNumber: 31 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: ... - -// RUN: cat %t/docs/./D.yaml | FileCheck %s --check-prefix CHECK-4 -// CHECK-4: --- -// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: Name: 'D' -// CHECK-4-NEXT: DefLocation: -// CHECK-4-NEXT: LineNumber: 23 -// CHECK-4-NEXT: Filename: 'test' -// CHECK-4-NEXT: TagType: Class -// CHECK-4-NEXT: ... - -// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-5 -// CHECK-5: --- -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Name: 'X' -// CHECK-5-NEXT: DefLocation: -// CHECK-5-NEXT: LineNumber: 38 -// CHECK-5-NEXT: Filename: 'test' -// CHECK-5-NEXT: TagType: Class -// CHECK-5-NEXT: ... - -// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-6 -// CHECK-6: --- -// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: ChildFunctions: -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'H' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 11 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: ReturnType: -// CHECK-6-NEXT: Type: -// CHECK-6-NEXT: Name: 'void' -// CHECK-6-NEXT: ChildEnums: -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'B' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 17 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: Members: -// CHECK-6-NEXT: - 'X' -// CHECK-6-NEXT: - 'Y' -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'Bc' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 19 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: Scoped: true -// CHECK-6-NEXT: Members: -// CHECK-6-NEXT: - 'A' -// CHECK-6-NEXT: - 'B' -// CHECK-6-NEXT: ... diff --git a/test/clang-doc/public-records.cpp b/test/clang-doc/public-records.cpp new file mode 100644 index 000000000..a00a23b0d --- /dev/null +++ b/test/clang-doc/public-records.cpp @@ -0,0 +1,341 @@ +// This test requires linux because it uses `diff` and compares filepaths +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" +// RUN: clang-doc --public --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --doxygen -p %t %t/test.cpp -output=%t/docs-without-flag +// RUN: cat %t/docs/function.yaml | FileCheck %s --check-prefix=CHECK-A +// RUN: cat %t/docs/inlinedFunction.yaml | FileCheck %s --check-prefix=CHECK-B +// RUN: cat %t/docs/functionWithInnerClass.yaml | FileCheck %s --check-prefix=CHECK-C +// RUN: cat %t/docs/inlinedFunctionWithInnerClass.yaml | FileCheck %s --check-prefix=CHECK-D +// RUN: cat %t/docs/Class/publicMethod.yaml| FileCheck %s --check-prefix=CHECK-E +// RUN: cat %t/docs/Class.yaml| FileCheck %s --check-prefix=CHECK-F +// RUN: cat %t/docs/Class/protectedMethod.yaml| FileCheck %s --check-prefix=CHECK-G +// RUN: cat %t/docs/named.yaml| FileCheck %s --check-prefix=CHECK-H +// RUN: cat %t/docs/named/NamedClass.yaml| FileCheck %s --check-prefix=CHECK-I +// RUN: cat %t/docs/named/namedFunction.yaml| FileCheck %s --check-prefix=CHECK-J +// RUN: cat %t/docs/named/namedInlineFunction.yaml| FileCheck %s --check-prefix=CHECK-K +// RUN: cat %t/docs/named/NamedClass/namedPublicMethod.yaml| FileCheck %s --check-prefix=CHECK-L +// RUN: cat %t/docs/named/NamedClass/namedProtectedMethod.yaml| FileCheck %s --check-prefix=CHECK-M +// RUN: (diff -qry %t/docs-without-flag %t/docs | sed 's:.*/::' > %t/public.diff) || true +// RUN: cat %t/public.diff | FileCheck %s --check-prefix=CHECK-N + +void function(int x); + +// CHECK-A: --- +// CHECK-A-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-A-NEXT: Name: 'function' +// CHECK-A-NEXT: Location: +// CHECK-A-NEXT: - LineNumber: 25 +// CHECK-A-NEXT: Filename: {{.*}} +// CHECK-A-NEXT: Params: +// CHECK-A-NEXT: - Type: +// CHECK-A-NEXT: Name: 'int' +// CHECK-A-NEXT: Name: 'x' +// CHECK-A-NEXT: ReturnType: +// CHECK-A-NEXT: Type: +// CHECK-A-NEXT: Name: 'void' +// CHECK-A-NEXT: ... + +inline int inlinedFunction(int x); + +// CHECK-B: --- +// CHECK-B-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-B-NEXT: Name: 'inlinedFunction' +// CHECK-B-NEXT: Location: +// CHECK-B-NEXT: - LineNumber: 42 +// CHECK-B-NEXT: Filename: {{.*}} +// CHECK-B-NEXT: Params: +// CHECK-B-NEXT: - Type: +// CHECK-B-NEXT: Name: 'int' +// CHECK-B-NEXT: Name: 'x' +// CHECK-B-NEXT: ReturnType: +// CHECK-B-NEXT: Type: +// CHECK-B-NEXT: Name: 'int' +// CHECK-B-NEXT: ... + +int functionWithInnerClass(int x){ + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +// CHECK-C: --- +// CHECK-C-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-C-NEXT: Name: 'functionWithInnerClass' +// CHECK-C-NEXT: DefLocation: +// CHECK-C-NEXT: LineNumber: 59 +// CHECK-C-NEXT: Filename: {{.*}} +// CHECK-C-NEXT: Params: +// CHECK-C-NEXT: - Type: +// CHECK-C-NEXT: Name: 'int' +// CHECK-C-NEXT: Name: 'x' +// CHECK-C-NEXT: ReturnType: +// CHECK-C-NEXT: Type: +// CHECK-C-NEXT: Name: 'int' +// CHECK-C-NEXT: ... + +inline int inlinedFunctionWithInnerClass(int x){ + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +// CHECK-D: --- +// CHECK-D-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-D-NEXT: Name: 'inlinedFunctionWithInnerClass' +// CHECK-D-NEXT: DefLocation: +// CHECK-D-NEXT: LineNumber: 83 +// CHECK-D-NEXT: Filename: {{.*}} +// CHECK-D-NEXT: Params: +// CHECK-D-NEXT: - Type: +// CHECK-D-NEXT: Name: 'int' +// CHECK-D-NEXT: Name: 'x' +// CHECK-D-NEXT: ReturnType: +// CHECK-D-NEXT: Type: +// CHECK-D-NEXT: Name: 'int' +// CHECK-D-NEXT: ... + +class Class { + public: + void publicMethod(); + int publicField; + protected: + void protectedMethod(); + int protectedField; + private: + void privateMethod(); + int privateField; +}; + +// CHECK-E: --- +// CHECK-E-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-E-NEXT: Name: 'publicMethod' +// CHECK-E-NEXT: Namespace: +// CHECK-E-NEXT: - Type: Record +// CHECK-E-NEXT: Name: 'Class' +// CHECK-E-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-E-NEXT: Location: +// CHECK-E-NEXT: - LineNumber: 109 +// CHECK-E-NEXT: Filename: {{.*}} +// CHECK-E-NEXT: IsMethod: true +// CHECK-E-NEXT: Parent: +// CHECK-E-NEXT: Type: Record +// CHECK-E-NEXT: Name: 'Class' +// CHECK-E-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-E-NEXT: ReturnType: +// CHECK-E-NEXT: Type: +// CHECK-E-NEXT: Name: 'void' +// CHECK-E-NEXT: ... + +// CHECK-F: --- +// CHECK-F-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-F-NEXT: Name: 'Class' +// CHECK-F-NEXT: DefLocation: +// CHECK-F-NEXT: LineNumber: 107 +// CHECK-F-NEXT: Filename: {{.*}} +// CHECK-F-NEXT: TagType: Class +// CHECK-F-NEXT: Members: +// CHECK-F-NEXT: - Type: +// CHECK-F-NEXT: Name: 'int' +// CHECK-F-NEXT: Name: 'publicField' +// CHECK-F-NEXT: - Type: +// CHECK-F-NEXT: Name: 'int' +// CHECK-F-NEXT: Name: 'protectedField' +// CHECK-F-NEXT: Access: Protected +// CHECK-F-NEXT: ... + +// CHECK-G: --- +// CHECK-G-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-G-NEXT: Name: 'protectedMethod' +// CHECK-G-NEXT: Namespace: +// CHECK-G-NEXT: - Type: Record +// CHECK-G-NEXT: Name: 'Class' +// CHECK-G-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-G-NEXT: Location: +// CHECK-G-NEXT: - LineNumber: 112 +// CHECK-G-NEXT: Filename: {{.*}} +// CHECK-G-NEXT: IsMethod: true +// CHECK-G-NEXT: Parent: +// CHECK-G-NEXT: Type: Record +// CHECK-G-NEXT: Name: 'Class' +// CHECK-G-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-G-NEXT: ReturnType: +// CHECK-G-NEXT: Type: +// CHECK-G-NEXT: Name: 'void' +// CHECK-G-NEXT: ... + +namespace named{ + class NamedClass { + public: + void namedPublicMethod(); + int namedPublicField; + protected: + void namedProtectedMethod(); + int namedProtectedField; + private: + void namedPrivateMethod(); + int namedPrivateField; + }; + + void namedFunction(); + static void namedStaticFunction(); + inline void namedInlineFunction(); +} + +// CHECK-H: --- +// CHECK-H-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-H-NEXT: Name: 'named' +// CHECK-H-NEXT: ... + +// CHECK-I: --- +// CHECK-I-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-I-NEXT: Name: 'NamedClass' +// CHECK-I-NEXT: Namespace: +// CHECK-I-NEXT: - Type: Namespace +// CHECK-I-NEXT: Name: 'named' +// CHECK-I-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-I-NEXT: DefLocation: +// CHECK-I-NEXT: LineNumber: 177 +// CHECK-I-NEXT: Filename: {{.*}} +// CHECK-I-NEXT: TagType: Class +// CHECK-I-NEXT: Members: +// CHECK-I-NEXT: - Type: +// CHECK-I-NEXT: Name: 'int' +// CHECK-I-NEXT: Name: 'namedPublicField' +// CHECK-I-NEXT: - Type: +// CHECK-I-NEXT: Name: 'int' +// CHECK-I-NEXT: Name: 'namedProtectedField' +// CHECK-I-NEXT: Access: Protected +// CHECK-I-NEXT: ... + +// CHECK-J: --- +// CHECK-J-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-J-NEXT: Name: 'namedFunction' +// CHECK-J-NEXT: Namespace: +// CHECK-J-NEXT: - Type: Namespace +// CHECK-J-NEXT: Name: 'named' +// CHECK-J-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-J-NEXT: Location: +// CHECK-J-NEXT: - LineNumber: 189 +// CHECK-J-NEXT: Filename: {{.*}} +// CHECK-J-NEXT: ReturnType: +// CHECK-J-NEXT: Type: +// CHECK-J-NEXT: Name: 'void' +// CHECK-J-NEXT: ... + +// CHECK-K: --- +// CHECK-K-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-K-NEXT: Name: 'namedInlineFunction' +// CHECK-K-NEXT: Namespace: +// CHECK-K-NEXT: - Type: Namespace +// CHECK-K-NEXT: Name: 'named' +// CHECK-K-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-K-NEXT: Location: +// CHECK-K-NEXT: - LineNumber: 191 +// CHECK-K-NEXT: Filename: {{.*}} +// CHECK-K-NEXT: ReturnType: +// CHECK-K-NEXT: Type: +// CHECK-K-NEXT: Name: 'void' +// CHECK-K-NEXT: ... + +// CHECK-L: --- +// CHECK-L-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-L-NEXT: Name: 'namedPublicMethod' +// CHECK-L-NEXT: Namespace: +// CHECK-L-NEXT: - Type: Record +// CHECK-L-NEXT: Name: 'NamedClass' +// CHECK-L-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-L-NEXT: - Type: Namespace +// CHECK-L-NEXT: Name: 'named' +// CHECK-L-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-L-NEXT: Location: +// CHECK-L-NEXT: - LineNumber: 179 +// CHECK-L-NEXT: Filename: {{.*}} +// CHECK-L-NEXT: IsMethod: true +// CHECK-L-NEXT: Parent: +// CHECK-L-NEXT: Type: Record +// CHECK-L-NEXT: Name: 'NamedClass' +// CHECK-L-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-L-NEXT: ReturnType: +// CHECK-L-NEXT: Type: +// CHECK-L-NEXT: Name: 'void' +// CHECK-L-NEXT: ... + +// CHECK-M: --- +// CHECK-M-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-M-NEXT: Name: 'namedProtectedMethod' +// CHECK-M-NEXT: Namespace: +// CHECK-M-NEXT: - Type: Record +// CHECK-M-NEXT: Name: 'NamedClass' +// CHECK-M-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-M-NEXT: - Type: Namespace +// CHECK-M-NEXT: Name: 'named' +// CHECK-M-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-M-NEXT: Location: +// CHECK-M-NEXT: - LineNumber: 182 +// CHECK-M-NEXT: Filename: {{.*}} +// CHECK-M-NEXT: IsMethod: true +// CHECK-M-NEXT: Parent: +// CHECK-M-NEXT: Type: Record +// CHECK-M-NEXT: Name: 'NamedClass' +// CHECK-M-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-M-NEXT: ReturnType: +// CHECK-M-NEXT: Type: +// CHECK-M-NEXT: Name: 'void' +// CHECK-M-NEXT: ... + + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x){ + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace{ + class AnonClass { + public: + void anonPublicMethod(); + int anonPublicField; + protected: + void anonProtectedMethod(); + int anonProtectedField; + private: + void anonPrivateMethod(); + int anonPrivateField; + }; + + void anonFunction(); + static void anonStaticFunction(); + inline void anonInlineFunction(); +} + +// CHECK-N: docs-without-flag: .yaml +// CHECK-N-NEXT: docs-without-flag: AnonClass +// CHECK-N-NEXT: docs-without-flag: AnonClass.yaml +// CHECK-N-NEXT: Class: privateMethod.yaml +// CHECK-N-NEXT: Class.yaml differ +// CHECK-N-NEXT: docs-without-flag: anonFunction.yaml +// CHECK-N-NEXT: docs-without-flag: anonInlineFunction.yaml +// CHECK-N-NEXT: docs-without-flag: anonStaticFunction.yaml +// CHECK-N-NEXT: docs-without-flag: functionWithInnerClass +// CHECK-N-NEXT: docs-without-flag: inlinedFunctionWithInnerClass +// CHECK-N-NEXT: NamedClass: namedPrivateMethod.yaml +// CHECK-N-NEXT: NamedClass.yaml differ +// CHECK-N-NEXT: named: namedStaticFunction.yaml +// CHECK-N-NEXT: docs-without-flag: staticFunction.yaml +// CHECK-N-NEXT: docs-without-flag: staticFunctionWithInnerClass +// CHECK-N-NEXT: docs-without-flag: staticFunctionWithInnerClass.yaml diff --git a/test/clang-doc/test_cases/linkage.cpp b/test/clang-doc/test_cases/linkage.cpp deleted file mode 100644 index ed4b4a303..000000000 --- a/test/clang-doc/test_cases/linkage.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// REQUIRES: system-linux -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -void function(int x); - -inline int inlinedFunction(int x); - -int functionWithInnerClass(int x) { - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -inline int inlinedFunctionWithInnerClass(int x) { - class InnerClass { //VisibleNoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -class Class { -public: - void publicMethod(); - int publicField; - -protected: - void protectedMethod(); - int protectedField; - -private: - void privateMethod(); - int privateField; -}; - -namespace named { -class NamedClass { -public: - void namedPublicMethod(); - int namedPublicField; - -protected: - void namedProtectedMethod(); - int namedProtectedField; - -private: - void namedPrivateMethod(); - int namedPrivateField; -}; - -void namedFunction(); -static void namedStaticFunction(); -inline void namedInlineFunction(); -} // namespace named - -static void staticFunction(int x); //Internal Linkage - -static int staticFunctionWithInnerClass(int x) { - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -namespace { -class AnonClass { -public: - void anonPublicMethod(); - int anonPublicField; - -protected: - void anonProtectedMethod(); - int anonProtectedField; - -private: - void anonPrivateMethod(); - int anonPrivateField; -}; - -void anonFunction(); -static void anonStaticFunction(); -inline void anonInlineFunction(); -} // namespace diff --git a/test/clang-doc/test_cases/module.cpp b/test/clang-doc/test_cases/module.cpp deleted file mode 100644 index 3c30a5476..000000000 --- a/test/clang-doc/test_cases/module.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -export module M; - -int moduleFunction(int x); // ModuleLinkage - -static int staticModuleFunction(int x); // ModuleInternalLinkage - -export double exportedModuleFunction(double y, int z); // ExternalLinkage diff --git a/test/clang-doc/yaml-comment.cpp b/test/clang-doc/yaml-comment.cpp index 7aa8e64d5..445e2be19 100644 --- a/test/clang-doc/yaml-comment.cpp +++ b/test/clang-doc/yaml-comment.cpp @@ -27,112 +27,110 @@ void F(int I, int J); /// Bonus comment on definition void F(int I, int J) {} -// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --format=yaml --doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-0 +// RUN: cat %t/docs/./F.yaml | FileCheck %s --check-prefix CHECK-0 // CHECK-0: --- -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: ChildFunctions: -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'F' -// CHECK-0-NEXT: Description: -// CHECK-0-NEXT: - Kind: 'FullComment' +// CHECK-0-NEXT: USR: '7574630614A535710E5A6ABCFFF98BCA2D06A4CA' +// CHECK-0-NEXT: Name: 'F' +// CHECK-0-NEXT: Description: +// CHECK-0-NEXT: - Kind: 'FullComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'BlockCommandComment' +// CHECK-0-NEXT: Name: 'brief' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'ParagraphComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'BlockCommandComment' -// CHECK-0-NEXT: Name: 'brief' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Brief description.' -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Extended description that' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' continues onto the next line.' +// CHECK-0-NEXT: Text: ' Brief description.' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Extended description that' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' continues onto the next line.' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' +// CHECK-0-NEXT: Name: 'ul' +// CHECK-0-NEXT: AttrKeys: +// CHECK-0-NEXT: - 'class' +// CHECK-0-NEXT: AttrValues: +// CHECK-0-NEXT: - 'test' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' +// CHECK-0-NEXT: Name: 'li' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Testing.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'HTMLEndTagComment' +// CHECK-0-NEXT: Name: 'ul' +// CHECK-0-NEXT: SelfClosing: true +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'VerbatimBlockComment' +// CHECK-0-NEXT: Name: 'verbatim' +// CHECK-0-NEXT: CloseName: 'endverbatim' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'VerbatimBlockLineComment' +// CHECK-0-NEXT: Text: ' The description continues.' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' --' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'ParamCommandComment' +// CHECK-0-NEXT: Direction: '[out]' +// CHECK-0-NEXT: ParamName: 'I' +// CHECK-0-NEXT: Explicit: true +// CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'ParagraphComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' -// CHECK-0-NEXT: Name: 'ul' -// CHECK-0-NEXT: AttrKeys: -// CHECK-0-NEXT: - 'class' -// CHECK-0-NEXT: AttrValues: -// CHECK-0-NEXT: - 'test' +// CHECK-0-NEXT: Text: ' is a parameter.' // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' -// CHECK-0-NEXT: Name: 'li' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Testing.' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'HTMLEndTagComment' -// CHECK-0-NEXT: Name: 'ul' -// CHECK-0-NEXT: SelfClosing: true -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'VerbatimBlockComment' -// CHECK-0-NEXT: Name: 'verbatim' -// CHECK-0-NEXT: CloseName: 'endverbatim' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'VerbatimBlockLineComment' -// CHECK-0-NEXT: Text: ' The description continues.' +// CHECK-0-NEXT: - Kind: 'ParamCommandComment' +// CHECK-0-NEXT: Direction: '[in]' +// CHECK-0-NEXT: ParamName: 'J' +// CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'ParagraphComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' --' +// CHECK-0-NEXT: Text: ' is a parameter.' // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'ParamCommandComment' -// CHECK-0-NEXT: Direction: '[out]' -// CHECK-0-NEXT: ParamName: 'I' -// CHECK-0-NEXT: Explicit: true -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' is a parameter.' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'ParamCommandComment' -// CHECK-0-NEXT: Direction: '[in]' -// CHECK-0-NEXT: ParamName: 'J' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' is a parameter.' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'BlockCommandComment' -// CHECK-0-NEXT: Name: 'return' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' void' -// CHECK-0-NEXT: - Kind: 'FullComment' +// CHECK-0-NEXT: - Kind: 'BlockCommandComment' +// CHECK-0-NEXT: Name: 'return' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'ParagraphComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Bonus comment on definition' -// CHECK-0-NEXT: DefLocation: -// CHECK-0-NEXT: LineNumber: 28 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 25 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Params: -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'I' -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'J' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: Text: ' void' +// CHECK-0-NEXT: - Kind: 'FullComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Bonus comment on definition' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 28 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 25 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'I' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'J' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' // CHECK-0-NEXT: ... diff --git a/test/clang-doc/yaml-linkage.cpp b/test/clang-doc/yaml-linkage.cpp deleted file mode 100644 index 3a0aa5bf9..000000000 --- a/test/clang-doc/yaml-linkage.cpp +++ /dev/null @@ -1,529 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// REQUIRES: system-linux -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -void function(int x); - -inline int inlinedFunction(int x); - -int functionWithInnerClass(int x) { - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -inline int inlinedFunctionWithInnerClass(int x) { - class InnerClass { //VisibleNoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -class Class { -public: - void publicMethod(); - int publicField; - -protected: - void protectedMethod(); - int protectedField; - -private: - void privateMethod(); - int privateField; -}; - -namespace named { -class NamedClass { -public: - void namedPublicMethod(); - int namedPublicField; - -protected: - void namedProtectedMethod(); - int namedProtectedField; - -private: - void namedPrivateMethod(); - int namedPrivateField; -}; - -void namedFunction(); -static void namedStaticFunction(); -inline void namedInlineFunction(); -} // namespace named - -static void staticFunction(int x); //Internal Linkage - -static int staticFunctionWithInnerClass(int x) { - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -namespace { -class AnonClass { -public: - void anonPublicMethod(); - int anonPublicField; - -protected: - void anonProtectedMethod(); - int anonProtectedField; - -private: - void anonPrivateMethod(); - int anonPrivateField; -}; - -void anonFunction(); -static void anonStaticFunction(); -inline void anonInlineFunction(); -} // namespace - -// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: cat %t/docs/./Class.yaml | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: --- -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'Class' -// CHECK-0-NEXT: DefLocation: -// CHECK-0-NEXT: LineNumber: 32 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: TagType: Class -// CHECK-0-NEXT: Members: -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'publicField' -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'protectedField' -// CHECK-0-NEXT: Access: Protected -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'privateField' -// CHECK-0-NEXT: Access: Private -// CHECK-0-NEXT: ChildFunctions: -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'publicMethod' -// CHECK-0-NEXT: Namespace: -// CHECK-0-NEXT: - Type: Record -// CHECK-0-NEXT: Name: 'Class' -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 34 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: IsMethod: true -// CHECK-0-NEXT: Parent: -// CHECK-0-NEXT: Type: Record -// CHECK-0-NEXT: Name: 'Class' -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'void' -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'protectedMethod' -// CHECK-0-NEXT: Namespace: -// CHECK-0-NEXT: - Type: Record -// CHECK-0-NEXT: Name: 'Class' -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 38 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: IsMethod: true -// CHECK-0-NEXT: Parent: -// CHECK-0-NEXT: Type: Record -// CHECK-0-NEXT: Name: 'Class' -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'void' -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'privateMethod' -// CHECK-0-NEXT: Namespace: -// CHECK-0-NEXT: - Type: Record -// CHECK-0-NEXT: Name: 'Class' -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 42 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: IsMethod: true -// CHECK-0-NEXT: Parent: -// CHECK-0-NEXT: Type: Record -// CHECK-0-NEXT: Name: 'Class' -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'void' -// CHECK-0-NEXT: ... - -// RUN: cat %t/docs/./named.yaml | FileCheck %s --check-prefix CHECK-1 -// CHECK-1: --- -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'named' -// CHECK-1-NEXT: ChildFunctions: -// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'namedFunction' -// CHECK-1-NEXT: Namespace: -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'named' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Location: -// CHECK-1-NEXT: - LineNumber: 61 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: ReturnType: -// CHECK-1-NEXT: Type: -// CHECK-1-NEXT: Name: 'void' -// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'namedStaticFunction' -// CHECK-1-NEXT: Namespace: -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'named' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Location: -// CHECK-1-NEXT: - LineNumber: 62 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: ReturnType: -// CHECK-1-NEXT: Type: -// CHECK-1-NEXT: Name: 'void' -// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'namedInlineFunction' -// CHECK-1-NEXT: Namespace: -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'named' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Location: -// CHECK-1-NEXT: - LineNumber: 63 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: ReturnType: -// CHECK-1-NEXT: Type: -// CHECK-1-NEXT: Name: 'void' -// CHECK-1-NEXT: ... - -// RUN: cat %t/docs/./AnonClass.yaml | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: --- -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: Name: 'AnonClass' -// CHECK-2-NEXT: Namespace: -// CHECK-2-NEXT: - Type: Namespace -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: DefLocation: -// CHECK-2-NEXT: LineNumber: 78 -// CHECK-2-NEXT: Filename: 'test' -// CHECK-2-NEXT: TagType: Class -// CHECK-2-NEXT: Members: -// CHECK-2-NEXT: - Type: -// CHECK-2-NEXT: Name: 'int' -// CHECK-2-NEXT: Name: 'anonPublicField' -// CHECK-2-NEXT: - Type: -// CHECK-2-NEXT: Name: 'int' -// CHECK-2-NEXT: Name: 'anonProtectedField' -// CHECK-2-NEXT: Access: Protected -// CHECK-2-NEXT: - Type: -// CHECK-2-NEXT: Name: 'int' -// CHECK-2-NEXT: Name: 'anonPrivateField' -// CHECK-2-NEXT: Access: Private -// CHECK-2-NEXT: ChildFunctions: -// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: Name: 'anonPublicMethod' -// CHECK-2-NEXT: Namespace: -// CHECK-2-NEXT: - Type: Record -// CHECK-2-NEXT: Name: 'AnonClass' -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: - Type: Namespace -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: Location: -// CHECK-2-NEXT: - LineNumber: 80 -// CHECK-2-NEXT: Filename: 'test' -// CHECK-2-NEXT: IsMethod: true -// CHECK-2-NEXT: Parent: -// CHECK-2-NEXT: Type: Record -// CHECK-2-NEXT: Name: 'AnonClass' -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: ReturnType: -// CHECK-2-NEXT: Type: -// CHECK-2-NEXT: Name: 'void' -// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: Name: 'anonProtectedMethod' -// CHECK-2-NEXT: Namespace: -// CHECK-2-NEXT: - Type: Record -// CHECK-2-NEXT: Name: 'AnonClass' -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: - Type: Namespace -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: Location: -// CHECK-2-NEXT: - LineNumber: 84 -// CHECK-2-NEXT: Filename: 'test' -// CHECK-2-NEXT: IsMethod: true -// CHECK-2-NEXT: Parent: -// CHECK-2-NEXT: Type: Record -// CHECK-2-NEXT: Name: 'AnonClass' -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: ReturnType: -// CHECK-2-NEXT: Type: -// CHECK-2-NEXT: Name: 'void' -// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: Name: 'anonPrivateMethod' -// CHECK-2-NEXT: Namespace: -// CHECK-2-NEXT: - Type: Record -// CHECK-2-NEXT: Name: 'AnonClass' -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: - Type: Namespace -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: Location: -// CHECK-2-NEXT: - LineNumber: 88 -// CHECK-2-NEXT: Filename: 'test' -// CHECK-2-NEXT: IsMethod: true -// CHECK-2-NEXT: Parent: -// CHECK-2-NEXT: Type: Record -// CHECK-2-NEXT: Name: 'AnonClass' -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-2-NEXT: ReturnType: -// CHECK-2-NEXT: Type: -// CHECK-2-NEXT: Name: 'void' -// CHECK-2-NEXT: ... - -// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: --- -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ChildFunctions: -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'anonFunction' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Namespace -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Location: -// CHECK-3-NEXT: - LineNumber: 92 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'anonStaticFunction' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Namespace -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Location: -// CHECK-3-NEXT: - LineNumber: 93 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'anonInlineFunction' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Namespace -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Location: -// CHECK-3-NEXT: - LineNumber: 94 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: ... - -// RUN: cat %t/docs/staticFunctionWithInnerClass/InnerClass.yaml | FileCheck %s --check-prefix CHECK-4 -// CHECK-4: --- -// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: Name: 'InnerClass' -// CHECK-4-NEXT: Namespace: -// CHECK-4-NEXT: - Type: Function -// CHECK-4-NEXT: Name: 'staticFunctionWithInnerClass' -// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: DefLocation: -// CHECK-4-NEXT: LineNumber: 69 -// CHECK-4-NEXT: Filename: 'test' -// CHECK-4-NEXT: TagType: Class -// CHECK-4-NEXT: ChildFunctions: -// CHECK-4-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: Name: 'innerPublicMethod' -// CHECK-4-NEXT: Namespace: -// CHECK-4-NEXT: - Type: Record -// CHECK-4-NEXT: Name: 'InnerClass' -// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: - Type: Function -// CHECK-4-NEXT: Name: 'staticFunctionWithInnerClass' -// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: DefLocation: -// CHECK-4-NEXT: LineNumber: 71 -// CHECK-4-NEXT: Filename: 'test' -// CHECK-4-NEXT: IsMethod: true -// CHECK-4-NEXT: Parent: -// CHECK-4-NEXT: Type: Record -// CHECK-4-NEXT: Name: 'InnerClass' -// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: ReturnType: -// CHECK-4-NEXT: Type: -// CHECK-4-NEXT: Name: 'int' -// CHECK-4-NEXT: ... - -// RUN: cat %t/docs/named/NamedClass.yaml | FileCheck %s --check-prefix CHECK-5 -// CHECK-5: --- -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: Namespace: -// CHECK-5-NEXT: - Type: Namespace -// CHECK-5-NEXT: Name: 'named' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: DefLocation: -// CHECK-5-NEXT: LineNumber: 47 -// CHECK-5-NEXT: Filename: 'test' -// CHECK-5-NEXT: TagType: Class -// CHECK-5-NEXT: Members: -// CHECK-5-NEXT: - Type: -// CHECK-5-NEXT: Name: 'int' -// CHECK-5-NEXT: Name: 'namedPublicField' -// CHECK-5-NEXT: - Type: -// CHECK-5-NEXT: Name: 'int' -// CHECK-5-NEXT: Name: 'namedProtectedField' -// CHECK-5-NEXT: Access: Protected -// CHECK-5-NEXT: - Type: -// CHECK-5-NEXT: Name: 'int' -// CHECK-5-NEXT: Name: 'namedPrivateField' -// CHECK-5-NEXT: Access: Private -// CHECK-5-NEXT: ChildFunctions: -// CHECK-5-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Name: 'namedPublicMethod' -// CHECK-5-NEXT: Namespace: -// CHECK-5-NEXT: - Type: Record -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: - Type: Namespace -// CHECK-5-NEXT: Name: 'named' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Location: -// CHECK-5-NEXT: - LineNumber: 49 -// CHECK-5-NEXT: Filename: 'test' -// CHECK-5-NEXT: IsMethod: true -// CHECK-5-NEXT: Parent: -// CHECK-5-NEXT: Type: Record -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: ReturnType: -// CHECK-5-NEXT: Type: -// CHECK-5-NEXT: Name: 'void' -// CHECK-5-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Name: 'namedProtectedMethod' -// CHECK-5-NEXT: Namespace: -// CHECK-5-NEXT: - Type: Record -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: - Type: Namespace -// CHECK-5-NEXT: Name: 'named' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Location: -// CHECK-5-NEXT: - LineNumber: 53 -// CHECK-5-NEXT: Filename: 'test' -// CHECK-5-NEXT: IsMethod: true -// CHECK-5-NEXT: Parent: -// CHECK-5-NEXT: Type: Record -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: ReturnType: -// CHECK-5-NEXT: Type: -// CHECK-5-NEXT: Name: 'void' -// CHECK-5-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Name: 'namedPrivateMethod' -// CHECK-5-NEXT: Namespace: -// CHECK-5-NEXT: - Type: Record -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: - Type: Namespace -// CHECK-5-NEXT: Name: 'named' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Location: -// CHECK-5-NEXT: - LineNumber: 57 -// CHECK-5-NEXT: Filename: 'test' -// CHECK-5-NEXT: IsMethod: true -// CHECK-5-NEXT: Parent: -// CHECK-5-NEXT: Type: Record -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: ReturnType: -// CHECK-5-NEXT: Type: -// CHECK-5-NEXT: Name: 'void' -// CHECK-5-NEXT: ... - -// RUN: cat %t/docs/functionWithInnerClass/InnerClass.yaml | FileCheck %s --check-prefix CHECK-6 -// CHECK-6: --- -// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'InnerClass' -// CHECK-6-NEXT: Namespace: -// CHECK-6-NEXT: - Type: Function -// CHECK-6-NEXT: Name: 'functionWithInnerClass' -// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 15 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: TagType: Class -// CHECK-6-NEXT: ChildFunctions: -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'innerPublicMethod' -// CHECK-6-NEXT: Namespace: -// CHECK-6-NEXT: - Type: Record -// CHECK-6-NEXT: Name: 'InnerClass' -// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: - Type: Function -// CHECK-6-NEXT: Name: 'functionWithInnerClass' -// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 17 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: IsMethod: true -// CHECK-6-NEXT: Parent: -// CHECK-6-NEXT: Type: Record -// CHECK-6-NEXT: Name: 'InnerClass' -// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: ReturnType: -// CHECK-6-NEXT: Type: -// CHECK-6-NEXT: Name: 'int' -// CHECK-6-NEXT: ... - -// RUN: cat %t/docs/inlinedFunctionWithInnerClass/InnerClass.yaml | FileCheck %s --check-prefix CHECK-7 -// CHECK-7: --- -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: Name: 'InnerClass' -// CHECK-7-NEXT: Namespace: -// CHECK-7-NEXT: - Type: Function -// CHECK-7-NEXT: Name: 'inlinedFunctionWithInnerClass' -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: DefLocation: -// CHECK-7-NEXT: LineNumber: 24 -// CHECK-7-NEXT: Filename: 'test' -// CHECK-7-NEXT: TagType: Class -// CHECK-7-NEXT: ChildFunctions: -// CHECK-7-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: Name: 'innerPublicMethod' -// CHECK-7-NEXT: Namespace: -// CHECK-7-NEXT: - Type: Record -// CHECK-7-NEXT: Name: 'InnerClass' -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: - Type: Function -// CHECK-7-NEXT: Name: 'inlinedFunctionWithInnerClass' -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: DefLocation: -// CHECK-7-NEXT: LineNumber: 26 -// CHECK-7-NEXT: Filename: 'test' -// CHECK-7-NEXT: IsMethod: true -// CHECK-7-NEXT: Parent: -// CHECK-7-NEXT: Type: Record -// CHECK-7-NEXT: Name: 'InnerClass' -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: ReturnType: -// CHECK-7-NEXT: Type: -// CHECK-7-NEXT: Name: 'int' -// CHECK-7-NEXT: ... diff --git a/test/clang-doc/yaml-module.cpp b/test/clang-doc/yaml-module.cpp deleted file mode 100644 index 80602aca8..000000000 --- a/test/clang-doc/yaml-module.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -export module M; - -int moduleFunction(int x); // ModuleLinkage - -static int staticModuleFunction(int x); // ModuleInternalLinkage - -export double exportedModuleFunction(double y, int z); // ExternalLinkage - -// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: --- -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: ChildFunctions: -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'moduleFunction' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 11 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Params: -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'x' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'staticModuleFunction' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 13 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Params: -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'x' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'exportedModuleFunction' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 15 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Params: -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'double' -// CHECK-0-NEXT: Name: 'y' -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'z' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'double' -// CHECK-0-NEXT: ... diff --git a/test/clang-doc/yaml-namespace.cpp b/test/clang-doc/yaml-namespace.cpp index d187f7e39..c547c5c49 100644 --- a/test/clang-doc/yaml-namespace.cpp +++ b/test/clang-doc/yaml-namespace.cpp @@ -25,72 +25,81 @@ E func(int i) { return X; } } // namespace B } // namespace A -// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --format=yaml --doxygen -p %t %t/test.cpp -output=%t/docs // RUN: cat %t/docs/./A.yaml | FileCheck %s --check-prefix CHECK-0 // CHECK-0: --- -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' // CHECK-0-NEXT: Name: 'A' -// CHECK-0-NEXT: ChildFunctions: -// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: Name: 'f' -// CHECK-0-NEXT: Namespace: -// CHECK-0-NEXT: - Type: Namespace -// CHECK-0-NEXT: Name: 'A' -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-0-NEXT: DefLocation: -// CHECK-0-NEXT: LineNumber: 17 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 11 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'void' // CHECK-0-NEXT: ... -// RUN: cat %t/docs/A/B.yaml | FileCheck %s --check-prefix CHECK-1 +// RUN: cat %t/docs/A/f.yaml | FileCheck %s --check-prefix CHECK-1 // CHECK-1: --- -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'B' +// CHECK-1-NEXT: USR: '39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC' +// CHECK-1-NEXT: Name: 'f' // CHECK-1-NEXT: Namespace: // CHECK-1-NEXT: - Type: Namespace // CHECK-1-NEXT: Name: 'A' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: ChildFunctions: -// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'func' -// CHECK-1-NEXT: Namespace: -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'B' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'A' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: DefLocation: -// CHECK-1-NEXT: LineNumber: 23 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: Params: -// CHECK-1-NEXT: - Type: -// CHECK-1-NEXT: Name: 'int' -// CHECK-1-NEXT: Name: 'i' -// CHECK-1-NEXT: ReturnType: -// CHECK-1-NEXT: Type: -// CHECK-1-NEXT: Name: 'enum A::B::E' -// CHECK-1-NEXT: ChildEnums: -// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: Name: 'E' -// CHECK-1-NEXT: Namespace: -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'B' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: - Type: Namespace -// CHECK-1-NEXT: Name: 'A' -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-1-NEXT: DefLocation: -// CHECK-1-NEXT: LineNumber: 21 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: Members: -// CHECK-1-NEXT: - 'X' +// CHECK-1-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' +// CHECK-1-NEXT: DefLocation: +// CHECK-1-NEXT: LineNumber: 17 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: Location: +// CHECK-1-NEXT: - LineNumber: 11 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'void' // CHECK-1-NEXT: ... + +// RUN: cat %t/docs/A/B.yaml | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: --- +// CHECK-2-NEXT: USR: 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' +// CHECK-2-NEXT: Name: 'B' +// CHECK-2-NEXT: Namespace: +// CHECK-2-NEXT: - Type: Namespace +// CHECK-2-NEXT: Name: 'A' +// CHECK-2-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' +// CHECK-2-NEXT: ... + +// RUN: cat %t/docs/A/B/E.yaml | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: --- +// CHECK-3-NEXT: USR: 'E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775' +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: Name: 'B' +// CHECK-3-NEXT: USR: 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: Name: 'A' +// CHECK-3-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 21 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: Members: +// CHECK-3-NEXT: - 'X' +// CHECK-3-NEXT: ... + +// RUN: cat %t/docs/A/B/func.yaml | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: --- +// CHECK-4-NEXT: USR: '9A82CB33ED0FDF81EE383D31CD0957D153C5E840' +// CHECK-4-NEXT: Name: 'func' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Namespace +// CHECK-4-NEXT: Name: 'B' +// CHECK-4-NEXT: USR: 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' +// CHECK-4-NEXT: - Type: Namespace +// CHECK-4-NEXT: Name: 'A' +// CHECK-4-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 23 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: Params: +// CHECK-4-NEXT: - Type: +// CHECK-4-NEXT: Name: 'int' +// CHECK-4-NEXT: Name: 'i' +// CHECK-4-NEXT: ReturnType: +// CHECK-4-NEXT: Type: +// CHECK-4-NEXT: Name: 'enum A::B::E' +// CHECK-4-NEXT: ... diff --git a/test/clang-doc/yaml-record.cpp b/test/clang-doc/yaml-record.cpp index 8fad22017..1d5235f5c 100644 --- a/test/clang-doc/yaml-record.cpp +++ b/test/clang-doc/yaml-record.cpp @@ -39,12 +39,12 @@ class X { class Y {}; }; -// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --format=yaml --doxygen -p %t %t/test.cpp -output=%t/docs // RUN: cat %t/docs/./C.yaml | FileCheck %s --check-prefix CHECK-0 // CHECK-0: --- -// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: USR: '06B5F6A19BA9F6A832E127C9968282B94619B210' // CHECK-0-NEXT: Name: 'C' // CHECK-0-NEXT: DefLocation: // CHECK-0-NEXT: LineNumber: 21 @@ -57,7 +57,7 @@ class X { // RUN: cat %t/docs/./A.yaml | FileCheck %s --check-prefix CHECK-1 // CHECK-1: --- -// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: USR: 'ACE81AFA6627B4CEF2B456FB6E1252925674AF7E' // CHECK-1-NEXT: Name: 'A' // CHECK-1-NEXT: DefLocation: // CHECK-1-NEXT: LineNumber: 15 @@ -74,7 +74,7 @@ class X { // RUN: cat %t/docs/./F.yaml | FileCheck %s --check-prefix CHECK-2 // CHECK-2: --- -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: USR: 'E3B54702FABFF4037025BA194FC27C47006330B5' // CHECK-2-NEXT: Name: 'F' // CHECK-2-NEXT: DefLocation: // CHECK-2-NEXT: LineNumber: 36 @@ -83,81 +83,26 @@ class X { // CHECK-2-NEXT: Parents: // CHECK-2-NEXT: - Type: Record // CHECK-2-NEXT: Name: 'E' -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' // CHECK-2-NEXT: VirtualParents: // CHECK-2-NEXT: - Type: Record // CHECK-2-NEXT: Name: 'D' -// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: USR: '0921737541208B8FA9BB42B60F78AC1D779AA054' // CHECK-2-NEXT: ... // RUN: cat %t/docs/./E.yaml | FileCheck %s --check-prefix CHECK-3 // CHECK-3: --- -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' // CHECK-3-NEXT: Name: 'E' // CHECK-3-NEXT: DefLocation: // CHECK-3-NEXT: LineNumber: 25 // CHECK-3-NEXT: Filename: 'test' // CHECK-3-NEXT: TagType: Class -// CHECK-3-NEXT: ChildFunctions: -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 27 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: '~E' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 28 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'ProtectedMethod' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 34 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: Location: -// CHECK-3-NEXT: - LineNumber: 31 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' // CHECK-3-NEXT: ... // RUN: cat %t/docs/./D.yaml | FileCheck %s --check-prefix CHECK-4 // CHECK-4: --- -// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: USR: '0921737541208B8FA9BB42B60F78AC1D779AA054' // CHECK-4-NEXT: Name: 'D' // CHECK-4-NEXT: DefLocation: // CHECK-4-NEXT: LineNumber: 23 @@ -165,72 +110,143 @@ class X { // CHECK-4-NEXT: TagType: Class // CHECK-4-NEXT: ... -// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-5 +// RUN: cat %t/docs/./B.yaml | FileCheck %s --check-prefix CHECK-5 // CHECK-5: --- -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Name: 'X' +// CHECK-5-NEXT: USR: 'FC07BD34D5E77782C263FA944447929EA8753740' +// CHECK-5-NEXT: Name: 'B' // CHECK-5-NEXT: DefLocation: -// CHECK-5-NEXT: LineNumber: 38 +// CHECK-5-NEXT: LineNumber: 17 // CHECK-5-NEXT: Filename: 'test' -// CHECK-5-NEXT: TagType: Class +// CHECK-5-NEXT: Members: +// CHECK-5-NEXT: - 'X' +// CHECK-5-NEXT: - 'Y' // CHECK-5-NEXT: ... -// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-6 +// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-6 // CHECK-6: --- -// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: ChildFunctions: -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'H' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 11 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: ReturnType: -// CHECK-6-NEXT: Type: -// CHECK-6-NEXT: Name: 'void' -// CHECK-6-NEXT: ChildEnums: -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'B' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 17 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: Members: -// CHECK-6-NEXT: - 'X' -// CHECK-6-NEXT: - 'Y' -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'Bc' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 19 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: Scoped: true -// CHECK-6-NEXT: Members: -// CHECK-6-NEXT: - 'A' -// CHECK-6-NEXT: - 'B' +// CHECK-6-NEXT: USR: 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E' +// CHECK-6-NEXT: Name: 'X' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 38 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: TagType: Class // CHECK-6-NEXT: ... -// RUN: cat %t/docs/H/I.yaml | FileCheck %s --check-prefix CHECK-7 +// RUN: cat %t/docs/./H.yaml | FileCheck %s --check-prefix CHECK-7 // CHECK-7: --- -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: Name: 'I' -// CHECK-7-NEXT: Namespace: -// CHECK-7-NEXT: - Type: Function -// CHECK-7-NEXT: Name: 'H' -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: USR: 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E' +// CHECK-7-NEXT: Name: 'H' // CHECK-7-NEXT: DefLocation: -// CHECK-7-NEXT: LineNumber: 12 +// CHECK-7-NEXT: LineNumber: 11 // CHECK-7-NEXT: Filename: 'test' -// CHECK-7-NEXT: TagType: Class +// CHECK-7-NEXT: ReturnType: +// CHECK-7-NEXT: Type: +// CHECK-7-NEXT: Name: 'void' // CHECK-7-NEXT: ... -// RUN: cat %t/docs/X/Y.yaml | FileCheck %s --check-prefix CHECK-8 +// RUN: cat %t/docs/./Bc.yaml | FileCheck %s --check-prefix CHECK-8 // CHECK-8: --- -// CHECK-8-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-8-NEXT: Name: 'Y' -// CHECK-8-NEXT: Namespace: -// CHECK-8-NEXT: - Type: Record -// CHECK-8-NEXT: Name: 'X' -// CHECK-8-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-8-NEXT: USR: '1E3438A08BA22025C0B46289FF0686F92C8924C5' +// CHECK-8-NEXT: Name: 'Bc' // CHECK-8-NEXT: DefLocation: -// CHECK-8-NEXT: LineNumber: 39 +// CHECK-8-NEXT: LineNumber: 19 // CHECK-8-NEXT: Filename: 'test' -// CHECK-8-NEXT: TagType: Class +// CHECK-8-NEXT: Scoped: true +// CHECK-8-NEXT: Members: +// CHECK-8-NEXT: - 'A' +// CHECK-8-NEXT: - 'B' // CHECK-8-NEXT: ... + +// RUN: cat %t/docs/H/I.yaml | FileCheck %s --check-prefix CHECK-9 +// CHECK-9: --- +// CHECK-9-NEXT: USR: '3FB542274573CAEAD54CEBFFCAEE3D77FB9713D8' +// CHECK-9-NEXT: Name: 'I' +// CHECK-9-NEXT: Namespace: +// CHECK-9-NEXT: - Type: Function +// CHECK-9-NEXT: Name: 'H' +// CHECK-9-NEXT: USR: 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E' +// CHECK-9-NEXT: DefLocation: +// CHECK-9-NEXT: LineNumber: 12 +// CHECK-9-NEXT: Filename: 'test' +// CHECK-9-NEXT: TagType: Class +// CHECK-9-NEXT: ... + +// RUN: cat %t/docs/X/Y.yaml | FileCheck %s --check-prefix CHECK-10 +// CHECK-10: --- +// CHECK-10-NEXT: USR: '641AB4A3D36399954ACDE29C7A8833032BF40472' +// CHECK-10-NEXT: Name: 'Y' +// CHECK-10-NEXT: Namespace: +// CHECK-10-NEXT: - Type: Record +// CHECK-10-NEXT: Name: 'X' +// CHECK-10-NEXT: USR: 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E' +// CHECK-10-NEXT: DefLocation: +// CHECK-10-NEXT: LineNumber: 39 +// CHECK-10-NEXT: Filename: 'test' +// CHECK-10-NEXT: TagType: Class +// CHECK-10-NEXT: ... + +// RUN: cat %t/docs/E/ProtectedMethod.yaml | FileCheck %s --check-prefix CHECK-11 +// CHECK-11: --- +// CHECK-11-NEXT: USR: '5093D428CDC62096A67547BA52566E4FB9404EEE' +// CHECK-11-NEXT: Name: 'ProtectedMethod' +// CHECK-11-NEXT: Namespace: +// CHECK-11-NEXT: - Type: Record +// CHECK-11-NEXT: Name: 'E' +// CHECK-11-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-11-NEXT: DefLocation: +// CHECK-11-NEXT: LineNumber: 34 +// CHECK-11-NEXT: Filename: 'test' +// CHECK-11-NEXT: Location: +// CHECK-11-NEXT: - LineNumber: 31 +// CHECK-11-NEXT: Filename: 'test' +// CHECK-11-NEXT: IsMethod: true +// CHECK-11-NEXT: Parent: +// CHECK-11-NEXT: Type: Record +// CHECK-11-NEXT: Name: 'E' +// CHECK-11-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-11-NEXT: ReturnType: +// CHECK-11-NEXT: Type: +// CHECK-11-NEXT: Name: 'void' +// CHECK-11-NEXT: ... + +// RUN: cat %t/docs/E/E.yaml | FileCheck %s --check-prefix CHECK-12 +// CHECK-12: --- +// CHECK-12-NEXT: USR: 'DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4' +// CHECK-12-NEXT: Name: 'E' +// CHECK-12-NEXT: Namespace: +// CHECK-12-NEXT: - Type: Record +// CHECK-12-NEXT: Name: 'E' +// CHECK-12-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-12-NEXT: DefLocation: +// CHECK-12-NEXT: LineNumber: 27 +// CHECK-12-NEXT: Filename: 'test' +// CHECK-12-NEXT: IsMethod: true +// CHECK-12-NEXT: Parent: +// CHECK-12-NEXT: Type: Record +// CHECK-12-NEXT: Name: 'E' +// CHECK-12-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-12-NEXT: ReturnType: +// CHECK-12-NEXT: Type: +// CHECK-12-NEXT: Name: 'void' +// CHECK-12-NEXT: ... + +// RUN: cat %t/docs/E/~E.yaml | FileCheck %s --check-prefix CHECK-13 +// CHECK-13: --- +// CHECK-13-NEXT: USR: 'BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17' +// CHECK-13-NEXT: Name: '~E' +// CHECK-13-NEXT: Namespace: +// CHECK-13-NEXT: - Type: Record +// CHECK-13-NEXT: Name: 'E' +// CHECK-13-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-13-NEXT: DefLocation: +// CHECK-13-NEXT: LineNumber: 28 +// CHECK-13-NEXT: Filename: 'test' +// CHECK-13-NEXT: IsMethod: true +// CHECK-13-NEXT: Parent: +// CHECK-13-NEXT: Type: Record +// CHECK-13-NEXT: Name: 'E' +// CHECK-13-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-13-NEXT: ReturnType: +// CHECK-13-NEXT: Type: +// CHECK-13-NEXT: Name: 'void' +// CHECK-13-NEXT: ... From ca69f0ef01b2cc199217cc6f4de0ebf436d9216b Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Thu, 2 Aug 2018 20:10:17 +0000 Subject: [PATCH 006/686] Reland "[clang-doc] Refactoring mapper to map by scope" Relanding with a minor change to prevent an assertion on release bots. The result of this adjusted mapper pass is that all Function and Enum infos are absorbed into the info of their enclosing scope (i.e. the class or namespace in which they are defined). Namespace and Record infos are passed along to the final output, but the second pass creates a reference to each in its parent scope. As a result, the top-level final outputs are Namespaces and Records. Differential Revision: https://reviews.llvm.org/D48341 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338763 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/BitcodeReader.cpp | 85 ++- clang-doc/BitcodeReader.h | 3 +- clang-doc/BitcodeWriter.cpp | 14 + clang-doc/BitcodeWriter.h | 26 +- clang-doc/Mapper.cpp | 12 +- clang-doc/Representation.cpp | 72 ++- clang-doc/Representation.h | 37 ++ clang-doc/Serialize.cpp | 142 +++-- clang-doc/Serialize.h | 22 +- clang-doc/YAMLGenerator.cpp | 14 +- clang-doc/gen_tests.py | 20 +- clang-doc/tool/ClangDocMain.cpp | 82 ++- test/clang-doc/bc-comment.cpp | 282 ++++----- test/clang-doc/bc-linkage.cpp | 844 ++++++++++++++++++++++++++ test/clang-doc/bc-module.cpp | 87 +++ test/clang-doc/bc-namespace.cpp | 172 +++--- test/clang-doc/bc-record.cpp | 390 ++++++------ test/clang-doc/mapper-comment.cpp | 72 +-- test/clang-doc/mapper-linkage.cpp | 402 ++++++++++++ test/clang-doc/mapper-module.cpp | 51 ++ test/clang-doc/mapper-namespace.cpp | 154 ++--- test/clang-doc/mapper-record.cpp | 327 ++++------ test/clang-doc/module.cpp | 61 -- test/clang-doc/public-comment.cpp | 138 +++++ test/clang-doc/public-linkage.cpp | 299 +++++++++ test/clang-doc/public-module.cpp | 84 ++- test/clang-doc/public-namespace.cpp | 96 +++ test/clang-doc/public-record.cpp | 208 +++++++ test/clang-doc/public-records.cpp | 341 ----------- test/clang-doc/test_cases/linkage.cpp | 95 +++ test/clang-doc/test_cases/module.cpp | 15 + test/clang-doc/yaml-comment.cpp | 176 +++--- test/clang-doc/yaml-linkage.cpp | 529 ++++++++++++++++ test/clang-doc/yaml-module.cpp | 63 ++ test/clang-doc/yaml-namespace.cpp | 123 ++-- test/clang-doc/yaml-record.cpp | 246 ++++---- 36 files changed, 4152 insertions(+), 1632 deletions(-) create mode 100644 test/clang-doc/bc-linkage.cpp create mode 100644 test/clang-doc/bc-module.cpp create mode 100644 test/clang-doc/mapper-linkage.cpp create mode 100644 test/clang-doc/mapper-module.cpp delete mode 100644 test/clang-doc/module.cpp create mode 100644 test/clang-doc/public-comment.cpp create mode 100644 test/clang-doc/public-linkage.cpp create mode 100644 test/clang-doc/public-namespace.cpp create mode 100644 test/clang-doc/public-record.cpp delete mode 100644 test/clang-doc/public-records.cpp create mode 100644 test/clang-doc/test_cases/linkage.cpp create mode 100644 test/clang-doc/test_cases/module.cpp create mode 100644 test/clang-doc/yaml-linkage.cpp create mode 100644 test/clang-doc/yaml-module.cpp diff --git a/clang-doc/BitcodeReader.cpp b/clang-doc/BitcodeReader.cpp index fa51d0135..7acf107db 100644 --- a/clang-doc/BitcodeReader.cpp +++ b/clang-doc/BitcodeReader.cpp @@ -100,6 +100,8 @@ bool decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { case FieldId::F_parent: case FieldId::F_vparent: case FieldId::F_type: + case FieldId::F_child_namespace: + case FieldId::F_child_record: case FieldId::F_default: Field = F; return true; @@ -372,6 +374,12 @@ template <> void addReference(NamespaceInfo *I, Reference &&R, FieldId F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); break; + case FieldId::F_child_namespace: + I->ChildNamespaces.emplace_back(std::move(R)); + break; + case FieldId::F_child_record: + I->ChildRecords.emplace_back(std::move(R)); + break; default: llvm::errs() << "Invalid field type for info.\n"; exit(1); @@ -403,12 +411,37 @@ template <> void addReference(RecordInfo *I, Reference &&R, FieldId F) { case FieldId::F_vparent: I->VirtualParents.emplace_back(std::move(R)); break; + case FieldId::F_child_record: + I->ChildRecords.emplace_back(std::move(R)); + break; default: llvm::errs() << "Invalid field type for info.\n"; exit(1); } } +template +void addChild(T I, ChildInfoType &&R) { + llvm::errs() << "Invalid child type for info.\n"; + exit(1); +} + +template <> void addChild(NamespaceInfo *I, FunctionInfo &&R) { + I->ChildFunctions.emplace_back(std::move(R)); +} + +template <> void addChild(NamespaceInfo *I, EnumInfo &&R) { + I->ChildEnums.emplace_back(std::move(R)); +} + +template <> void addChild(RecordInfo *I, FunctionInfo &&R) { + I->ChildFunctions.emplace_back(std::move(R)); +} + +template <> void addChild(RecordInfo *I, EnumInfo &&R) { + I->ChildEnums.emplace_back(std::move(R)); +} + // Read records from bitcode into a given info. template bool ClangDocBitcodeReader::readRecord(unsigned ID, T I) { Record R; @@ -455,7 +488,8 @@ template bool ClangDocBitcodeReader::readBlock(unsigned ID, T I) { template bool ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) { switch (ID) { - // Blocks can only have Comment, Reference, or TypeInfo subblocks + // Blocks can only have Comment, Reference, TypeInfo, FunctionInfo, or + // EnumInfo subblocks case BI_COMMENT_BLOCK_ID: if (readBlock(ID, getCommentInfo(I))) return true; @@ -492,6 +526,22 @@ bool ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) { } return false; } + case BI_FUNCTION_BLOCK_ID: { + FunctionInfo F; + if (readBlock(ID, &F)) { + addChild(I, std::move(F)); + return true; + } + return false; + } + case BI_ENUM_BLOCK_ID: { + EnumInfo E; + if (readBlock(ID, &E)) { + addChild(I, std::move(E)); + return true; + } + return false; + } default: llvm::errs() << "Invalid subblock type.\n"; return false; @@ -573,16 +623,21 @@ std::unique_ptr ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { } // Entry point -std::vector> ClangDocBitcodeReader::readBitcode() { +llvm::Expected>> +ClangDocBitcodeReader::readBitcode() { std::vector> Infos; if (!validateStream()) - return Infos; + return llvm::make_error("Invalid bitcode stream.\n", + llvm::inconvertibleErrorCode()); + ; // Read the top level blocks. while (!Stream.AtEndOfStream()) { unsigned Code = Stream.ReadCode(); if (Code != llvm::bitc::ENTER_SUBBLOCK) - return Infos; + return llvm::make_error( + "Missing subblock in bitcode.\n", llvm::inconvertibleErrorCode()); + ; unsigned ID = Stream.ReadSubBlockID(); switch (ID) { @@ -592,30 +647,36 @@ std::vector> ClangDocBitcodeReader::readBitcode() { case BI_MEMBER_TYPE_BLOCK_ID: case BI_COMMENT_BLOCK_ID: case BI_REFERENCE_BLOCK_ID: - llvm::errs() << "Invalid top level block.\n"; - return Infos; + return llvm::make_error( + "Invalid top level block in bitcode.\n", + llvm::inconvertibleErrorCode()); + ; case BI_NAMESPACE_BLOCK_ID: case BI_RECORD_BLOCK_ID: case BI_ENUM_BLOCK_ID: case BI_FUNCTION_BLOCK_ID: - if (std::unique_ptr I = readBlockToInfo(ID)) { + if (std::unique_ptr I = readBlockToInfo(ID)) Infos.emplace_back(std::move(I)); - } - return Infos; + continue; case BI_VERSION_BLOCK_ID: if (readBlock(ID, VersionNumber)) continue; - return Infos; + return llvm::make_error( + "Invalid bitcode version in bitcode.\n", + llvm::inconvertibleErrorCode()); + ; case llvm::bitc::BLOCKINFO_BLOCK_ID: if (readBlockInfoBlock()) continue; - return Infos; + return llvm::make_error( + "Invalid BlockInfo in bitcode.\n", llvm::inconvertibleErrorCode()); + ; default: if (!Stream.SkipBlock()) continue; } } - return Infos; + return std::move(Infos); } } // namespace doc diff --git a/clang-doc/BitcodeReader.h b/clang-doc/BitcodeReader.h index c0cf24a17..aaf25257c 100644 --- a/clang-doc/BitcodeReader.h +++ b/clang-doc/BitcodeReader.h @@ -19,6 +19,7 @@ #include "BitcodeWriter.h" #include "Representation.h" #include "clang/AST/AST.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Bitcode/BitstreamReader.h" @@ -31,7 +32,7 @@ class ClangDocBitcodeReader { ClangDocBitcodeReader(llvm::BitstreamCursor &Stream) : Stream(Stream) {} // Main entry point, calls readBlock to read each block in the given stream. - std::vector> readBitcode(); + llvm::Expected>> readBitcode(); private: enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin }; diff --git a/clang-doc/BitcodeWriter.cpp b/clang-doc/BitcodeWriter.cpp index 623ed1a2a..f73724e4f 100644 --- a/clang-doc/BitcodeWriter.cpp +++ b/clang-doc/BitcodeWriter.cpp @@ -434,6 +434,14 @@ void ClangDocBitcodeWriter::emitBlock(const NamespaceInfo &I) { emitBlock(N, FieldId::F_namespace); for (const auto &CI : I.Description) emitBlock(CI); + for (const auto &C : I.ChildNamespaces) + emitBlock(C, FieldId::F_child_namespace); + for (const auto &C : I.ChildRecords) + emitBlock(C, FieldId::F_child_record); + for (const auto &C : I.ChildFunctions) + emitBlock(C); + for (const auto &C : I.ChildEnums) + emitBlock(C); } void ClangDocBitcodeWriter::emitBlock(const EnumInfo &I) { @@ -472,6 +480,12 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) { emitBlock(P, FieldId::F_parent); for (const auto &P : I.VirtualParents) emitBlock(P, FieldId::F_vparent); + for (const auto &C : I.ChildRecords) + emitBlock(C, FieldId::F_child_record); + for (const auto &C : I.ChildFunctions) + emitBlock(C); + for (const auto &C : I.ChildEnums) + emitBlock(C); } void ClangDocBitcodeWriter::emitBlock(const FunctionInfo &I) { diff --git a/clang-doc/BitcodeWriter.h b/clang-doc/BitcodeWriter.h index 8aa38e142..2ff46c612 100644 --- a/clang-doc/BitcodeWriter.h +++ b/clang-doc/BitcodeWriter.h @@ -68,11 +68,10 @@ enum BlockId { // New Ids need to be added to the enum here, and to the relevant IdNameMap and // initialization list in the implementation file. -#define INFORECORDS(X) X##_USR, X##_NAME - enum RecordId { VERSION = 1, - INFORECORDS(FUNCTION), + FUNCTION_USR, + FUNCTION_NAME, FUNCTION_DEFLOCATION, FUNCTION_LOCATION, FUNCTION_ACCESS, @@ -91,13 +90,16 @@ enum RecordId { FIELD_TYPE_NAME, MEMBER_TYPE_NAME, MEMBER_TYPE_ACCESS, - INFORECORDS(NAMESPACE), - INFORECORDS(ENUM), + NAMESPACE_USR, + NAMESPACE_NAME, + ENUM_USR, + ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION, ENUM_MEMBER, ENUM_SCOPED, - INFORECORDS(RECORD), + RECORD_USR, + RECORD_NAME, RECORD_DEFLOCATION, RECORD_LOCATION, RECORD_TAG_TYPE, @@ -112,10 +114,16 @@ enum RecordId { static constexpr unsigned BlockIdCount = BI_LAST - BI_FIRST; static constexpr unsigned RecordIdCount = RI_LAST - RI_FIRST; -#undef INFORECORDS - // Identifiers for differentiating between subblocks -enum class FieldId { F_default, F_namespace, F_parent, F_vparent, F_type }; +enum class FieldId { + F_default, + F_namespace, + F_parent, + F_vparent, + F_type, + F_child_namespace, + F_child_record +}; class ClangDocBitcodeWriter { public: diff --git a/clang-doc/Mapper.cpp b/clang-doc/Mapper.cpp index fb0b42af3..4456c1ed9 100644 --- a/clang-doc/Mapper.cpp +++ b/clang-doc/Mapper.cpp @@ -13,6 +13,7 @@ #include "clang/AST/Comment.h" #include "clang/Index/USRGeneration.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Error.h" using clang::comments::FullComment; @@ -33,14 +34,15 @@ template bool MapASTVisitor::mapDecl(const T *D) { if (index::generateUSRForDecl(D, USR)) return true; - std::string info = serialize::emitInfo( + auto I = serialize::emitInfo( D, getComment(D, D->getASTContext()), getLine(D, D->getASTContext()), getFile(D, D->getASTContext()), CDCtx.PublicOnly); - if (info != "") - CDCtx.ECtx->reportResult( - llvm::toHex(llvm::toStringRef(serialize::hashUSR(USR))), info); - + // A null in place of I indicates that the serializer is skipping this decl + // for some reason (e.g. we're only reporting public decls). + if (I) + CDCtx.ECtx->reportResult(llvm::toHex(llvm::toStringRef(I->USR)), + serialize::serialize(I)); return true; } diff --git a/clang-doc/Representation.cpp b/clang-doc/Representation.cpp index 6107b98ec..eacf11a85 100644 --- a/clang-doc/Representation.cpp +++ b/clang-doc/Representation.cpp @@ -26,17 +26,70 @@ namespace clang { namespace doc { -static const SymbolID EmptySID = SymbolID(); +namespace { + +const SymbolID EmptySID = SymbolID(); template -std::unique_ptr reduce(std::vector> &Values) { - std::unique_ptr Merged = llvm::make_unique(); +llvm::Expected> +reduce(std::vector> &Values) { + if (Values.empty()) + return llvm::make_error(" No values to reduce.\n", + llvm::inconvertibleErrorCode()); + std::unique_ptr Merged = llvm::make_unique(Values[0]->USR); T *Tmp = static_cast(Merged.get()); for (auto &I : Values) Tmp->merge(std::move(*static_cast(I.get()))); - return Merged; + return std::move(Merged); +} + +// Return the index of the matching child in the vector, or -1 if merge is not +// necessary. +template +int getChildIndexIfExists(std::vector &Children, T &ChildToMerge) { + for (unsigned long I = 0; I < Children.size(); I++) { + if (ChildToMerge.USR == Children[I].USR) + return I; + } + return -1; +} + +// For References, we don't need to actually merge them, we just don't want +// duplicates. +void reduceChildren(std::vector &Children, + std::vector &&ChildrenToMerge) { + for (auto &ChildToMerge : ChildrenToMerge) { + if (getChildIndexIfExists(Children, ChildToMerge) == -1) + Children.push_back(std::move(ChildToMerge)); + } +} + +void reduceChildren(std::vector &Children, + std::vector &&ChildrenToMerge) { + for (auto &ChildToMerge : ChildrenToMerge) { + int mergeIdx = getChildIndexIfExists(Children, ChildToMerge); + if (mergeIdx == -1) { + Children.push_back(std::move(ChildToMerge)); + continue; + } + Children[mergeIdx].merge(std::move(ChildToMerge)); + } } +void reduceChildren(std::vector &Children, + std::vector &&ChildrenToMerge) { + for (auto &ChildToMerge : ChildrenToMerge) { + int mergeIdx = getChildIndexIfExists(Children, ChildToMerge); + if (mergeIdx == -1) { + Children.push_back(std::move(ChildToMerge)); + continue; + } + Children[mergeIdx].merge(std::move(ChildToMerge)); + } +} + +} // namespace + // Dispatch function. llvm::Expected> mergeInfos(std::vector> &Values) { @@ -73,7 +126,7 @@ void Info::mergeBase(Info &&Other) { } bool Info::mergeable(const Info &Other) { - return IT == Other.IT && (USR == EmptySID || USR == Other.USR); + return IT == Other.IT && USR == Other.USR; } void SymbolInfo::merge(SymbolInfo &&Other) { @@ -87,6 +140,11 @@ void SymbolInfo::merge(SymbolInfo &&Other) { void NamespaceInfo::merge(NamespaceInfo &&Other) { assert(mergeable(Other)); + // Reduce children if necessary. + reduceChildren(ChildNamespaces, std::move(Other.ChildNamespaces)); + reduceChildren(ChildRecords, std::move(Other.ChildRecords)); + reduceChildren(ChildFunctions, std::move(Other.ChildFunctions)); + reduceChildren(ChildEnums, std::move(Other.ChildEnums)); mergeBase(std::move(Other)); } @@ -100,6 +158,10 @@ void RecordInfo::merge(RecordInfo &&Other) { Parents = std::move(Other.Parents); if (VirtualParents.empty()) VirtualParents = std::move(Other.VirtualParents); + // Reduce children if necessary. + reduceChildren(ChildRecords, std::move(Other.ChildRecords)); + reduceChildren(ChildFunctions, std::move(Other.ChildFunctions)); + reduceChildren(ChildEnums, std::move(Other.ChildEnums)); SymbolInfo::merge(std::move(Other)); } diff --git a/clang-doc/Representation.h b/clang-doc/Representation.h index f952954f9..9e88bd9c8 100644 --- a/clang-doc/Representation.h +++ b/clang-doc/Representation.h @@ -31,6 +31,9 @@ namespace doc { using SymbolID = std::array; struct Info; +struct FunctionInfo; +struct EnumInfo; + enum class InfoType { IT_default, IT_namespace, @@ -153,6 +156,9 @@ struct Location { struct Info { Info() = default; Info(InfoType IT) : IT(IT) {} + Info(InfoType IT, SymbolID USR) : USR(USR), IT(IT) {} + Info(InfoType IT, SymbolID USR, StringRef Name) + : USR(USR), IT(IT), Name(Name) {} Info(const Info &Other) = delete; Info(Info &&Other) = default; @@ -166,18 +172,36 @@ struct Info { void mergeBase(Info &&I); bool mergeable(const Info &Other); + + // Returns a reference to the parent scope (that is, the immediate parent + // namespace or class in which this decl resides). + llvm::Expected getEnclosingScope(); }; // Info for namespaces. struct NamespaceInfo : public Info { NamespaceInfo() : Info(InfoType::IT_namespace) {} + NamespaceInfo(SymbolID USR) : Info(InfoType::IT_namespace, USR) {} + NamespaceInfo(SymbolID USR, StringRef Name) + : Info(InfoType::IT_namespace, USR, Name) {} void merge(NamespaceInfo &&I); + + // Namespaces and Records are references because they will be properly + // documented in their own info, while the entirety of Functions and Enums are + // included here because they should not have separate documentation from + // their scope. + std::vector ChildNamespaces; + std::vector ChildRecords; + std::vector ChildFunctions; + std::vector ChildEnums; }; // Info for symbols. struct SymbolInfo : public Info { SymbolInfo(InfoType IT) : Info(IT) {} + SymbolInfo(InfoType IT, SymbolID USR) : Info(IT, USR) {} + SymbolInfo(InfoType IT, SymbolID USR, StringRef Name) : Info(IT, USR, Name) {} void merge(SymbolInfo &&I); @@ -189,6 +213,7 @@ struct SymbolInfo : public Info { // Info for functions. struct FunctionInfo : public SymbolInfo { FunctionInfo() : SymbolInfo(InfoType::IT_function) {} + FunctionInfo(SymbolID USR) : SymbolInfo(InfoType::IT_function, USR) {} void merge(FunctionInfo &&I); @@ -205,6 +230,9 @@ struct FunctionInfo : public SymbolInfo { // Info for types. struct RecordInfo : public SymbolInfo { RecordInfo() : SymbolInfo(InfoType::IT_record) {} + RecordInfo(SymbolID USR) : SymbolInfo(InfoType::IT_record, USR) {} + RecordInfo(SymbolID USR, StringRef Name) + : SymbolInfo(InfoType::IT_record, USR, Name) {} void merge(RecordInfo &&I); @@ -218,12 +246,21 @@ struct RecordInfo : public SymbolInfo { // parents). llvm::SmallVector VirtualParents; // List of virtual base/parent records. + + // Records are references because they will be properly + // documented in their own info, while the entirety of Functions and Enums are + // included here because they should not have separate documentation from + // their scope. + std::vector ChildRecords; + std::vector ChildFunctions; + std::vector ChildEnums; }; // TODO: Expand to allow for documenting templating. // Info for types. struct EnumInfo : public SymbolInfo { EnumInfo() : SymbolInfo(InfoType::IT_enum) {} + EnumInfo(SymbolID USR) : SymbolInfo(InfoType::IT_enum, USR) {} void merge(EnumInfo &&I); diff --git a/clang-doc/Serialize.cpp b/clang-doc/Serialize.cpp index c1e6d316f..b12961463 100644 --- a/clang-doc/Serialize.cpp +++ b/clang-doc/Serialize.cpp @@ -152,6 +152,21 @@ template static std::string serialize(T &I) { return Buffer.str().str(); } +std::string serialize(std::unique_ptr &I) { + switch (I->IT) { + case InfoType::IT_namespace: + return serialize(*static_cast(I.get())); + case InfoType::IT_record: + return serialize(*static_cast(I.get())); + case InfoType::IT_enum: + return serialize(*static_cast(I.get())); + case InfoType::IT_function: + return serialize(*static_cast(I.get())); + default: + return ""; + } +} + static void parseFullComment(const FullComment *C, CommentInfo &CI) { ClangDocCommentVisitor Visitor(CI); Visitor.parseComment(C); @@ -306,61 +321,108 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D, parseParameters(I, D); } -std::string emitInfo(const NamespaceDecl *D, const FullComment *FC, - int LineNumber, llvm::StringRef File, bool PublicOnly) { +std::unique_ptr emitInfo(const NamespaceDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, + bool PublicOnly) { if (PublicOnly && ((D->isAnonymousNamespace()) || !isPublic(D->getAccess(), D->getLinkageInternal()))) - return ""; - NamespaceInfo I; - populateInfo(I, D, FC); - return serialize(I); + return nullptr; + auto I = llvm::make_unique(); + populateInfo(*I, D, FC); + return I; } -std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool PublicOnly) { +std::unique_ptr emitInfo(const RecordDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, + bool PublicOnly) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) - return ""; - RecordInfo I; - populateSymbolInfo(I, D, FC, LineNumber, File); - I.TagType = D->getTagKind(); - parseFields(I, D, PublicOnly); + return nullptr; + auto I = llvm::make_unique(); + populateSymbolInfo(*I, D, FC, LineNumber, File); + I->TagType = D->getTagKind(); + parseFields(*I, D, PublicOnly); if (const auto *C = dyn_cast(D)) - parseBases(I, C); - return serialize(I); + parseBases(*I, C); + return I; } -std::string emitInfo(const FunctionDecl *D, const FullComment *FC, - int LineNumber, llvm::StringRef File, bool PublicOnly) { +std::unique_ptr emitInfo(const FunctionDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, + bool PublicOnly) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) - return ""; - FunctionInfo I; - populateFunctionInfo(I, D, FC, LineNumber, File); - I.Access = clang::AccessSpecifier::AS_none; - return serialize(I); + return nullptr; + FunctionInfo Func; + populateFunctionInfo(Func, D, FC, LineNumber, File); + Func.Access = clang::AccessSpecifier::AS_none; + + // Wrap in enclosing scope + auto I = llvm::make_unique(); + if (!Func.Namespace.empty()) + I->USR = Func.Namespace[0].USR; + else + I->USR = SymbolID(); + I->ChildFunctions.push_back(std::move(Func)); + return I; } -std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC, - int LineNumber, llvm::StringRef File, bool PublicOnly) { +std::unique_ptr emitInfo(const CXXMethodDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, + bool PublicOnly) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) - return ""; - FunctionInfo I; - populateFunctionInfo(I, D, FC, LineNumber, File); - I.IsMethod = true; - I.Parent = Reference{getUSRForDecl(D->getParent()), - D->getParent()->getNameAsString(), InfoType::IT_record}; - I.Access = D->getAccess(); - return serialize(I); + return nullptr; + FunctionInfo Func; + populateFunctionInfo(Func, D, FC, LineNumber, File); + Func.IsMethod = true; + + SymbolID ParentUSR = getUSRForDecl(D->getParent()); + Func.Parent = Reference{ParentUSR, D->getParent()->getNameAsString(), + InfoType::IT_record}; + Func.Access = D->getAccess(); + + // Wrap in enclosing scope + auto I = llvm::make_unique(); + I->USR = ParentUSR; + I->ChildFunctions.push_back(std::move(Func)); + return I; } -std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, - llvm::StringRef File, bool PublicOnly) { +std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, + int LineNumber, llvm::StringRef File, + bool PublicOnly) { if (PublicOnly && !isPublic(D->getAccess(), D->getLinkageInternal())) - return ""; - EnumInfo I; - populateSymbolInfo(I, D, FC, LineNumber, File); - I.Scoped = D->isScoped(); - parseEnumerators(I, D); - return serialize(I); + return nullptr; + EnumInfo Enum; + populateSymbolInfo(Enum, D, FC, LineNumber, File); + Enum.Scoped = D->isScoped(); + parseEnumerators(Enum, D); + + // Wrap in enclosing scope + if (!Enum.Namespace.empty()) { + switch (Enum.Namespace[0].RefType) { + case InfoType::IT_namespace: { + std::unique_ptr IPtr = llvm::make_unique(); + NamespaceInfo *I = static_cast(IPtr.get()); + I->USR = Enum.Namespace[0].USR; + I->ChildEnums.push_back(std::move(Enum)); + return IPtr; + } + case InfoType::IT_record: { + std::unique_ptr IPtr = llvm::make_unique(); + RecordInfo *I = static_cast(IPtr.get()); + I->USR = Enum.Namespace[0].USR; + I->ChildEnums.push_back(std::move(Enum)); + return IPtr; + } + default: + break; + } + } + + // Put in global namespace + auto I = llvm::make_unique(); + I->USR = SymbolID(); + I->ChildEnums.push_back(std::move(Enum)); + return I; } } // namespace serialize diff --git a/clang-doc/Serialize.h b/clang-doc/Serialize.h index 5181cf61b..d89dac809 100644 --- a/clang-doc/Serialize.h +++ b/clang-doc/Serialize.h @@ -28,16 +28,16 @@ namespace clang { namespace doc { namespace serialize { -std::string emitInfo(const NamespaceDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); -std::string emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool PublicOnly); -std::string emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber, - StringRef File, bool PublicOnly); -std::string emitInfo(const FunctionDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); -std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC, - int LineNumber, StringRef File, bool PublicOnly); +std::unique_ptr emitInfo(const NamespaceDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); +std::unique_ptr emitInfo(const RecordDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); +std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); +std::unique_ptr emitInfo(const FunctionDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); +std::unique_ptr emitInfo(const CXXMethodDecl *D, const FullComment *FC, + int LineNumber, StringRef File, bool PublicOnly); // Function to hash a given USR value for storage. // As USRs (Unified Symbol Resolution) could be large, especially for functions @@ -46,6 +46,8 @@ std::string emitInfo(const CXXMethodDecl *D, const FullComment *FC, // memory (vs storing USRs directly). SymbolID hashUSR(llvm::StringRef USR); +std::string serialize(std::unique_ptr &I); + } // namespace serialize } // namespace doc } // namespace clang diff --git a/clang-doc/YAMLGenerator.cpp b/clang-doc/YAMLGenerator.cpp index f29b4787d..58c1e1f36 100644 --- a/clang-doc/YAMLGenerator.cpp +++ b/clang-doc/YAMLGenerator.cpp @@ -20,6 +20,8 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(MemberTypeInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(Reference) LLVM_YAML_IS_SEQUENCE_VECTOR(Location) LLVM_YAML_IS_SEQUENCE_VECTOR(CommentInfo) +LLVM_YAML_IS_SEQUENCE_VECTOR(FunctionInfo) +LLVM_YAML_IS_SEQUENCE_VECTOR(EnumInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::SmallString<16>) @@ -175,7 +177,14 @@ template <> struct MappingTraits { }; template <> struct MappingTraits { - static void mapping(IO &IO, NamespaceInfo &I) { InfoMapping(IO, I); } + static void mapping(IO &IO, NamespaceInfo &I) { + InfoMapping(IO, I); + IO.mapOptional("ChildNamespaces", I.ChildNamespaces, + std::vector()); + IO.mapOptional("ChildRecords", I.ChildRecords, std::vector()); + IO.mapOptional("ChildFunctions", I.ChildFunctions); + IO.mapOptional("ChildEnums", I.ChildEnums); + } }; template <> struct MappingTraits { @@ -186,6 +195,9 @@ template <> struct MappingTraits { IO.mapOptional("Parents", I.Parents, llvm::SmallVector()); IO.mapOptional("VirtualParents", I.VirtualParents, llvm::SmallVector()); + IO.mapOptional("ChildRecords", I.ChildRecords, std::vector()); + IO.mapOptional("ChildFunctions", I.ChildFunctions); + IO.mapOptional("ChildEnums", I.ChildEnums); } }; diff --git a/clang-doc/gen_tests.py b/clang-doc/gen_tests.py index 5004892e5..ccdb069c4 100644 --- a/clang-doc/gen_tests.py +++ b/clang-doc/gen_tests.py @@ -18,14 +18,17 @@ To generate all current tests: - Generate mapper tests: - gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -prefix mapper + python gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix mapper - Generate reducer tests: - gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -prefix bc - + python gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix bc + - Generate yaml tests: - gen_tests.py -flag='--format=yaml' -flag='--doxygen' -prefix yaml - + python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix yaml + +- Generate public decl tests: + python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix public + This script was written on/for Linux, and has not been tested on any other platform and so it may not work. @@ -34,6 +37,7 @@ import argparse import glob import os +import re import shutil import subprocess @@ -48,6 +52,10 @@ CHECK_NEXT = '// CHECK-{0}-NEXT: ' +BITCODE_USR = '' +BITCODE_USR_REGEX = r'' +YAML_USR = "USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}'" +YAML_USR_REGEX = r"USR: '[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]'" def clear_test_prefix_files(prefix, tests_path): if os.path.isdir(tests_path): @@ -108,6 +116,8 @@ def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer): # Format output. output = output.replace('blob data = \'test\'', 'blob data = \'{{.*}}\'') + output = re.sub(YAML_USR_REGEX, YAML_USR, output) + output = re.sub(BITCODE_USR_REGEX, BITCODE_USR, output) output = CHECK.format(checkname) + output.rstrip() output = run_cmd + output.replace('\n', '\n' + CHECK_NEXT.format(checkname)) diff --git a/clang-doc/tool/ClangDocMain.cpp b/clang-doc/tool/ClangDocMain.cpp index 9e89e8568..6e4a92d7b 100644 --- a/clang-doc/tool/ClangDocMain.cpp +++ b/clang-doc/tool/ClangDocMain.cpp @@ -121,9 +121,24 @@ bool DumpResultToFile(const Twine &DirName, const Twine &FileName, return false; } +// A function to extract the appropriate path name for a given info's +// documentation. The path returned is a composite of the parent namespaces as +// directories plus the decl name as the filename. +// +// Example: Given the below, the path for class C will be < +// root>/A/B/C. +// +// namespace A { +// namesapce B { +// +// class C {}; +// +// } +// } llvm::Expected> -getPath(StringRef Root, StringRef Ext, StringRef Name, - llvm::SmallVectorImpl &Namespaces) { +getInfoOutputFile(StringRef Root, + llvm::SmallVectorImpl &Namespaces, + StringRef Name, StringRef Ext) { std::error_code OK; llvm::SmallString<128> Path; llvm::sys::path::native(Root, Path); @@ -134,6 +149,8 @@ getPath(StringRef Root, StringRef Ext, StringRef Name, return llvm::make_error("Unable to create directory.\n", llvm::inconvertibleErrorCode()); + if (Name.empty()) + Name = "GlobalNamespace"; llvm::sys::path::append(Path, Name + Ext); return Path; } @@ -146,6 +163,30 @@ std::string getFormatString(OutputFormatTy Ty) { llvm_unreachable("Unknown OutputFormatTy"); } +// Iterate through tool results and build string map of info vectors from the +// encoded bitstreams. +bool bitcodeResultsToInfos( + tooling::ToolResults &Results, + llvm::StringMap>> &Output) { + bool Err = false; + Results.forEachResult([&](StringRef Key, StringRef Value) { + llvm::BitstreamCursor Stream(Value); + doc::ClangDocBitcodeReader Reader(Stream); + auto Infos = Reader.readBitcode(); + if (!Infos) { + llvm::errs() << toString(Infos.takeError()) << "\n"; + Err = true; + return; + } + for (auto &I : Infos.get()) { + auto R = + Output.try_emplace(Key, std::vector>()); + R.first->second.emplace_back(std::move(I)); + } + }); + return Err; +} + int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); std::error_code OK; @@ -196,30 +237,21 @@ int main(int argc, const char **argv) { } // Collect values into output by key. - llvm::outs() << "Collecting infos...\n"; - llvm::StringMap>> MapOutput; - // In ToolResults, the Key is the hashed USR and the value is the // bitcode-encoded representation of the Info object. - Exec->get()->getToolResults()->forEachResult([&](StringRef Key, - StringRef Value) { - llvm::BitstreamCursor Stream(Value); - doc::ClangDocBitcodeReader Reader(Stream); - auto Infos = Reader.readBitcode(); - for (auto &I : Infos) { - auto R = - MapOutput.try_emplace(Key, std::vector>()); - R.first->second.emplace_back(std::move(I)); - } - }); + llvm::outs() << "Collecting infos...\n"; + llvm::StringMap>> USRToInfos; + if (bitcodeResultsToInfos(*Exec->get()->getToolResults(), USRToInfos)) + return 1; - // Reducing and generation phases - llvm::outs() << "Reducing " << MapOutput.size() << " infos...\n"; - llvm::StringMap> ReduceOutput; - for (auto &Group : MapOutput) { + // First reducing phase (reduce all decls into one info per decl). + llvm::outs() << "Reducing " << USRToInfos.size() << " infos...\n"; + for (auto &Group : USRToInfos) { auto Reduced = doc::mergeInfos(Group.getValue()); - if (!Reduced) + if (!Reduced) { llvm::errs() << llvm::toString(Reduced.takeError()); + continue; + } if (DumpIntermediateResult) { SmallString<4096> Buffer; @@ -230,10 +262,10 @@ int main(int argc, const char **argv) { llvm::errs() << "Error dumping to bitcode.\n"; continue; } - - // Create the relevant ostream and emit the documentation for this decl. doc::Info *I = Reduced.get().get(); - auto InfoPath = getPath(OutDirectory, "." + Format, I->Name, I->Namespace); + + auto InfoPath = + getInfoOutputFile(OutDirectory, I->Namespace, I->Name, "." + Format); if (!InfoPath) { llvm::errs() << toString(InfoPath.takeError()) << "\n"; continue; @@ -241,7 +273,7 @@ int main(int argc, const char **argv) { std::error_code FileErr; llvm::raw_fd_ostream InfoOS(InfoPath.get(), FileErr, llvm::sys::fs::F_None); if (FileErr != OK) { - llvm::errs() << "Error opening index file: " << FileErr.message() << "\n"; + llvm::errs() << "Error opening info file: " << FileErr.message() << "\n"; continue; } diff --git a/test/clang-doc/bc-comment.cpp b/test/clang-doc/bc-comment.cpp index fa3ea7f71..3b006ab8a 100644 --- a/test/clang-doc/bc-comment.cpp +++ b/test/clang-doc/bc-comment.cpp @@ -27,176 +27,178 @@ void F(int I, int J); /// Bonus comment on definition void F(int I, int J) {} -// RUN: clang-doc --dump-intermediate --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/7574630614A535710E5A6ABCFFF98BCA2D06A4CA.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'F' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'FullComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'BlockCommandComment' -// CHECK-0-NEXT: blob data = 'brief' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'F' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'FullComment' +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Brief description.' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Extended description that' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' continues onto the next line.' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'HTMLStartTagComment' -// CHECK-0-NEXT: blob data = 'ul' -// CHECK-0-NEXT: blob data = 'class' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'HTMLStartTagComment' -// CHECK-0-NEXT: blob data = 'li' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Testing.' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'HTMLEndTagComment' -// CHECK-0-NEXT: blob data = 'ul' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'VerbatimBlockComment' -// CHECK-0-NEXT: blob data = 'verbatim' -// CHECK-0-NEXT: blob data = 'endverbatim' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'VerbatimBlockLineComment' -// CHECK-0-NEXT: blob data = ' The description continues.' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' --' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'BlockCommandComment' +// CHECK-0-NEXT: blob data = 'brief' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' Brief description.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' Extended description that' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' continues onto the next line.' +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParamCommandComment' -// CHECK-0-NEXT: blob data = '[out]' -// CHECK-0-NEXT: blob data = 'I' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'HTMLStartTagComment' +// CHECK-0-NEXT: blob data = 'ul' +// CHECK-0-NEXT: blob data = 'class' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'HTMLStartTagComment' +// CHECK-0-NEXT: blob data = 'li' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' is a parameter.' +// CHECK-0-NEXT: blob data = ' Testing.' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'HTMLEndTagComment' +// CHECK-0-NEXT: blob data = 'ul' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParamCommandComment' -// CHECK-0-NEXT: blob data = '[in]' -// CHECK-0-NEXT: blob data = 'J' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' is a parameter.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'VerbatimBlockComment' +// CHECK-0-NEXT: blob data = 'verbatim' +// CHECK-0-NEXT: blob data = 'endverbatim' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'VerbatimBlockLineComment' +// CHECK-0-NEXT: blob data = ' The description continues.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' --' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: // CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParamCommandComment' +// CHECK-0-NEXT: blob data = '[out]' +// CHECK-0-NEXT: blob data = 'I' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' is a parameter.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParamCommandComment' +// CHECK-0-NEXT: blob data = '[in]' +// CHECK-0-NEXT: blob data = 'J' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' is a parameter.' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'BlockCommandComment' +// CHECK-0-NEXT: blob data = 'return' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'BlockCommandComment' -// CHECK-0-NEXT: blob data = 'return' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'FullComment' +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' void' +// CHECK-0-NEXT: blob data = ' Bonus comment on definition' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'FullComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Bonus comment on definition' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'I' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'J' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'I' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'J' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: diff --git a/test/clang-doc/bc-linkage.cpp b/test/clang-doc/bc-linkage.cpp new file mode 100644 index 000000000..8fec0d351 --- /dev/null +++ b/test/clang-doc/bc-linkage.cpp @@ -0,0 +1,844 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void function(int x); + +inline int inlinedFunction(int x); + +int functionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +inline int inlinedFunctionWithInnerClass(int x) { + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +class Class { +public: + void publicMethod(); + int publicField; + +protected: + void protectedMethod(); + int protectedField; + +private: + void privateMethod(); + int privateField; +}; + +namespace named { +class NamedClass { +public: + void namedPublicMethod(); + int namedPublicField; + +protected: + void namedProtectedMethod(); + int namedProtectedField; + +private: + void namedPrivateMethod(); + int namedPrivateField; +}; + +void namedFunction(); +static void namedStaticFunction(); +inline void namedInlineFunction(); +} // namespace named + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace { +class AnonClass { +public: + void anonPublicMethod(); + int anonPublicField; + +protected: + void anonProtectedMethod(); + int anonProtectedField; + +private: + void anonPrivateMethod(); + int anonPrivateField; +}; + +void anonFunction(); +static void anonStaticFunction(); +inline void anonInlineFunction(); +} // namespace + +// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/C9B3B71ACDD84C5BB320D34E97677715CDB3EA32.bc | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'InnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'inlinedFunctionWithInnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'innerPublicMethod' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'InnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'inlinedFunctionWithInnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'InnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/8960B5C9247D6F5C532756E53A1AD1240FA2146F.bc | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'named' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'namedFunction' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'named' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'void' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'namedStaticFunction' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'named' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'void' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'namedInlineFunction' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'named' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'void' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/74A031CBE68C101F3E83F60ED17F20C11EC19D48.bc | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'InnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'innerPublicMethod' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'InnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'InnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/7CDD73DCD6CD72F7E5CE25502810A182C66C4B45.bc | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'int' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'publicField' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'int' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'protectedField' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'int' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'privateField' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'publicMethod' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'void' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'protectedMethod' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'void' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'privateMethod' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'void' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/85427901413EC77C961019EBB3ADEF7B0BAAFE78.bc | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'functionWithInnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'innerPublicMethod' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'functionWithInnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'int' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 +// CHECK-5: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'function' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'void' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'inlinedFunction' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'functionWithInnerClass' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'inlinedFunctionWithInnerClass' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'staticFunction' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'void' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'staticFunctionWithInnerClass' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/4712C5FA37B298A25501D1033C619B65B0ECC449.bc | FileCheck %s --check-prefix CHECK-6 +// CHECK-6: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'named' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'int' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedPublicField' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'int' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedProtectedField' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'int' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedPrivateField' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedPublicMethod' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'named' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'void' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedProtectedMethod' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'named' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'void' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedPrivateMethod' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'named' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'void' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/6E8FB72A89761E77020BFCEE9A9A6E64B15CC2A9.bc | FileCheck %s --check-prefix CHECK-7 +// CHECK-7: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'int' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonPublicField' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'int' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonProtectedField' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'int' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonPrivateField' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonPublicMethod' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'void' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonProtectedMethod' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'void' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonPrivateMethod' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'void' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/83CC52D32583E0771710A7742DE81C839E953AC8.bc | FileCheck %s --check-prefix CHECK-8 +// CHECK-8: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'anonFunction' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'void' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'anonStaticFunction' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'void' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'anonInlineFunction' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'void' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: diff --git a/test/clang-doc/bc-module.cpp b/test/clang-doc/bc-module.cpp new file mode 100644 index 000000000..101d8da85 --- /dev/null +++ b/test/clang-doc/bc-module.cpp @@ -0,0 +1,87 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +export module M; + +int moduleFunction(int x); // ModuleLinkage + +static int staticModuleFunction(int x); // ModuleInternalLinkage + +export double exportedModuleFunction(double y, int z); // ExternalLinkage + +// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'moduleFunction' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'x' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'staticModuleFunction' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'x' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'exportedModuleFunction' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'double' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'double' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'y' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'z' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: diff --git a/test/clang-doc/bc-namespace.cpp b/test/clang-doc/bc-namespace.cpp index b1c03636c..79b35bd9a 100644 --- a/test/clang-doc/bc-namespace.cpp +++ b/test/clang-doc/bc-namespace.cpp @@ -25,115 +25,97 @@ E func(int i) { return X; } } // namespace B } // namespace A -// RUN: clang-doc --dump-intermediate --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'B' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'A' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: blob data = 'X' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'A' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'f' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'A' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC.bc | FileCheck %s --check-prefix CHECK-1 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'f' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'B' // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'A' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'void' -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'func' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'B' +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/9A82CB33ED0FDF81EE383D31CD0957D153C5E840.bc | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'func' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'B' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'A' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'enum A::B::E' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'int' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'i' -// CHECK-2-NEXT: -// CHECK-2-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'A' -// CHECK-3-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-4 -// CHECK-4: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'B' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'A' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'A' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'enum A::B::E' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'int' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'i' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'E' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'B' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'A' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: blob data = 'X' +// CHECK-1-NEXT: +// CHECK-1-NEXT: diff --git a/test/clang-doc/bc-record.cpp b/test/clang-doc/bc-record.cpp index 7a09118c7..a0e224485 100644 --- a/test/clang-doc/bc-record.cpp +++ b/test/clang-doc/bc-record.cpp @@ -39,7 +39,7 @@ class X { class Y {}; }; -// RUN: clang-doc --dump-intermediate --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs // RUN: llvm-bcanalyzer --dump %t/docs/bc/289584A8E0FF4178A794622A547AA622503967A1.bc | FileCheck %s --check-prefix CHECK-0 @@ -47,11 +47,84 @@ class X { // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' // CHECK-0-NEXT: blob data = '{{.*}}' // CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '~E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ProtectedMethod' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/3FB542274573CAEAD54CEBFFCAEE3D77FB9713D8.bc | FileCheck %s --check-prefix CHECK-1 @@ -60,10 +133,10 @@ class X { // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'I' // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'H' // CHECK-1-NEXT: // CHECK-1-NEXT: @@ -72,250 +145,149 @@ class X { // CHECK-1-NEXT: // CHECK-1-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/5093D428CDC62096A67547BA52566E4FB9404EEE.bc | FileCheck %s --check-prefix CHECK-2 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'ProtectedMethod' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'E' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'E' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'void' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'X' +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-3 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'X' -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'C' +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'int' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'i' +// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E.bc | FileCheck %s --check-prefix CHECK-4 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'H' -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'void' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'Y' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'X' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-5 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'C' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'i' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'H' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'void' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'B' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: blob data = 'X' +// CHECK-5-NEXT: blob data = 'Y' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'Bc' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'A' +// CHECK-5-NEXT: blob data = 'B' +// CHECK-5-NEXT: +// CHECK-5-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17.bc | FileCheck %s --check-prefix CHECK-6 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-6 // CHECK-6: // CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '~E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'void' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'D' +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4.bc | FileCheck %s --check-prefix CHECK-7 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-7 // CHECK-7: // CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'E' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'F' +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: blob data = 'E' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'E' +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'D' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'void' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-8 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-8 // CHECK-8: // CHECK-8-NEXT: // CHECK-8-NEXT: // CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'Y' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'X' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'A' +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'int' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'X' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'int' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'Y' +// CHECK-8-NEXT: +// CHECK-8-NEXT: // CHECK-8-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/FC07BD34D5E77782C263FA944447929EA8753740.bc | FileCheck %s --check-prefix CHECK-9 -// CHECK-9: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: blob data = 'B' -// CHECK-9-NEXT: blob data = '{{.*}}' -// CHECK-9-NEXT: blob data = 'X' -// CHECK-9-NEXT: blob data = 'Y' -// CHECK-9-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-10 -// CHECK-10: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: blob data = 'D' -// CHECK-10-NEXT: blob data = '{{.*}}' -// CHECK-10-NEXT: -// CHECK-10-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-11 -// CHECK-11: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: blob data = 'F' -// CHECK-11-NEXT: blob data = '{{.*}}' -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: blob data = 'E' -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: blob data = 'D' -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-12 -// CHECK-12: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'A' -// CHECK-12-NEXT: blob data = '{{.*}}' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'int' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'X' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'int' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'Y' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/1E3438A08BA22025C0B46289FF0686F92C8924C5.bc | FileCheck %s --check-prefix CHECK-13 -// CHECK-13: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: blob data = 'Bc' -// CHECK-13-NEXT: blob data = '{{.*}}' -// CHECK-13-NEXT: -// CHECK-13-NEXT: blob data = 'A' -// CHECK-13-NEXT: blob data = 'B' -// CHECK-13-NEXT: diff --git a/test/clang-doc/mapper-comment.cpp b/test/clang-doc/mapper-comment.cpp index da691b156..efd3dc54c 100644 --- a/test/clang-doc/mapper-comment.cpp +++ b/test/clang-doc/mapper-comment.cpp @@ -27,46 +27,48 @@ void F(int I, int J); /// Bonus comment on definition void F(int I, int J) {} -// RUN: clang-doc --dump-mapper --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/7574630614A535710E5A6ABCFFF98BCA2D06A4CA.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'F' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'FullComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'TextComment' -// CHECK-0-NEXT: blob data = ' Bonus comment on definition' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'F' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'FullComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ParagraphComment' +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'TextComment' +// CHECK-0-NEXT: blob data = ' Bonus comment on definition' +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'void' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'I' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'J' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'I' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'J' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: diff --git a/test/clang-doc/mapper-linkage.cpp b/test/clang-doc/mapper-linkage.cpp new file mode 100644 index 000000000..5b4fe7df3 --- /dev/null +++ b/test/clang-doc/mapper-linkage.cpp @@ -0,0 +1,402 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void function(int x); + +inline int inlinedFunction(int x); + +int functionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +inline int inlinedFunctionWithInnerClass(int x) { + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +class Class { +public: + void publicMethod(); + int publicField; + +protected: + void protectedMethod(); + int protectedField; + +private: + void privateMethod(); + int privateField; +}; + +namespace named { +class NamedClass { +public: + void namedPublicMethod(); + int namedPublicField; + +protected: + void namedProtectedMethod(); + int namedProtectedField; + +private: + void namedPrivateMethod(); + int namedPrivateField; +}; + +void namedFunction(); +static void namedStaticFunction(); +inline void namedInlineFunction(); +} // namespace named + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace { +class AnonClass { +public: + void anonPublicMethod(); + int anonPublicField; + +protected: + void anonProtectedMethod(); + int anonProtectedField; + +private: + void anonPrivateMethod(); + int anonPrivateField; +}; + +void anonFunction(); +static void anonStaticFunction(); +inline void anonInlineFunction(); +} // namespace + +// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/C9B3B71ACDD84C5BB320D34E97677715CDB3EA32.bc | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'innerPublicMethod' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'InnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'inlinedFunctionWithInnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'InnerClass' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/8960B5C9247D6F5C532756E53A1AD1240FA2146F.bc | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'namedInlineFunction' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'named' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'void' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/74A031CBE68C101F3E83F60ED17F20C11EC19D48.bc | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'innerPublicMethod' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'InnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'InnerClass' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/7CDD73DCD6CD72F7E5CE25502810A182C66C4B45.bc | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'privateMethod' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'void' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/85427901413EC77C961019EBB3ADEF7B0BAAFE78.bc | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'innerPublicMethod' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'functionWithInnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'int' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 +// CHECK-5: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'staticFunctionWithInnerClass' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'x' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/4712C5FA37B298A25501D1033C619B65B0ECC449.bc | FileCheck %s --check-prefix CHECK-6 +// CHECK-6: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'namedPrivateMethod' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'named' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'NamedClass' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'void' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/6E8FB72A89761E77020BFCEE9A9A6E64B15CC2A9.bc | FileCheck %s --check-prefix CHECK-7 +// CHECK-7: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'anonPrivateMethod' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'AnonClass' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'void' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/83CC52D32583E0771710A7742DE81C839E953AC8.bc | FileCheck %s --check-prefix CHECK-8 +// CHECK-8: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'anonInlineFunction' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'void' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: diff --git a/test/clang-doc/mapper-module.cpp b/test/clang-doc/mapper-module.cpp new file mode 100644 index 000000000..04a34c68d --- /dev/null +++ b/test/clang-doc/mapper-module.cpp @@ -0,0 +1,51 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +export module M; + +int moduleFunction(int x); // ModuleLinkage + +static int staticModuleFunction(int x); // ModuleInternalLinkage + +export double exportedModuleFunction(double y, int z); // ExternalLinkage + +// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'exportedModuleFunction' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'double' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'double' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'y' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'z' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: diff --git a/test/clang-doc/mapper-namespace.cpp b/test/clang-doc/mapper-namespace.cpp index aeda90816..d00082331 100644 --- a/test/clang-doc/mapper-namespace.cpp +++ b/test/clang-doc/mapper-namespace.cpp @@ -25,114 +25,70 @@ E func(int i) { return X; } } // namespace B } // namespace A -// RUN: clang-doc --dump-mapper --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'B' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'A' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: blob data = 'X' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'f' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'A' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC.bc | FileCheck %s --check-prefix CHECK-1 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'f' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'A' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'void' -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'func' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'B' +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/9A82CB33ED0FDF81EE383D31CD0957D153C5E840.bc | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'func' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'B' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'A' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'enum A::B::E' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'int' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'i' -// CHECK-2-NEXT: -// CHECK-2-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'A' -// CHECK-3-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-4 -// CHECK-4: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'B' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'A' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'A' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'enum A::B::E' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'int' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'i' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: diff --git a/test/clang-doc/mapper-record.cpp b/test/clang-doc/mapper-record.cpp index 82a5e2f01..dbabd8fdd 100644 --- a/test/clang-doc/mapper-record.cpp +++ b/test/clang-doc/mapper-record.cpp @@ -39,7 +39,7 @@ class X { class Y {}; }; -// RUN: clang-doc --dump-mapper --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs // RUN: llvm-bcanalyzer --dump %t/docs/bc/289584A8E0FF4178A794622A547AA622503967A1.bc | FileCheck %s --check-prefix CHECK-0 @@ -47,11 +47,32 @@ class X { // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'ProtectedMethod' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'E' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/3FB542274573CAEAD54CEBFFCAEE3D77FB9713D8.bc | FileCheck %s --check-prefix CHECK-1 @@ -60,10 +81,10 @@ class X { // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'I' // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'H' // CHECK-1-NEXT: // CHECK-1-NEXT: @@ -72,246 +93,128 @@ class X { // CHECK-1-NEXT: // CHECK-1-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/5093D428CDC62096A67547BA52566E4FB9404EEE.bc | FileCheck %s --check-prefix CHECK-2 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'ProtectedMethod' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'E' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'E' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'void' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-3 +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'X' +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'X' -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'C' +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'int' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'i' +// CHECK-3-NEXT: // CHECK-3-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E.bc | FileCheck %s --check-prefix CHECK-4 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'H' -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'void' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-5 +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'Y' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'X' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'C' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'i' -// CHECK-5-NEXT: -// CHECK-5-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17.bc | FileCheck %s --check-prefix CHECK-6 +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'Bc' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'A' +// CHECK-5-NEXT: blob data = 'B' +// CHECK-5-NEXT: +// CHECK-5-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-6 // CHECK-6: // CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '~E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'void' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4.bc | FileCheck %s --check-prefix CHECK-7 +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'D' +// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: +// CHECK-6-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-7 // CHECK-7: // CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'E' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'F' +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: blob data = 'E' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'E' +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'D' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'void' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-8 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-8 // CHECK-8: // CHECK-8-NEXT: // CHECK-8-NEXT: // CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'Y' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'X' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'A' +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'int' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'X' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'int' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'Y' +// CHECK-8-NEXT: // CHECK-8-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/FC07BD34D5E77782C263FA944447929EA8753740.bc | FileCheck %s --check-prefix CHECK-9 -// CHECK-9: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: -// CHECK-9-NEXT: blob data = 'B' -// CHECK-9-NEXT: blob data = '{{.*}}' -// CHECK-9-NEXT: blob data = 'X' -// CHECK-9-NEXT: blob data = 'Y' -// CHECK-9-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-10 -// CHECK-10: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: -// CHECK-10-NEXT: blob data = 'D' -// CHECK-10-NEXT: blob data = '{{.*}}' -// CHECK-10-NEXT: -// CHECK-10-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-11 -// CHECK-11: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: blob data = 'F' -// CHECK-11-NEXT: blob data = '{{.*}}' -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: blob data = 'E' -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: blob data = 'D' -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: -// CHECK-11-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-12 -// CHECK-12: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'A' -// CHECK-12-NEXT: blob data = '{{.*}}' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'int' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'X' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'int' -// CHECK-12-NEXT: -// CHECK-12-NEXT: -// CHECK-12-NEXT: blob data = 'Y' -// CHECK-12-NEXT: -// CHECK-12-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/1E3438A08BA22025C0B46289FF0686F92C8924C5.bc | FileCheck %s --check-prefix CHECK-13 -// CHECK-13: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: -// CHECK-13-NEXT: blob data = 'Bc' -// CHECK-13-NEXT: blob data = '{{.*}}' -// CHECK-13-NEXT: -// CHECK-13-NEXT: blob data = 'A' -// CHECK-13-NEXT: blob data = 'B' -// CHECK-13-NEXT: diff --git a/test/clang-doc/module.cpp b/test/clang-doc/module.cpp deleted file mode 100644 index a2b594597..000000000 --- a/test/clang-doc/module.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --extra-arg=-fmodules-ts --doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: cat %t/docs/moduleFunction.yaml | FileCheck %s --check-prefix=CHECK-A -// RUN: cat %t/docs/staticModuleFunction.yaml | FileCheck %s --check-prefix=CHECK-B -// RUN: cat %t/docs/exportedModuleFunction.yaml | FileCheck %s --check-prefix=CHECK-C - -export module M; - -int moduleFunction(int x); //ModuleLinkage -// CHECK-A: --- -// CHECK-A-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-A-NEXT: Name: 'moduleFunction' -// CHECK-A-NEXT: Location: -// CHECK-A-NEXT: - LineNumber: 12 -// CHECK-A-NEXT: Filename: {{.*}} -// CHECK-A-NEXT: Params: -// CHECK-A-NEXT: - Type: -// CHECK-A-NEXT: Name: 'int' -// CHECK-A-NEXT: Name: 'x' -// CHECK-A-NEXT: ReturnType: -// CHECK-A-NEXT: Type: -// CHECK-A-NEXT: Name: 'int' -// CHECK-A-NEXT: ... - -static int staticModuleFunction(int x); //ModuleInternalLinkage -// CHECK-B: --- -// CHECK-B-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-B-NEXT: Name: 'staticModuleFunction' -// CHECK-B-NEXT: Location: -// CHECK-B-NEXT: - LineNumber: 28 -// CHECK-B-NEXT: Filename: {{.*}} -// CHECK-B-NEXT: Params: -// CHECK-B-NEXT: - Type: -// CHECK-B-NEXT: Name: 'int' -// CHECK-B-NEXT: Name: 'x' -// CHECK-B-NEXT: ReturnType: -// CHECK-B-NEXT: Type: -// CHECK-B-NEXT: Name: 'int' -// CHECK-B-NEXT: ... - -export double exportedModuleFunction(double y, int z); //ExternalLinkage -// CHECK-C: --- -// CHECK-C-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-C-NEXT: Name: 'exportedModuleFunction' -// CHECK-C-NEXT: Location: -// CHECK-C-NEXT: - LineNumber: 44 -// CHECK-C-NEXT: Filename: {{.*}} -// CHECK-C-NEXT: Params: -// CHECK-C-NEXT: - Type: -// CHECK-C-NEXT: Name: 'double' -// CHECK-C-NEXT: Name: 'y' -// CHECK-C-NEXT: - Type: -// CHECK-C-NEXT: Name: 'int' -// CHECK-C-NEXT: Name: 'z' -// CHECK-C-NEXT: ReturnType: -// CHECK-C-NEXT: Type: -// CHECK-C-NEXT: Name: 'double' -// CHECK-C-NEXT: ... diff --git a/test/clang-doc/public-comment.cpp b/test/clang-doc/public-comment.cpp new file mode 100644 index 000000000..6c5545e8e --- /dev/null +++ b/test/clang-doc/public-comment.cpp @@ -0,0 +1,138 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +/// \brief Brief description. +/// +/// Extended description that +/// continues onto the next line. +/// +///
    +///
  • Testing. +///
+/// +/// \verbatim +/// The description continues. +/// \endverbatim +/// -- +/// \param [out] I is a parameter. +/// \param J is a parameter. +/// \return void +void F(int I, int J); + +/// Bonus comment on definition +void F(int I, int J) {} + +// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'F' +// CHECK-0-NEXT: Description: +// CHECK-0-NEXT: - Kind: 'FullComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'BlockCommandComment' +// CHECK-0-NEXT: Name: 'brief' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Brief description.' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Extended description that' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' continues onto the next line.' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' +// CHECK-0-NEXT: Name: 'ul' +// CHECK-0-NEXT: AttrKeys: +// CHECK-0-NEXT: - 'class' +// CHECK-0-NEXT: AttrValues: +// CHECK-0-NEXT: - 'test' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' +// CHECK-0-NEXT: Name: 'li' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Testing.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'HTMLEndTagComment' +// CHECK-0-NEXT: Name: 'ul' +// CHECK-0-NEXT: SelfClosing: true +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'VerbatimBlockComment' +// CHECK-0-NEXT: Name: 'verbatim' +// CHECK-0-NEXT: CloseName: 'endverbatim' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'VerbatimBlockLineComment' +// CHECK-0-NEXT: Text: ' The description continues.' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' --' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'ParamCommandComment' +// CHECK-0-NEXT: Direction: '[out]' +// CHECK-0-NEXT: ParamName: 'I' +// CHECK-0-NEXT: Explicit: true +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' is a parameter.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'ParamCommandComment' +// CHECK-0-NEXT: Direction: '[in]' +// CHECK-0-NEXT: ParamName: 'J' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' is a parameter.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'BlockCommandComment' +// CHECK-0-NEXT: Name: 'return' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' void' +// CHECK-0-NEXT: - Kind: 'FullComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Bonus comment on definition' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 28 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 25 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'I' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'J' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: ... diff --git a/test/clang-doc/public-linkage.cpp b/test/clang-doc/public-linkage.cpp new file mode 100644 index 000000000..c33e08ce6 --- /dev/null +++ b/test/clang-doc/public-linkage.cpp @@ -0,0 +1,299 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void function(int x); + +inline int inlinedFunction(int x); + +int functionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +inline int inlinedFunctionWithInnerClass(int x) { + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +class Class { +public: + void publicMethod(); + int publicField; + +protected: + void protectedMethod(); + int protectedField; + +private: + void privateMethod(); + int privateField; +}; + +namespace named { +class NamedClass { +public: + void namedPublicMethod(); + int namedPublicField; + +protected: + void namedProtectedMethod(); + int namedProtectedField; + +private: + void namedPrivateMethod(); + int namedPrivateField; +}; + +void namedFunction(); +static void namedStaticFunction(); +inline void namedInlineFunction(); +} // namespace named + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace { +class AnonClass { +public: + void anonPublicMethod(); + int anonPublicField; + +protected: + void anonProtectedMethod(); + int anonProtectedField; + +private: + void anonPrivateMethod(); + int anonPrivateField; +}; + +void anonFunction(); +static void anonStaticFunction(); +inline void anonInlineFunction(); +} // namespace + +// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./Class.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 32 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: TagType: Class +// CHECK-0-NEXT: Members: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'publicField' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'protectedField' +// CHECK-0-NEXT: Access: Protected +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'publicMethod' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 34 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: IsMethod: true +// CHECK-0-NEXT: Parent: +// CHECK-0-NEXT: Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'protectedMethod' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 38 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: IsMethod: true +// CHECK-0-NEXT: Parent: +// CHECK-0-NEXT: Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: ... + +// RUN: cat %t/docs/./named.yaml | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: --- +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: ChildFunctions: +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'namedFunction' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Location: +// CHECK-1-NEXT: - LineNumber: 61 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'void' +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'namedInlineFunction' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Location: +// CHECK-1-NEXT: - LineNumber: 63 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'void' +// CHECK-1-NEXT: ... + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: --- +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: ChildFunctions: +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'function' +// CHECK-2-NEXT: Location: +// CHECK-2-NEXT: - LineNumber: 10 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: Params: +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'x' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'void' +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'inlinedFunction' +// CHECK-2-NEXT: Location: +// CHECK-2-NEXT: - LineNumber: 12 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: Params: +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'x' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'functionWithInnerClass' +// CHECK-2-NEXT: DefLocation: +// CHECK-2-NEXT: LineNumber: 14 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: Params: +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'x' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'inlinedFunctionWithInnerClass' +// CHECK-2-NEXT: DefLocation: +// CHECK-2-NEXT: LineNumber: 23 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: Params: +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'x' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: ... + +// RUN: cat %t/docs/named/NamedClass.yaml | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: --- +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'NamedClass' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: Name: 'named' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 47 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: TagType: Class +// CHECK-3-NEXT: Members: +// CHECK-3-NEXT: - Type: +// CHECK-3-NEXT: Name: 'int' +// CHECK-3-NEXT: Name: 'namedPublicField' +// CHECK-3-NEXT: - Type: +// CHECK-3-NEXT: Name: 'int' +// CHECK-3-NEXT: Name: 'namedProtectedField' +// CHECK-3-NEXT: Access: Protected +// CHECK-3-NEXT: ChildFunctions: +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'namedPublicMethod' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'NamedClass' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: Name: 'named' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 49 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'NamedClass' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'namedProtectedMethod' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'NamedClass' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: Name: 'named' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 53 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'NamedClass' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: ... diff --git a/test/clang-doc/public-module.cpp b/test/clang-doc/public-module.cpp index c7ebadda7..0c93d6884 100644 --- a/test/clang-doc/public-module.cpp +++ b/test/clang-doc/public-module.cpp @@ -1,53 +1,51 @@ -// This test requires linux because it uses `diff` and compares filepaths -// REQUIRES: system-linux +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// // RUN: rm -rf %t // RUN: mkdir %t // RUN: echo "" > %t/compile_flags.txt // RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --public --extra-arg=-fmodules-ts --doxygen -p %t %t/test.cpp -output=%t/docs-with-public-flag -// RUN: clang-doc --extra-arg=-fmodules-ts --doxygen -p %t %t/test.cpp -output=%t/docs-without -// RUN: cat %t/docs-with-public-flag/moduleFunction.yaml | FileCheck %s --check-prefix=CHECK-A -// RUN: cat %t/docs-with-public-flag/exportedModuleFunction.yaml | FileCheck %s --check-prefix=CHECK-B -// RUN: (diff -qry %t/docs-with-public-flag %t/docs-without | sed 's:.*/::' > %t/public.diff) || true -// RUN: cat %t/public.diff | FileCheck %s --check-prefix=CHECK-C export module M; -int moduleFunction(int x); //ModuleLinkage -// CHECK-A: --- -// CHECK-A-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-A-NEXT: Name: 'moduleFunction' -// CHECK-A-NEXT: Location: -// CHECK-A-NEXT: - LineNumber: 16 -// CHECK-A-NEXT: Filename: {{.*}} -// CHECK-A-NEXT: Params: -// CHECK-A-NEXT: - Type: -// CHECK-A-NEXT: Name: 'int' -// CHECK-A-NEXT: Name: 'x' -// CHECK-A-NEXT: ReturnType: -// CHECK-A-NEXT: Type: -// CHECK-A-NEXT: Name: 'int' -// CHECK-A-NEXT: ... +int moduleFunction(int x); // ModuleLinkage -static int staticModuleFunction(int x); //ModuleInternalLinkage +static int staticModuleFunction(int x); // ModuleInternalLinkage -export double exportedModuleFunction(double y, int z); //ExternalLinkage -// CHECK-B: --- -// CHECK-B-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-B-NEXT: Name: 'exportedModuleFunction' -// CHECK-B-NEXT: Location: -// CHECK-B-NEXT: - LineNumber: 34 -// CHECK-B-NEXT: Filename: {{.*}} -// CHECK-B-NEXT: Params: -// CHECK-B-NEXT: - Type: -// CHECK-B-NEXT: Name: 'double' -// CHECK-B-NEXT: Name: 'y' -// CHECK-B-NEXT: - Type: -// CHECK-B-NEXT: Name: 'int' -// CHECK-B-NEXT: Name: 'z' -// CHECK-B-NEXT: ReturnType: -// CHECK-B-NEXT: Type: -// CHECK-B-NEXT: Name: 'double' -// CHECK-B-NEXT: ... +export double exportedModuleFunction(double y, int z); // ExternalLinkage -// CHECK-C: docs-without: staticModuleFunction.yaml +// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'moduleFunction' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 11 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'x' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'exportedModuleFunction' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 15 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'double' +// CHECK-0-NEXT: Name: 'y' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'z' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'double' +// CHECK-0-NEXT: ... diff --git a/test/clang-doc/public-namespace.cpp b/test/clang-doc/public-namespace.cpp new file mode 100644 index 000000000..d104ff2c7 --- /dev/null +++ b/test/clang-doc/public-namespace.cpp @@ -0,0 +1,96 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +namespace A { + +void f(); + +} // namespace A + +namespace A { + +void f(){}; + +namespace B { + +enum E { X }; + +E func(int i) { return X; } + +} // namespace B +} // namespace A + +// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./A.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'A' +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'f' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Namespace +// CHECK-0-NEXT: Name: 'A' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 17 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 11 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: ... + +// RUN: cat %t/docs/A/B.yaml | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: --- +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'B' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'A' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: ChildFunctions: +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'func' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'B' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'A' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: DefLocation: +// CHECK-1-NEXT: LineNumber: 23 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: Params: +// CHECK-1-NEXT: - Type: +// CHECK-1-NEXT: Name: 'int' +// CHECK-1-NEXT: Name: 'i' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'enum A::B::E' +// CHECK-1-NEXT: ChildEnums: +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'E' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'B' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'A' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: DefLocation: +// CHECK-1-NEXT: LineNumber: 21 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: Members: +// CHECK-1-NEXT: - 'X' +// CHECK-1-NEXT: ... diff --git a/test/clang-doc/public-record.cpp b/test/clang-doc/public-record.cpp new file mode 100644 index 000000000..d3302193f --- /dev/null +++ b/test/clang-doc/public-record.cpp @@ -0,0 +1,208 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// This test requires Linux due to system-dependent USR for the inner class. +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void H() { + class I {}; +} + +union A { int X; int Y; }; + +enum B { X, Y }; + +enum class Bc { A, B }; + +struct C { int i; }; + +class D {}; + +class E { +public: + E() {} + ~E() {} + +protected: + void ProtectedMethod(); +}; + +void E::ProtectedMethod() {} + +class F : virtual private D, public E {}; + +class X { + class Y {}; +}; + +// RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./C.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'C' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 21 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Members: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'i' +// CHECK-0-NEXT: ... + +// RUN: cat %t/docs/./A.yaml | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: --- +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'A' +// CHECK-1-NEXT: DefLocation: +// CHECK-1-NEXT: LineNumber: 15 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: TagType: Union +// CHECK-1-NEXT: Members: +// CHECK-1-NEXT: - Type: +// CHECK-1-NEXT: Name: 'int' +// CHECK-1-NEXT: Name: 'X' +// CHECK-1-NEXT: - Type: +// CHECK-1-NEXT: Name: 'int' +// CHECK-1-NEXT: Name: 'Y' +// CHECK-1-NEXT: ... + +// RUN: cat %t/docs/./F.yaml | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: --- +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'F' +// CHECK-2-NEXT: DefLocation: +// CHECK-2-NEXT: LineNumber: 36 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: TagType: Class +// CHECK-2-NEXT: Parents: +// CHECK-2-NEXT: - Type: Record +// CHECK-2-NEXT: Name: 'E' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: VirtualParents: +// CHECK-2-NEXT: - Type: Record +// CHECK-2-NEXT: Name: 'D' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: ... + +// RUN: cat %t/docs/./E.yaml | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: --- +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 25 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: TagType: Class +// CHECK-3-NEXT: ChildFunctions: +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 27 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: '~E' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 28 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'ProtectedMethod' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 34 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 31 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: ... + +// RUN: cat %t/docs/./D.yaml | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: --- +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: 'D' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 23 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: TagType: Class +// CHECK-4-NEXT: ... + +// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-5 +// CHECK-5: --- +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Name: 'X' +// CHECK-5-NEXT: DefLocation: +// CHECK-5-NEXT: LineNumber: 38 +// CHECK-5-NEXT: Filename: 'test' +// CHECK-5-NEXT: TagType: Class +// CHECK-5-NEXT: ... + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-6 +// CHECK-6: --- +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: ChildFunctions: +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'H' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 11 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: ReturnType: +// CHECK-6-NEXT: Type: +// CHECK-6-NEXT: Name: 'void' +// CHECK-6-NEXT: ChildEnums: +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'B' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 17 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: Members: +// CHECK-6-NEXT: - 'X' +// CHECK-6-NEXT: - 'Y' +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'Bc' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 19 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: Scoped: true +// CHECK-6-NEXT: Members: +// CHECK-6-NEXT: - 'A' +// CHECK-6-NEXT: - 'B' +// CHECK-6-NEXT: ... diff --git a/test/clang-doc/public-records.cpp b/test/clang-doc/public-records.cpp deleted file mode 100644 index a00a23b0d..000000000 --- a/test/clang-doc/public-records.cpp +++ /dev/null @@ -1,341 +0,0 @@ -// This test requires linux because it uses `diff` and compares filepaths -// REQUIRES: system-linux -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" -// RUN: clang-doc --public --doxygen -p %t %t/test.cpp -output=%t/docs -// RUN: clang-doc --doxygen -p %t %t/test.cpp -output=%t/docs-without-flag -// RUN: cat %t/docs/function.yaml | FileCheck %s --check-prefix=CHECK-A -// RUN: cat %t/docs/inlinedFunction.yaml | FileCheck %s --check-prefix=CHECK-B -// RUN: cat %t/docs/functionWithInnerClass.yaml | FileCheck %s --check-prefix=CHECK-C -// RUN: cat %t/docs/inlinedFunctionWithInnerClass.yaml | FileCheck %s --check-prefix=CHECK-D -// RUN: cat %t/docs/Class/publicMethod.yaml| FileCheck %s --check-prefix=CHECK-E -// RUN: cat %t/docs/Class.yaml| FileCheck %s --check-prefix=CHECK-F -// RUN: cat %t/docs/Class/protectedMethod.yaml| FileCheck %s --check-prefix=CHECK-G -// RUN: cat %t/docs/named.yaml| FileCheck %s --check-prefix=CHECK-H -// RUN: cat %t/docs/named/NamedClass.yaml| FileCheck %s --check-prefix=CHECK-I -// RUN: cat %t/docs/named/namedFunction.yaml| FileCheck %s --check-prefix=CHECK-J -// RUN: cat %t/docs/named/namedInlineFunction.yaml| FileCheck %s --check-prefix=CHECK-K -// RUN: cat %t/docs/named/NamedClass/namedPublicMethod.yaml| FileCheck %s --check-prefix=CHECK-L -// RUN: cat %t/docs/named/NamedClass/namedProtectedMethod.yaml| FileCheck %s --check-prefix=CHECK-M -// RUN: (diff -qry %t/docs-without-flag %t/docs | sed 's:.*/::' > %t/public.diff) || true -// RUN: cat %t/public.diff | FileCheck %s --check-prefix=CHECK-N - -void function(int x); - -// CHECK-A: --- -// CHECK-A-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-A-NEXT: Name: 'function' -// CHECK-A-NEXT: Location: -// CHECK-A-NEXT: - LineNumber: 25 -// CHECK-A-NEXT: Filename: {{.*}} -// CHECK-A-NEXT: Params: -// CHECK-A-NEXT: - Type: -// CHECK-A-NEXT: Name: 'int' -// CHECK-A-NEXT: Name: 'x' -// CHECK-A-NEXT: ReturnType: -// CHECK-A-NEXT: Type: -// CHECK-A-NEXT: Name: 'void' -// CHECK-A-NEXT: ... - -inline int inlinedFunction(int x); - -// CHECK-B: --- -// CHECK-B-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-B-NEXT: Name: 'inlinedFunction' -// CHECK-B-NEXT: Location: -// CHECK-B-NEXT: - LineNumber: 42 -// CHECK-B-NEXT: Filename: {{.*}} -// CHECK-B-NEXT: Params: -// CHECK-B-NEXT: - Type: -// CHECK-B-NEXT: Name: 'int' -// CHECK-B-NEXT: Name: 'x' -// CHECK-B-NEXT: ReturnType: -// CHECK-B-NEXT: Type: -// CHECK-B-NEXT: Name: 'int' -// CHECK-B-NEXT: ... - -int functionWithInnerClass(int x){ - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -// CHECK-C: --- -// CHECK-C-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-C-NEXT: Name: 'functionWithInnerClass' -// CHECK-C-NEXT: DefLocation: -// CHECK-C-NEXT: LineNumber: 59 -// CHECK-C-NEXT: Filename: {{.*}} -// CHECK-C-NEXT: Params: -// CHECK-C-NEXT: - Type: -// CHECK-C-NEXT: Name: 'int' -// CHECK-C-NEXT: Name: 'x' -// CHECK-C-NEXT: ReturnType: -// CHECK-C-NEXT: Type: -// CHECK-C-NEXT: Name: 'int' -// CHECK-C-NEXT: ... - -inline int inlinedFunctionWithInnerClass(int x){ - class InnerClass { //VisibleNoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -// CHECK-D: --- -// CHECK-D-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-D-NEXT: Name: 'inlinedFunctionWithInnerClass' -// CHECK-D-NEXT: DefLocation: -// CHECK-D-NEXT: LineNumber: 83 -// CHECK-D-NEXT: Filename: {{.*}} -// CHECK-D-NEXT: Params: -// CHECK-D-NEXT: - Type: -// CHECK-D-NEXT: Name: 'int' -// CHECK-D-NEXT: Name: 'x' -// CHECK-D-NEXT: ReturnType: -// CHECK-D-NEXT: Type: -// CHECK-D-NEXT: Name: 'int' -// CHECK-D-NEXT: ... - -class Class { - public: - void publicMethod(); - int publicField; - protected: - void protectedMethod(); - int protectedField; - private: - void privateMethod(); - int privateField; -}; - -// CHECK-E: --- -// CHECK-E-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-E-NEXT: Name: 'publicMethod' -// CHECK-E-NEXT: Namespace: -// CHECK-E-NEXT: - Type: Record -// CHECK-E-NEXT: Name: 'Class' -// CHECK-E-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-E-NEXT: Location: -// CHECK-E-NEXT: - LineNumber: 109 -// CHECK-E-NEXT: Filename: {{.*}} -// CHECK-E-NEXT: IsMethod: true -// CHECK-E-NEXT: Parent: -// CHECK-E-NEXT: Type: Record -// CHECK-E-NEXT: Name: 'Class' -// CHECK-E-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-E-NEXT: ReturnType: -// CHECK-E-NEXT: Type: -// CHECK-E-NEXT: Name: 'void' -// CHECK-E-NEXT: ... - -// CHECK-F: --- -// CHECK-F-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-F-NEXT: Name: 'Class' -// CHECK-F-NEXT: DefLocation: -// CHECK-F-NEXT: LineNumber: 107 -// CHECK-F-NEXT: Filename: {{.*}} -// CHECK-F-NEXT: TagType: Class -// CHECK-F-NEXT: Members: -// CHECK-F-NEXT: - Type: -// CHECK-F-NEXT: Name: 'int' -// CHECK-F-NEXT: Name: 'publicField' -// CHECK-F-NEXT: - Type: -// CHECK-F-NEXT: Name: 'int' -// CHECK-F-NEXT: Name: 'protectedField' -// CHECK-F-NEXT: Access: Protected -// CHECK-F-NEXT: ... - -// CHECK-G: --- -// CHECK-G-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-G-NEXT: Name: 'protectedMethod' -// CHECK-G-NEXT: Namespace: -// CHECK-G-NEXT: - Type: Record -// CHECK-G-NEXT: Name: 'Class' -// CHECK-G-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-G-NEXT: Location: -// CHECK-G-NEXT: - LineNumber: 112 -// CHECK-G-NEXT: Filename: {{.*}} -// CHECK-G-NEXT: IsMethod: true -// CHECK-G-NEXT: Parent: -// CHECK-G-NEXT: Type: Record -// CHECK-G-NEXT: Name: 'Class' -// CHECK-G-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-G-NEXT: ReturnType: -// CHECK-G-NEXT: Type: -// CHECK-G-NEXT: Name: 'void' -// CHECK-G-NEXT: ... - -namespace named{ - class NamedClass { - public: - void namedPublicMethod(); - int namedPublicField; - protected: - void namedProtectedMethod(); - int namedProtectedField; - private: - void namedPrivateMethod(); - int namedPrivateField; - }; - - void namedFunction(); - static void namedStaticFunction(); - inline void namedInlineFunction(); -} - -// CHECK-H: --- -// CHECK-H-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-H-NEXT: Name: 'named' -// CHECK-H-NEXT: ... - -// CHECK-I: --- -// CHECK-I-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-I-NEXT: Name: 'NamedClass' -// CHECK-I-NEXT: Namespace: -// CHECK-I-NEXT: - Type: Namespace -// CHECK-I-NEXT: Name: 'named' -// CHECK-I-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-I-NEXT: DefLocation: -// CHECK-I-NEXT: LineNumber: 177 -// CHECK-I-NEXT: Filename: {{.*}} -// CHECK-I-NEXT: TagType: Class -// CHECK-I-NEXT: Members: -// CHECK-I-NEXT: - Type: -// CHECK-I-NEXT: Name: 'int' -// CHECK-I-NEXT: Name: 'namedPublicField' -// CHECK-I-NEXT: - Type: -// CHECK-I-NEXT: Name: 'int' -// CHECK-I-NEXT: Name: 'namedProtectedField' -// CHECK-I-NEXT: Access: Protected -// CHECK-I-NEXT: ... - -// CHECK-J: --- -// CHECK-J-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-J-NEXT: Name: 'namedFunction' -// CHECK-J-NEXT: Namespace: -// CHECK-J-NEXT: - Type: Namespace -// CHECK-J-NEXT: Name: 'named' -// CHECK-J-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-J-NEXT: Location: -// CHECK-J-NEXT: - LineNumber: 189 -// CHECK-J-NEXT: Filename: {{.*}} -// CHECK-J-NEXT: ReturnType: -// CHECK-J-NEXT: Type: -// CHECK-J-NEXT: Name: 'void' -// CHECK-J-NEXT: ... - -// CHECK-K: --- -// CHECK-K-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-K-NEXT: Name: 'namedInlineFunction' -// CHECK-K-NEXT: Namespace: -// CHECK-K-NEXT: - Type: Namespace -// CHECK-K-NEXT: Name: 'named' -// CHECK-K-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-K-NEXT: Location: -// CHECK-K-NEXT: - LineNumber: 191 -// CHECK-K-NEXT: Filename: {{.*}} -// CHECK-K-NEXT: ReturnType: -// CHECK-K-NEXT: Type: -// CHECK-K-NEXT: Name: 'void' -// CHECK-K-NEXT: ... - -// CHECK-L: --- -// CHECK-L-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-L-NEXT: Name: 'namedPublicMethod' -// CHECK-L-NEXT: Namespace: -// CHECK-L-NEXT: - Type: Record -// CHECK-L-NEXT: Name: 'NamedClass' -// CHECK-L-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-L-NEXT: - Type: Namespace -// CHECK-L-NEXT: Name: 'named' -// CHECK-L-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-L-NEXT: Location: -// CHECK-L-NEXT: - LineNumber: 179 -// CHECK-L-NEXT: Filename: {{.*}} -// CHECK-L-NEXT: IsMethod: true -// CHECK-L-NEXT: Parent: -// CHECK-L-NEXT: Type: Record -// CHECK-L-NEXT: Name: 'NamedClass' -// CHECK-L-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-L-NEXT: ReturnType: -// CHECK-L-NEXT: Type: -// CHECK-L-NEXT: Name: 'void' -// CHECK-L-NEXT: ... - -// CHECK-M: --- -// CHECK-M-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-M-NEXT: Name: 'namedProtectedMethod' -// CHECK-M-NEXT: Namespace: -// CHECK-M-NEXT: - Type: Record -// CHECK-M-NEXT: Name: 'NamedClass' -// CHECK-M-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-M-NEXT: - Type: Namespace -// CHECK-M-NEXT: Name: 'named' -// CHECK-M-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-M-NEXT: Location: -// CHECK-M-NEXT: - LineNumber: 182 -// CHECK-M-NEXT: Filename: {{.*}} -// CHECK-M-NEXT: IsMethod: true -// CHECK-M-NEXT: Parent: -// CHECK-M-NEXT: Type: Record -// CHECK-M-NEXT: Name: 'NamedClass' -// CHECK-M-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-M-NEXT: ReturnType: -// CHECK-M-NEXT: Type: -// CHECK-M-NEXT: Name: 'void' -// CHECK-M-NEXT: ... - - -static void staticFunction(int x); //Internal Linkage - -static int staticFunctionWithInnerClass(int x){ - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -namespace{ - class AnonClass { - public: - void anonPublicMethod(); - int anonPublicField; - protected: - void anonProtectedMethod(); - int anonProtectedField; - private: - void anonPrivateMethod(); - int anonPrivateField; - }; - - void anonFunction(); - static void anonStaticFunction(); - inline void anonInlineFunction(); -} - -// CHECK-N: docs-without-flag: .yaml -// CHECK-N-NEXT: docs-without-flag: AnonClass -// CHECK-N-NEXT: docs-without-flag: AnonClass.yaml -// CHECK-N-NEXT: Class: privateMethod.yaml -// CHECK-N-NEXT: Class.yaml differ -// CHECK-N-NEXT: docs-without-flag: anonFunction.yaml -// CHECK-N-NEXT: docs-without-flag: anonInlineFunction.yaml -// CHECK-N-NEXT: docs-without-flag: anonStaticFunction.yaml -// CHECK-N-NEXT: docs-without-flag: functionWithInnerClass -// CHECK-N-NEXT: docs-without-flag: inlinedFunctionWithInnerClass -// CHECK-N-NEXT: NamedClass: namedPrivateMethod.yaml -// CHECK-N-NEXT: NamedClass.yaml differ -// CHECK-N-NEXT: named: namedStaticFunction.yaml -// CHECK-N-NEXT: docs-without-flag: staticFunction.yaml -// CHECK-N-NEXT: docs-without-flag: staticFunctionWithInnerClass -// CHECK-N-NEXT: docs-without-flag: staticFunctionWithInnerClass.yaml diff --git a/test/clang-doc/test_cases/linkage.cpp b/test/clang-doc/test_cases/linkage.cpp new file mode 100644 index 000000000..ed4b4a303 --- /dev/null +++ b/test/clang-doc/test_cases/linkage.cpp @@ -0,0 +1,95 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void function(int x); + +inline int inlinedFunction(int x); + +int functionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +inline int inlinedFunctionWithInnerClass(int x) { + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +class Class { +public: + void publicMethod(); + int publicField; + +protected: + void protectedMethod(); + int protectedField; + +private: + void privateMethod(); + int privateField; +}; + +namespace named { +class NamedClass { +public: + void namedPublicMethod(); + int namedPublicField; + +protected: + void namedProtectedMethod(); + int namedProtectedField; + +private: + void namedPrivateMethod(); + int namedPrivateField; +}; + +void namedFunction(); +static void namedStaticFunction(); +inline void namedInlineFunction(); +} // namespace named + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace { +class AnonClass { +public: + void anonPublicMethod(); + int anonPublicField; + +protected: + void anonProtectedMethod(); + int anonProtectedField; + +private: + void anonPrivateMethod(); + int anonPrivateField; +}; + +void anonFunction(); +static void anonStaticFunction(); +inline void anonInlineFunction(); +} // namespace diff --git a/test/clang-doc/test_cases/module.cpp b/test/clang-doc/test_cases/module.cpp new file mode 100644 index 000000000..3c30a5476 --- /dev/null +++ b/test/clang-doc/test_cases/module.cpp @@ -0,0 +1,15 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +export module M; + +int moduleFunction(int x); // ModuleLinkage + +static int staticModuleFunction(int x); // ModuleInternalLinkage + +export double exportedModuleFunction(double y, int z); // ExternalLinkage diff --git a/test/clang-doc/yaml-comment.cpp b/test/clang-doc/yaml-comment.cpp index 445e2be19..7aa8e64d5 100644 --- a/test/clang-doc/yaml-comment.cpp +++ b/test/clang-doc/yaml-comment.cpp @@ -27,110 +27,112 @@ void F(int I, int J); /// Bonus comment on definition void F(int I, int J) {} -// RUN: clang-doc --format=yaml --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs -// RUN: cat %t/docs/./F.yaml | FileCheck %s --check-prefix CHECK-0 +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-0 // CHECK-0: --- -// CHECK-0-NEXT: USR: '7574630614A535710E5A6ABCFFF98BCA2D06A4CA' -// CHECK-0-NEXT: Name: 'F' -// CHECK-0-NEXT: Description: -// CHECK-0-NEXT: - Kind: 'FullComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'BlockCommandComment' -// CHECK-0-NEXT: Name: 'brief' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'F' +// CHECK-0-NEXT: Description: +// CHECK-0-NEXT: - Kind: 'FullComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'ParagraphComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Brief description.' -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Extended description that' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' continues onto the next line.' -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' -// CHECK-0-NEXT: Name: 'ul' -// CHECK-0-NEXT: AttrKeys: -// CHECK-0-NEXT: - 'class' -// CHECK-0-NEXT: AttrValues: -// CHECK-0-NEXT: - 'test' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' -// CHECK-0-NEXT: Name: 'li' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Testing.' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'HTMLEndTagComment' -// CHECK-0-NEXT: Name: 'ul' -// CHECK-0-NEXT: SelfClosing: true -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'VerbatimBlockComment' -// CHECK-0-NEXT: Name: 'verbatim' -// CHECK-0-NEXT: CloseName: 'endverbatim' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'VerbatimBlockLineComment' -// CHECK-0-NEXT: Text: ' The description continues.' -// CHECK-0-NEXT: - Kind: 'ParagraphComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' --' -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'ParamCommandComment' -// CHECK-0-NEXT: Direction: '[out]' -// CHECK-0-NEXT: ParamName: 'I' -// CHECK-0-NEXT: Explicit: true -// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'BlockCommandComment' +// CHECK-0-NEXT: Name: 'brief' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Brief description.' // CHECK-0-NEXT: - Kind: 'ParagraphComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' is a parameter.' +// CHECK-0-NEXT: Text: ' Extended description that' // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'ParamCommandComment' -// CHECK-0-NEXT: Direction: '[in]' -// CHECK-0-NEXT: ParamName: 'J' -// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: Text: ' continues onto the next line.' // CHECK-0-NEXT: - Kind: 'ParagraphComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' is a parameter.' +// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' +// CHECK-0-NEXT: Name: 'ul' +// CHECK-0-NEXT: AttrKeys: +// CHECK-0-NEXT: - 'class' +// CHECK-0-NEXT: AttrValues: +// CHECK-0-NEXT: - 'test' // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: - Kind: 'BlockCommandComment' -// CHECK-0-NEXT: Name: 'return' -// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'HTMLStartTagComment' +// CHECK-0-NEXT: Name: 'li' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Testing.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'HTMLEndTagComment' +// CHECK-0-NEXT: Name: 'ul' +// CHECK-0-NEXT: SelfClosing: true // CHECK-0-NEXT: - Kind: 'ParagraphComment' // CHECK-0-NEXT: Children: // CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' void' -// CHECK-0-NEXT: - Kind: 'FullComment' -// CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: - Kind: 'VerbatimBlockComment' +// CHECK-0-NEXT: Name: 'verbatim' +// CHECK-0-NEXT: CloseName: 'endverbatim' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'VerbatimBlockLineComment' +// CHECK-0-NEXT: Text: ' The description continues.' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' --' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'ParamCommandComment' +// CHECK-0-NEXT: Direction: '[out]' +// CHECK-0-NEXT: ParamName: 'I' +// CHECK-0-NEXT: Explicit: true +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' is a parameter.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'ParamCommandComment' +// CHECK-0-NEXT: Direction: '[in]' +// CHECK-0-NEXT: ParamName: 'J' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' is a parameter.' +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: - Kind: 'BlockCommandComment' +// CHECK-0-NEXT: Name: 'return' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' void' +// CHECK-0-NEXT: - Kind: 'FullComment' // CHECK-0-NEXT: Children: -// CHECK-0-NEXT: - Kind: 'TextComment' -// CHECK-0-NEXT: Text: ' Bonus comment on definition' -// CHECK-0-NEXT: DefLocation: -// CHECK-0-NEXT: LineNumber: 28 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Location: -// CHECK-0-NEXT: - LineNumber: 25 -// CHECK-0-NEXT: Filename: 'test' -// CHECK-0-NEXT: Params: -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'I' -// CHECK-0-NEXT: - Type: -// CHECK-0-NEXT: Name: 'int' -// CHECK-0-NEXT: Name: 'J' -// CHECK-0-NEXT: ReturnType: -// CHECK-0-NEXT: Type: -// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: - Kind: 'ParagraphComment' +// CHECK-0-NEXT: Children: +// CHECK-0-NEXT: - Kind: 'TextComment' +// CHECK-0-NEXT: Text: ' Bonus comment on definition' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 28 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 25 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'I' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'J' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' // CHECK-0-NEXT: ... diff --git a/test/clang-doc/yaml-linkage.cpp b/test/clang-doc/yaml-linkage.cpp new file mode 100644 index 000000000..3a0aa5bf9 --- /dev/null +++ b/test/clang-doc/yaml-linkage.cpp @@ -0,0 +1,529 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void function(int x); + +inline int inlinedFunction(int x); + +int functionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +inline int inlinedFunctionWithInnerClass(int x) { + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +class Class { +public: + void publicMethod(); + int publicField; + +protected: + void protectedMethod(); + int protectedField; + +private: + void privateMethod(); + int privateField; +}; + +namespace named { +class NamedClass { +public: + void namedPublicMethod(); + int namedPublicField; + +protected: + void namedProtectedMethod(); + int namedProtectedField; + +private: + void namedPrivateMethod(); + int namedPrivateField; +}; + +void namedFunction(); +static void namedStaticFunction(); +inline void namedInlineFunction(); +} // namespace named + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace { +class AnonClass { +public: + void anonPublicMethod(); + int anonPublicField; + +protected: + void anonProtectedMethod(); + int anonProtectedField; + +private: + void anonPrivateMethod(); + int anonPrivateField; +}; + +void anonFunction(); +static void anonStaticFunction(); +inline void anonInlineFunction(); +} // namespace + +// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./Class.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 32 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: TagType: Class +// CHECK-0-NEXT: Members: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'publicField' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'protectedField' +// CHECK-0-NEXT: Access: Protected +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'privateField' +// CHECK-0-NEXT: Access: Private +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'publicMethod' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 34 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: IsMethod: true +// CHECK-0-NEXT: Parent: +// CHECK-0-NEXT: Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'protectedMethod' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 38 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: IsMethod: true +// CHECK-0-NEXT: Parent: +// CHECK-0-NEXT: Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'privateMethod' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 42 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: IsMethod: true +// CHECK-0-NEXT: Parent: +// CHECK-0-NEXT: Type: Record +// CHECK-0-NEXT: Name: 'Class' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' +// CHECK-0-NEXT: ... + +// RUN: cat %t/docs/./named.yaml | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: --- +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: ChildFunctions: +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'namedFunction' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Location: +// CHECK-1-NEXT: - LineNumber: 61 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'void' +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'namedStaticFunction' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Location: +// CHECK-1-NEXT: - LineNumber: 62 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'void' +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'namedInlineFunction' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'named' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Location: +// CHECK-1-NEXT: - LineNumber: 63 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'void' +// CHECK-1-NEXT: ... + +// RUN: cat %t/docs/./AnonClass.yaml | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: --- +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: Namespace: +// CHECK-2-NEXT: - Type: Namespace +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: DefLocation: +// CHECK-2-NEXT: LineNumber: 78 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: TagType: Class +// CHECK-2-NEXT: Members: +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'anonPublicField' +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'anonProtectedField' +// CHECK-2-NEXT: Access: Protected +// CHECK-2-NEXT: - Type: +// CHECK-2-NEXT: Name: 'int' +// CHECK-2-NEXT: Name: 'anonPrivateField' +// CHECK-2-NEXT: Access: Private +// CHECK-2-NEXT: ChildFunctions: +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'anonPublicMethod' +// CHECK-2-NEXT: Namespace: +// CHECK-2-NEXT: - Type: Record +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: - Type: Namespace +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Location: +// CHECK-2-NEXT: - LineNumber: 80 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: IsMethod: true +// CHECK-2-NEXT: Parent: +// CHECK-2-NEXT: Type: Record +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'void' +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'anonProtectedMethod' +// CHECK-2-NEXT: Namespace: +// CHECK-2-NEXT: - Type: Record +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: - Type: Namespace +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Location: +// CHECK-2-NEXT: - LineNumber: 84 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: IsMethod: true +// CHECK-2-NEXT: Parent: +// CHECK-2-NEXT: Type: Record +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'void' +// CHECK-2-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Name: 'anonPrivateMethod' +// CHECK-2-NEXT: Namespace: +// CHECK-2-NEXT: - Type: Record +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: - Type: Namespace +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: Location: +// CHECK-2-NEXT: - LineNumber: 88 +// CHECK-2-NEXT: Filename: 'test' +// CHECK-2-NEXT: IsMethod: true +// CHECK-2-NEXT: Parent: +// CHECK-2-NEXT: Type: Record +// CHECK-2-NEXT: Name: 'AnonClass' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-2-NEXT: ReturnType: +// CHECK-2-NEXT: Type: +// CHECK-2-NEXT: Name: 'void' +// CHECK-2-NEXT: ... + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: --- +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ChildFunctions: +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'anonFunction' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 92 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'anonStaticFunction' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 93 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'anonInlineFunction' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Namespace +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 94 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: ... + +// RUN: cat %t/docs/staticFunctionWithInnerClass/InnerClass.yaml | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: --- +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: 'InnerClass' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Function +// CHECK-4-NEXT: Name: 'staticFunctionWithInnerClass' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 69 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: TagType: Class +// CHECK-4-NEXT: ChildFunctions: +// CHECK-4-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: 'innerPublicMethod' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Record +// CHECK-4-NEXT: Name: 'InnerClass' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: - Type: Function +// CHECK-4-NEXT: Name: 'staticFunctionWithInnerClass' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 71 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: IsMethod: true +// CHECK-4-NEXT: Parent: +// CHECK-4-NEXT: Type: Record +// CHECK-4-NEXT: Name: 'InnerClass' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: ReturnType: +// CHECK-4-NEXT: Type: +// CHECK-4-NEXT: Name: 'int' +// CHECK-4-NEXT: ... + +// RUN: cat %t/docs/named/NamedClass.yaml | FileCheck %s --check-prefix CHECK-5 +// CHECK-5: --- +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: Namespace: +// CHECK-5-NEXT: - Type: Namespace +// CHECK-5-NEXT: Name: 'named' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: DefLocation: +// CHECK-5-NEXT: LineNumber: 47 +// CHECK-5-NEXT: Filename: 'test' +// CHECK-5-NEXT: TagType: Class +// CHECK-5-NEXT: Members: +// CHECK-5-NEXT: - Type: +// CHECK-5-NEXT: Name: 'int' +// CHECK-5-NEXT: Name: 'namedPublicField' +// CHECK-5-NEXT: - Type: +// CHECK-5-NEXT: Name: 'int' +// CHECK-5-NEXT: Name: 'namedProtectedField' +// CHECK-5-NEXT: Access: Protected +// CHECK-5-NEXT: - Type: +// CHECK-5-NEXT: Name: 'int' +// CHECK-5-NEXT: Name: 'namedPrivateField' +// CHECK-5-NEXT: Access: Private +// CHECK-5-NEXT: ChildFunctions: +// CHECK-5-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Name: 'namedPublicMethod' +// CHECK-5-NEXT: Namespace: +// CHECK-5-NEXT: - Type: Record +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: - Type: Namespace +// CHECK-5-NEXT: Name: 'named' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Location: +// CHECK-5-NEXT: - LineNumber: 49 +// CHECK-5-NEXT: Filename: 'test' +// CHECK-5-NEXT: IsMethod: true +// CHECK-5-NEXT: Parent: +// CHECK-5-NEXT: Type: Record +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: ReturnType: +// CHECK-5-NEXT: Type: +// CHECK-5-NEXT: Name: 'void' +// CHECK-5-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Name: 'namedProtectedMethod' +// CHECK-5-NEXT: Namespace: +// CHECK-5-NEXT: - Type: Record +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: - Type: Namespace +// CHECK-5-NEXT: Name: 'named' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Location: +// CHECK-5-NEXT: - LineNumber: 53 +// CHECK-5-NEXT: Filename: 'test' +// CHECK-5-NEXT: IsMethod: true +// CHECK-5-NEXT: Parent: +// CHECK-5-NEXT: Type: Record +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: ReturnType: +// CHECK-5-NEXT: Type: +// CHECK-5-NEXT: Name: 'void' +// CHECK-5-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Name: 'namedPrivateMethod' +// CHECK-5-NEXT: Namespace: +// CHECK-5-NEXT: - Type: Record +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: - Type: Namespace +// CHECK-5-NEXT: Name: 'named' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Location: +// CHECK-5-NEXT: - LineNumber: 57 +// CHECK-5-NEXT: Filename: 'test' +// CHECK-5-NEXT: IsMethod: true +// CHECK-5-NEXT: Parent: +// CHECK-5-NEXT: Type: Record +// CHECK-5-NEXT: Name: 'NamedClass' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: ReturnType: +// CHECK-5-NEXT: Type: +// CHECK-5-NEXT: Name: 'void' +// CHECK-5-NEXT: ... + +// RUN: cat %t/docs/functionWithInnerClass/InnerClass.yaml | FileCheck %s --check-prefix CHECK-6 +// CHECK-6: --- +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'InnerClass' +// CHECK-6-NEXT: Namespace: +// CHECK-6-NEXT: - Type: Function +// CHECK-6-NEXT: Name: 'functionWithInnerClass' +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 15 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: TagType: Class +// CHECK-6-NEXT: ChildFunctions: +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'innerPublicMethod' +// CHECK-6-NEXT: Namespace: +// CHECK-6-NEXT: - Type: Record +// CHECK-6-NEXT: Name: 'InnerClass' +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: - Type: Function +// CHECK-6-NEXT: Name: 'functionWithInnerClass' +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 17 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: IsMethod: true +// CHECK-6-NEXT: Parent: +// CHECK-6-NEXT: Type: Record +// CHECK-6-NEXT: Name: 'InnerClass' +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: ReturnType: +// CHECK-6-NEXT: Type: +// CHECK-6-NEXT: Name: 'int' +// CHECK-6-NEXT: ... + +// RUN: cat %t/docs/inlinedFunctionWithInnerClass/InnerClass.yaml | FileCheck %s --check-prefix CHECK-7 +// CHECK-7: --- +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: Name: 'InnerClass' +// CHECK-7-NEXT: Namespace: +// CHECK-7-NEXT: - Type: Function +// CHECK-7-NEXT: Name: 'inlinedFunctionWithInnerClass' +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: DefLocation: +// CHECK-7-NEXT: LineNumber: 24 +// CHECK-7-NEXT: Filename: 'test' +// CHECK-7-NEXT: TagType: Class +// CHECK-7-NEXT: ChildFunctions: +// CHECK-7-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: Name: 'innerPublicMethod' +// CHECK-7-NEXT: Namespace: +// CHECK-7-NEXT: - Type: Record +// CHECK-7-NEXT: Name: 'InnerClass' +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: - Type: Function +// CHECK-7-NEXT: Name: 'inlinedFunctionWithInnerClass' +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: DefLocation: +// CHECK-7-NEXT: LineNumber: 26 +// CHECK-7-NEXT: Filename: 'test' +// CHECK-7-NEXT: IsMethod: true +// CHECK-7-NEXT: Parent: +// CHECK-7-NEXT: Type: Record +// CHECK-7-NEXT: Name: 'InnerClass' +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: ReturnType: +// CHECK-7-NEXT: Type: +// CHECK-7-NEXT: Name: 'int' +// CHECK-7-NEXT: ... diff --git a/test/clang-doc/yaml-module.cpp b/test/clang-doc/yaml-module.cpp new file mode 100644 index 000000000..80602aca8 --- /dev/null +++ b/test/clang-doc/yaml-module.cpp @@ -0,0 +1,63 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +export module M; + +int moduleFunction(int x); // ModuleLinkage + +static int staticModuleFunction(int x); // ModuleInternalLinkage + +export double exportedModuleFunction(double y, int z); // ExternalLinkage + +// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: --- +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'moduleFunction' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 11 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'x' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'staticModuleFunction' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 13 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'x' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'exportedModuleFunction' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 15 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Params: +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'double' +// CHECK-0-NEXT: Name: 'y' +// CHECK-0-NEXT: - Type: +// CHECK-0-NEXT: Name: 'int' +// CHECK-0-NEXT: Name: 'z' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'double' +// CHECK-0-NEXT: ... diff --git a/test/clang-doc/yaml-namespace.cpp b/test/clang-doc/yaml-namespace.cpp index c547c5c49..d187f7e39 100644 --- a/test/clang-doc/yaml-namespace.cpp +++ b/test/clang-doc/yaml-namespace.cpp @@ -25,81 +25,72 @@ E func(int i) { return X; } } // namespace B } // namespace A -// RUN: clang-doc --format=yaml --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs // RUN: cat %t/docs/./A.yaml | FileCheck %s --check-prefix CHECK-0 // CHECK-0: --- -// CHECK-0-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-0-NEXT: Name: 'A' +// CHECK-0-NEXT: ChildFunctions: +// CHECK-0-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: Name: 'f' +// CHECK-0-NEXT: Namespace: +// CHECK-0-NEXT: - Type: Namespace +// CHECK-0-NEXT: Name: 'A' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-0-NEXT: DefLocation: +// CHECK-0-NEXT: LineNumber: 17 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: Location: +// CHECK-0-NEXT: - LineNumber: 11 +// CHECK-0-NEXT: Filename: 'test' +// CHECK-0-NEXT: ReturnType: +// CHECK-0-NEXT: Type: +// CHECK-0-NEXT: Name: 'void' // CHECK-0-NEXT: ... -// RUN: cat %t/docs/A/f.yaml | FileCheck %s --check-prefix CHECK-1 +// RUN: cat %t/docs/A/B.yaml | FileCheck %s --check-prefix CHECK-1 // CHECK-1: --- -// CHECK-1-NEXT: USR: '39D3C95A5F7CE2BA4937BD7B01BAE09EBC2AD8AC' -// CHECK-1-NEXT: Name: 'f' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'B' // CHECK-1-NEXT: Namespace: // CHECK-1-NEXT: - Type: Namespace // CHECK-1-NEXT: Name: 'A' -// CHECK-1-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' -// CHECK-1-NEXT: DefLocation: -// CHECK-1-NEXT: LineNumber: 17 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: Location: -// CHECK-1-NEXT: - LineNumber: 11 -// CHECK-1-NEXT: Filename: 'test' -// CHECK-1-NEXT: ReturnType: -// CHECK-1-NEXT: Type: -// CHECK-1-NEXT: Name: 'void' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: ChildFunctions: +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'func' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'B' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'A' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: DefLocation: +// CHECK-1-NEXT: LineNumber: 23 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: Params: +// CHECK-1-NEXT: - Type: +// CHECK-1-NEXT: Name: 'int' +// CHECK-1-NEXT: Name: 'i' +// CHECK-1-NEXT: ReturnType: +// CHECK-1-NEXT: Type: +// CHECK-1-NEXT: Name: 'enum A::B::E' +// CHECK-1-NEXT: ChildEnums: +// CHECK-1-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: Name: 'E' +// CHECK-1-NEXT: Namespace: +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'B' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: - Type: Namespace +// CHECK-1-NEXT: Name: 'A' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-1-NEXT: DefLocation: +// CHECK-1-NEXT: LineNumber: 21 +// CHECK-1-NEXT: Filename: 'test' +// CHECK-1-NEXT: Members: +// CHECK-1-NEXT: - 'X' // CHECK-1-NEXT: ... - -// RUN: cat %t/docs/A/B.yaml | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: --- -// CHECK-2-NEXT: USR: 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' -// CHECK-2-NEXT: Name: 'B' -// CHECK-2-NEXT: Namespace: -// CHECK-2-NEXT: - Type: Namespace -// CHECK-2-NEXT: Name: 'A' -// CHECK-2-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' -// CHECK-2-NEXT: ... - -// RUN: cat %t/docs/A/B/E.yaml | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: --- -// CHECK-3-NEXT: USR: 'E9ABF7E7E2425B626723D41E76E4BC7E7A5BD775' -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Namespace -// CHECK-3-NEXT: Name: 'B' -// CHECK-3-NEXT: USR: 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' -// CHECK-3-NEXT: - Type: Namespace -// CHECK-3-NEXT: Name: 'A' -// CHECK-3-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 21 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: Members: -// CHECK-3-NEXT: - 'X' -// CHECK-3-NEXT: ... - -// RUN: cat %t/docs/A/B/func.yaml | FileCheck %s --check-prefix CHECK-4 -// CHECK-4: --- -// CHECK-4-NEXT: USR: '9A82CB33ED0FDF81EE383D31CD0957D153C5E840' -// CHECK-4-NEXT: Name: 'func' -// CHECK-4-NEXT: Namespace: -// CHECK-4-NEXT: - Type: Namespace -// CHECK-4-NEXT: Name: 'B' -// CHECK-4-NEXT: USR: 'E21AF79E2A9D02554BA090D10DF39FE273F5CDB5' -// CHECK-4-NEXT: - Type: Namespace -// CHECK-4-NEXT: Name: 'A' -// CHECK-4-NEXT: USR: '8D042EFFC98B373450BC6B5B90A330C25A150E9C' -// CHECK-4-NEXT: DefLocation: -// CHECK-4-NEXT: LineNumber: 23 -// CHECK-4-NEXT: Filename: 'test' -// CHECK-4-NEXT: Params: -// CHECK-4-NEXT: - Type: -// CHECK-4-NEXT: Name: 'int' -// CHECK-4-NEXT: Name: 'i' -// CHECK-4-NEXT: ReturnType: -// CHECK-4-NEXT: Type: -// CHECK-4-NEXT: Name: 'enum A::B::E' -// CHECK-4-NEXT: ... diff --git a/test/clang-doc/yaml-record.cpp b/test/clang-doc/yaml-record.cpp index 1d5235f5c..8fad22017 100644 --- a/test/clang-doc/yaml-record.cpp +++ b/test/clang-doc/yaml-record.cpp @@ -39,12 +39,12 @@ class X { class Y {}; }; -// RUN: clang-doc --format=yaml --doxygen -p %t %t/test.cpp -output=%t/docs +// RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs // RUN: cat %t/docs/./C.yaml | FileCheck %s --check-prefix CHECK-0 // CHECK-0: --- -// CHECK-0-NEXT: USR: '06B5F6A19BA9F6A832E127C9968282B94619B210' +// CHECK-0-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-0-NEXT: Name: 'C' // CHECK-0-NEXT: DefLocation: // CHECK-0-NEXT: LineNumber: 21 @@ -57,7 +57,7 @@ class X { // RUN: cat %t/docs/./A.yaml | FileCheck %s --check-prefix CHECK-1 // CHECK-1: --- -// CHECK-1-NEXT: USR: 'ACE81AFA6627B4CEF2B456FB6E1252925674AF7E' +// CHECK-1-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-1-NEXT: Name: 'A' // CHECK-1-NEXT: DefLocation: // CHECK-1-NEXT: LineNumber: 15 @@ -74,7 +74,7 @@ class X { // RUN: cat %t/docs/./F.yaml | FileCheck %s --check-prefix CHECK-2 // CHECK-2: --- -// CHECK-2-NEXT: USR: 'E3B54702FABFF4037025BA194FC27C47006330B5' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-2-NEXT: Name: 'F' // CHECK-2-NEXT: DefLocation: // CHECK-2-NEXT: LineNumber: 36 @@ -83,26 +83,81 @@ class X { // CHECK-2-NEXT: Parents: // CHECK-2-NEXT: - Type: Record // CHECK-2-NEXT: Name: 'E' -// CHECK-2-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-2-NEXT: VirtualParents: // CHECK-2-NEXT: - Type: Record // CHECK-2-NEXT: Name: 'D' -// CHECK-2-NEXT: USR: '0921737541208B8FA9BB42B60F78AC1D779AA054' +// CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-2-NEXT: ... // RUN: cat %t/docs/./E.yaml | FileCheck %s --check-prefix CHECK-3 // CHECK-3: --- -// CHECK-3-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-3-NEXT: Name: 'E' // CHECK-3-NEXT: DefLocation: // CHECK-3-NEXT: LineNumber: 25 // CHECK-3-NEXT: Filename: 'test' // CHECK-3-NEXT: TagType: Class +// CHECK-3-NEXT: ChildFunctions: +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 27 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: '~E' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 28 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' +// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: Name: 'ProtectedMethod' +// CHECK-3-NEXT: Namespace: +// CHECK-3-NEXT: - Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: DefLocation: +// CHECK-3-NEXT: LineNumber: 34 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 31 +// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: IsMethod: true +// CHECK-3-NEXT: Parent: +// CHECK-3-NEXT: Type: Record +// CHECK-3-NEXT: Name: 'E' +// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-3-NEXT: ReturnType: +// CHECK-3-NEXT: Type: +// CHECK-3-NEXT: Name: 'void' // CHECK-3-NEXT: ... // RUN: cat %t/docs/./D.yaml | FileCheck %s --check-prefix CHECK-4 // CHECK-4: --- -// CHECK-4-NEXT: USR: '0921737541208B8FA9BB42B60F78AC1D779AA054' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-4-NEXT: Name: 'D' // CHECK-4-NEXT: DefLocation: // CHECK-4-NEXT: LineNumber: 23 @@ -110,143 +165,72 @@ class X { // CHECK-4-NEXT: TagType: Class // CHECK-4-NEXT: ... -// RUN: cat %t/docs/./B.yaml | FileCheck %s --check-prefix CHECK-5 +// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-5 // CHECK-5: --- -// CHECK-5-NEXT: USR: 'FC07BD34D5E77782C263FA944447929EA8753740' -// CHECK-5-NEXT: Name: 'B' +// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-5-NEXT: Name: 'X' // CHECK-5-NEXT: DefLocation: -// CHECK-5-NEXT: LineNumber: 17 +// CHECK-5-NEXT: LineNumber: 38 // CHECK-5-NEXT: Filename: 'test' -// CHECK-5-NEXT: Members: -// CHECK-5-NEXT: - 'X' -// CHECK-5-NEXT: - 'Y' +// CHECK-5-NEXT: TagType: Class // CHECK-5-NEXT: ... -// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-6 +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-6 // CHECK-6: --- -// CHECK-6-NEXT: USR: 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E' -// CHECK-6-NEXT: Name: 'X' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 38 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: TagType: Class +// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: ChildFunctions: +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'H' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 11 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: ReturnType: +// CHECK-6-NEXT: Type: +// CHECK-6-NEXT: Name: 'void' +// CHECK-6-NEXT: ChildEnums: +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'B' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 17 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: Members: +// CHECK-6-NEXT: - 'X' +// CHECK-6-NEXT: - 'Y' +// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-6-NEXT: Name: 'Bc' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 19 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: Scoped: true +// CHECK-6-NEXT: Members: +// CHECK-6-NEXT: - 'A' +// CHECK-6-NEXT: - 'B' // CHECK-6-NEXT: ... -// RUN: cat %t/docs/./H.yaml | FileCheck %s --check-prefix CHECK-7 +// RUN: cat %t/docs/H/I.yaml | FileCheck %s --check-prefix CHECK-7 // CHECK-7: --- -// CHECK-7-NEXT: USR: 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E' -// CHECK-7-NEXT: Name: 'H' +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: Name: 'I' +// CHECK-7-NEXT: Namespace: +// CHECK-7-NEXT: - Type: Function +// CHECK-7-NEXT: Name: 'H' +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-7-NEXT: DefLocation: -// CHECK-7-NEXT: LineNumber: 11 +// CHECK-7-NEXT: LineNumber: 12 // CHECK-7-NEXT: Filename: 'test' -// CHECK-7-NEXT: ReturnType: -// CHECK-7-NEXT: Type: -// CHECK-7-NEXT: Name: 'void' +// CHECK-7-NEXT: TagType: Class // CHECK-7-NEXT: ... -// RUN: cat %t/docs/./Bc.yaml | FileCheck %s --check-prefix CHECK-8 +// RUN: cat %t/docs/X/Y.yaml | FileCheck %s --check-prefix CHECK-8 // CHECK-8: --- -// CHECK-8-NEXT: USR: '1E3438A08BA22025C0B46289FF0686F92C8924C5' -// CHECK-8-NEXT: Name: 'Bc' +// CHECK-8-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-8-NEXT: Name: 'Y' +// CHECK-8-NEXT: Namespace: +// CHECK-8-NEXT: - Type: Record +// CHECK-8-NEXT: Name: 'X' +// CHECK-8-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-8-NEXT: DefLocation: -// CHECK-8-NEXT: LineNumber: 19 +// CHECK-8-NEXT: LineNumber: 39 // CHECK-8-NEXT: Filename: 'test' -// CHECK-8-NEXT: Scoped: true -// CHECK-8-NEXT: Members: -// CHECK-8-NEXT: - 'A' -// CHECK-8-NEXT: - 'B' +// CHECK-8-NEXT: TagType: Class // CHECK-8-NEXT: ... - -// RUN: cat %t/docs/H/I.yaml | FileCheck %s --check-prefix CHECK-9 -// CHECK-9: --- -// CHECK-9-NEXT: USR: '3FB542274573CAEAD54CEBFFCAEE3D77FB9713D8' -// CHECK-9-NEXT: Name: 'I' -// CHECK-9-NEXT: Namespace: -// CHECK-9-NEXT: - Type: Function -// CHECK-9-NEXT: Name: 'H' -// CHECK-9-NEXT: USR: 'B6AC4C5C9F2EA3F2B3ECE1A33D349F4EE502B24E' -// CHECK-9-NEXT: DefLocation: -// CHECK-9-NEXT: LineNumber: 12 -// CHECK-9-NEXT: Filename: 'test' -// CHECK-9-NEXT: TagType: Class -// CHECK-9-NEXT: ... - -// RUN: cat %t/docs/X/Y.yaml | FileCheck %s --check-prefix CHECK-10 -// CHECK-10: --- -// CHECK-10-NEXT: USR: '641AB4A3D36399954ACDE29C7A8833032BF40472' -// CHECK-10-NEXT: Name: 'Y' -// CHECK-10-NEXT: Namespace: -// CHECK-10-NEXT: - Type: Record -// CHECK-10-NEXT: Name: 'X' -// CHECK-10-NEXT: USR: 'CA7C7935730B5EACD25F080E9C83FA087CCDC75E' -// CHECK-10-NEXT: DefLocation: -// CHECK-10-NEXT: LineNumber: 39 -// CHECK-10-NEXT: Filename: 'test' -// CHECK-10-NEXT: TagType: Class -// CHECK-10-NEXT: ... - -// RUN: cat %t/docs/E/ProtectedMethod.yaml | FileCheck %s --check-prefix CHECK-11 -// CHECK-11: --- -// CHECK-11-NEXT: USR: '5093D428CDC62096A67547BA52566E4FB9404EEE' -// CHECK-11-NEXT: Name: 'ProtectedMethod' -// CHECK-11-NEXT: Namespace: -// CHECK-11-NEXT: - Type: Record -// CHECK-11-NEXT: Name: 'E' -// CHECK-11-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' -// CHECK-11-NEXT: DefLocation: -// CHECK-11-NEXT: LineNumber: 34 -// CHECK-11-NEXT: Filename: 'test' -// CHECK-11-NEXT: Location: -// CHECK-11-NEXT: - LineNumber: 31 -// CHECK-11-NEXT: Filename: 'test' -// CHECK-11-NEXT: IsMethod: true -// CHECK-11-NEXT: Parent: -// CHECK-11-NEXT: Type: Record -// CHECK-11-NEXT: Name: 'E' -// CHECK-11-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' -// CHECK-11-NEXT: ReturnType: -// CHECK-11-NEXT: Type: -// CHECK-11-NEXT: Name: 'void' -// CHECK-11-NEXT: ... - -// RUN: cat %t/docs/E/E.yaml | FileCheck %s --check-prefix CHECK-12 -// CHECK-12: --- -// CHECK-12-NEXT: USR: 'DEB4AC1CD9253CD9EF7FBE6BCAC506D77984ABD4' -// CHECK-12-NEXT: Name: 'E' -// CHECK-12-NEXT: Namespace: -// CHECK-12-NEXT: - Type: Record -// CHECK-12-NEXT: Name: 'E' -// CHECK-12-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' -// CHECK-12-NEXT: DefLocation: -// CHECK-12-NEXT: LineNumber: 27 -// CHECK-12-NEXT: Filename: 'test' -// CHECK-12-NEXT: IsMethod: true -// CHECK-12-NEXT: Parent: -// CHECK-12-NEXT: Type: Record -// CHECK-12-NEXT: Name: 'E' -// CHECK-12-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' -// CHECK-12-NEXT: ReturnType: -// CHECK-12-NEXT: Type: -// CHECK-12-NEXT: Name: 'void' -// CHECK-12-NEXT: ... - -// RUN: cat %t/docs/E/~E.yaml | FileCheck %s --check-prefix CHECK-13 -// CHECK-13: --- -// CHECK-13-NEXT: USR: 'BD2BDEBD423F80BACCEA75DE6D6622D355FC2D17' -// CHECK-13-NEXT: Name: '~E' -// CHECK-13-NEXT: Namespace: -// CHECK-13-NEXT: - Type: Record -// CHECK-13-NEXT: Name: 'E' -// CHECK-13-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' -// CHECK-13-NEXT: DefLocation: -// CHECK-13-NEXT: LineNumber: 28 -// CHECK-13-NEXT: Filename: 'test' -// CHECK-13-NEXT: IsMethod: true -// CHECK-13-NEXT: Parent: -// CHECK-13-NEXT: Type: Record -// CHECK-13-NEXT: Name: 'E' -// CHECK-13-NEXT: USR: '289584A8E0FF4178A794622A547AA622503967A1' -// CHECK-13-NEXT: ReturnType: -// CHECK-13-NEXT: Type: -// CHECK-13-NEXT: Name: 'void' -// CHECK-13-NEXT: ... From bd4816fe3b99b87907f1fe1ad4ef520e95f29504 Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Fri, 3 Aug 2018 00:40:11 +0000 Subject: [PATCH 007/686] [clang-doc] Fix unique_ptr error on bots Explicitly return the base unique_ptr type, as some of the older compilers on the bots don't support full C++11. Differential Revision: https://reviews.llvm.org/D50208 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338796 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/Serialize.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/clang-doc/Serialize.cpp b/clang-doc/Serialize.cpp index b12961463..873a9bdd1 100644 --- a/clang-doc/Serialize.cpp +++ b/clang-doc/Serialize.cpp @@ -329,7 +329,7 @@ std::unique_ptr emitInfo(const NamespaceDecl *D, const FullComment *FC, return nullptr; auto I = llvm::make_unique(); populateInfo(*I, D, FC); - return I; + return std::unique_ptr{std::move(I)}; } std::unique_ptr emitInfo(const RecordDecl *D, const FullComment *FC, @@ -343,7 +343,7 @@ std::unique_ptr emitInfo(const RecordDecl *D, const FullComment *FC, parseFields(*I, D, PublicOnly); if (const auto *C = dyn_cast(D)) parseBases(*I, C); - return I; + return std::unique_ptr{std::move(I)}; } std::unique_ptr emitInfo(const FunctionDecl *D, const FullComment *FC, @@ -362,7 +362,7 @@ std::unique_ptr emitInfo(const FunctionDecl *D, const FullComment *FC, else I->USR = SymbolID(); I->ChildFunctions.push_back(std::move(Func)); - return I; + return std::unique_ptr{std::move(I)}; } std::unique_ptr emitInfo(const CXXMethodDecl *D, const FullComment *FC, @@ -383,7 +383,7 @@ std::unique_ptr emitInfo(const CXXMethodDecl *D, const FullComment *FC, auto I = llvm::make_unique(); I->USR = ParentUSR; I->ChildFunctions.push_back(std::move(Func)); - return I; + return std::unique_ptr{std::move(I)}; } std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, @@ -400,18 +400,16 @@ std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, if (!Enum.Namespace.empty()) { switch (Enum.Namespace[0].RefType) { case InfoType::IT_namespace: { - std::unique_ptr IPtr = llvm::make_unique(); - NamespaceInfo *I = static_cast(IPtr.get()); + auto I = llvm::make_unique(); I->USR = Enum.Namespace[0].USR; I->ChildEnums.push_back(std::move(Enum)); - return IPtr; + return std::unique_ptr{std::move(I)}; } case InfoType::IT_record: { - std::unique_ptr IPtr = llvm::make_unique(); - RecordInfo *I = static_cast(IPtr.get()); + auto I = llvm::make_unique(); I->USR = Enum.Namespace[0].USR; I->ChildEnums.push_back(std::move(Enum)); - return IPtr; + return std::unique_ptr{std::move(I)}; } default: break; @@ -422,7 +420,7 @@ std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, auto I = llvm::make_unique(); I->USR = SymbolID(); I->ChildEnums.push_back(std::move(Enum)); - return I; + return std::unique_ptr{std::move(I)}; } } // namespace serialize From b318ad0396e10edd9324d8f127471e3f526eb021 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Fri, 3 Aug 2018 17:23:37 +0000 Subject: [PATCH 008/686] Use ExprMutationAnalyzer in performance-unnecessary-value-param Summary: This yields better recall as ExprMutationAnalyzer is more accurate. One common pattern this check is now able to catch is: ``` void foo(std::vector v) { for (const auto& elm : v) { // ... } } ``` Reviewers: george.karpenkov Subscribers: a.sidorin, cfe-commits Differential Revision: https://reviews.llvm.org/D50102 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338903 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../UnnecessaryValueParamCheck.cpp | 73 ++++++++++--------- .../performance-unnecessary-value-param.cpp | 22 ++++++ 2 files changed, 61 insertions(+), 34 deletions(-) diff --git a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp index e277ad341..588a29825 100644 --- a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp +++ b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp @@ -10,6 +10,7 @@ #include "UnnecessaryValueParamCheck.h" #include "../utils/DeclRefExprUtils.h" +#include "../utils/ExprMutationAnalyzer.h" #include "../utils/FixItHintUtils.h" #include "../utils/Matchers.h" #include "../utils/TypeTraits.h" @@ -31,14 +32,6 @@ std::string paramNameOrIndex(StringRef Name, size_t Index) { .str(); } -template -bool isSubset(const S &SubsetCandidate, const S &SupersetCandidate) { - for (const auto &E : SubsetCandidate) - if (SupersetCandidate.count(E) == 0) - return false; - return true; -} - bool isReferencedOutsideOfCallExpr(const FunctionDecl &Function, ASTContext &Context) { auto Matches = match(declRefExpr(to(functionDecl(equalsNode(&Function))), @@ -98,43 +91,55 @@ void UnnecessaryValueParamCheck::registerMatchers(MatchFinder *Finder) { void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) { const auto *Param = Result.Nodes.getNodeAs("param"); const auto *Function = Result.Nodes.getNodeAs("functionDecl"); - const size_t Index = std::find(Function->parameters().begin(), - Function->parameters().end(), Param) - - Function->parameters().begin(); - bool IsConstQualified = - Param->getType().getCanonicalType().isConstQualified(); - auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs( - *Param, *Function, *Result.Context); - auto ConstDeclRefExprs = utils::decl_ref_expr::constReferenceDeclRefExprs( - *Param, *Function, *Result.Context); - - // Do not trigger on non-const value parameters when they are not only used as - // const. - if (!isSubset(AllDeclRefExprs, ConstDeclRefExprs)) + // Do not trigger on non-const value parameters when they are mutated either + // within the function body or within init expression(s) when the function is + // a ctor. + if (utils::ExprMutationAnalyzer(Function->getBody(), Result.Context) + .isMutated(Param)) return; + // CXXCtorInitializer might also mutate Param but they're not part of function + // body, so check them separately here. + if (const auto *Ctor = dyn_cast(Function)) { + for (const auto *Init : Ctor->inits()) { + if (utils::ExprMutationAnalyzer(Init->getInit(), Result.Context) + .isMutated(Param)) + return; + } + } + + const bool IsConstQualified = + Param->getType().getCanonicalType().isConstQualified(); // If the parameter is non-const, check if it has a move constructor and is // only referenced once to copy-construct another object or whether it has a // move assignment operator and is only referenced once when copy-assigned. // In this case wrap DeclRefExpr with std::move() to avoid the unnecessary // copy. - if (!IsConstQualified && AllDeclRefExprs.size() == 1) { - auto CanonicalType = Param->getType().getCanonicalType(); - const auto &DeclRefExpr = **AllDeclRefExprs.begin(); - - if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) && - ((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) && - utils::decl_ref_expr::isCopyConstructorArgument( - DeclRefExpr, *Function, *Result.Context)) || - (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) && - utils::decl_ref_expr::isCopyAssignmentArgument( - DeclRefExpr, *Function, *Result.Context)))) { - handleMoveFix(*Param, DeclRefExpr, *Result.Context); - return; + if (!IsConstQualified) { + auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs( + *Param, *Function, *Result.Context); + if (AllDeclRefExprs.size() == 1) { + auto CanonicalType = Param->getType().getCanonicalType(); + const auto &DeclRefExpr = **AllDeclRefExprs.begin(); + + if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) && + ((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) && + utils::decl_ref_expr::isCopyConstructorArgument( + DeclRefExpr, *Function, *Result.Context)) || + (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) && + utils::decl_ref_expr::isCopyAssignmentArgument( + DeclRefExpr, *Function, *Result.Context)))) { + handleMoveFix(*Param, DeclRefExpr, *Result.Context); + return; + } } } + const size_t Index = std::find(Function->parameters().begin(), + Function->parameters().end(), Param) - + Function->parameters().begin(); + auto Diag = diag(Param->getLocation(), IsConstQualified ? "the const qualified parameter %0 is " diff --git a/test/clang-tidy/performance-unnecessary-value-param.cpp b/test/clang-tidy/performance-unnecessary-value-param.cpp index dcf82df2e..f801494cf 100644 --- a/test/clang-tidy/performance-unnecessary-value-param.cpp +++ b/test/clang-tidy/performance-unnecessary-value-param.cpp @@ -15,6 +15,20 @@ void mutate(ExpensiveToCopyType *); void useAsConstReference(const ExpensiveToCopyType &); void useByValue(ExpensiveToCopyType); +template class Vector { + public: + using iterator = T*; + using const_iterator = const T*; + + Vector(const Vector&); + Vector& operator=(const Vector&); + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; +}; + // This class simulates std::pair<>. It is trivially copy constructible // and trivially destructible, but not trivially copy assignable. class SomewhatTrivial { @@ -59,6 +73,14 @@ void positiveExpensiveValue(ExpensiveToCopyType Obj) { useByValue(Obj); } +void positiveVector(Vector V) { + // CHECK-MESSAGES: [[@LINE-1]]:49: warning: the parameter 'V' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param] + // CHECK-FIXES: void positiveVector(const Vector& V) { + for (const auto& Obj : V) { + useByValue(Obj); + } +} + void positiveWithComment(const ExpensiveToCopyType /* important */ S); // CHECK-FIXES: void positiveWithComment(const ExpensiveToCopyType& /* important */ S); void positiveWithComment(const ExpensiveToCopyType /* important */ S) { From 8d60b693400082ea3b036f55ed96ba8f569934be Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Fri, 3 Aug 2018 19:40:19 +0000 Subject: [PATCH 009/686] [clangd] Add test for changing build configuration Summary: This patch adds tests for the two ways of changing build configuration (pointing to a particular compile_commands.json): - Through the workspace/didChangeConfiguration notification. - Through the initialize request. Subscribers: ilya-biryukov, ioeric, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50255 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338914 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../compile-commands-path-in-initialize.test | 35 ++++++++++++++++ test/clangd/compile-commands-path.test | 42 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 test/clangd/compile-commands-path-in-initialize.test create mode 100644 test/clangd/compile-commands-path.test diff --git a/test/clangd/compile-commands-path-in-initialize.test b/test/clangd/compile-commands-path-in-initialize.test new file mode 100644 index 000000000..b34c59525 --- /dev/null +++ b/test/clangd/compile-commands-path-in-initialize.test @@ -0,0 +1,35 @@ +# Test that we can set choose a configuration/build directly in the initialize +# request. + +# RUN: rm -rf %t.dir/* && mkdir -p %t.dir +# RUN: mkdir %t.dir/build-1 +# RUN: mkdir %t.dir/build-2 +# RUN: echo '[{"directory": "%/t.dir", "command": "c++ the-file.cpp", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json +# RUN: echo '[{"directory": "%/t.dir/build-1", "command": "c++ -DMACRO=1 the-file.cpp", "file": "../the-file.cpp"}]' > %t.dir/build-1/compile_commands.json +# RUN: echo '[{"directory": "%/t.dir/build-2", "command": "c++ -DMACRO=2 the-file.cpp", "file": "../the-file.cpp"}]' > %t.dir/build-2/compile_commands.json + +# RUN: sed -e "s|INPUT_DIR|%/t.dir|g" %s > %t.test.1 + +# On Windows, we need the URI in didOpen to look like "uri":"file:///C:/..." +# (with the extra slash in the front), so we add it here. +# RUN: sed -e "s|file://\([A-Z]\):/|file:///\1:/|g" %t.test.1 > %t.test + +# RUN: clangd -lit-test < %t.test | FileCheck -strict-whitespace %t.test + +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"initializationOptions":{"compilationDatabasePath":"INPUT_DIR/build-1"}}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file://INPUT_DIR/the-file.cpp","languageId":"cpp","version":1,"text":"#if !defined(MACRO)\n#pragma message (\"MACRO is not defined\")\n#elif MACRO == 1\n#pragma message (\"MACRO is one\")\n#elif MACRO == 2\n#pragma message (\"MACRO is two\")\n#else\n#pragma message (\"woops\")\n#endif\nint main() {}\n"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "message": "MACRO is one", +--- +{"jsonrpc":"2.0","id":0,"method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabasePath":"INPUT_DIR/build-2"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "message": "MACRO is two", +--- +{"jsonrpc":"2.0","id":10000,"method":"shutdown"} diff --git a/test/clangd/compile-commands-path.test b/test/clangd/compile-commands-path.test new file mode 100644 index 000000000..f25d002f9 --- /dev/null +++ b/test/clangd/compile-commands-path.test @@ -0,0 +1,42 @@ +# Test that we can switch between configuration/build using the +# workspace/didChangeConfiguration notification. + +# RUN: rm -rf %t.dir/* && mkdir -p %t.dir +# RUN: mkdir %t.dir/build-1 +# RUN: mkdir %t.dir/build-2 +# RUN: echo '[{"directory": "%/t.dir", "command": "c++ the-file.cpp", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json +# RUN: echo '[{"directory": "%/t.dir/build-1", "command": "c++ -DMACRO=1 the-file.cpp", "file": "../the-file.cpp"}]' > %t.dir/build-1/compile_commands.json +# RUN: echo '[{"directory": "%/t.dir/build-2", "command": "c++ -DMACRO=2 the-file.cpp", "file": "../the-file.cpp"}]' > %t.dir/build-2/compile_commands.json + +# RUN: sed -e "s|INPUT_DIR|%/t.dir|g" %s > %t.test.1 + +# On Windows, we need the URI in didOpen to look like "uri":"file:///C:/..." +# (with the extra slash in the front), so we add it here. +# RUN: sed -e "s|file://\([A-Z]\):/|file:///\1:/|g" %t.test.1 > %t.test + +# RUN: clangd -lit-test < %t.test | FileCheck -strict-whitespace %t.test + +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file://INPUT_DIR/the-file.cpp","languageId":"cpp","version":1,"text":"#if !defined(MACRO)\n#pragma message (\"MACRO is not defined\")\n#elif MACRO == 1\n#pragma message (\"MACRO is one\")\n#elif MACRO == 2\n#pragma message (\"MACRO is two\")\n#else\n#pragma message (\"woops\")\n#endif\nint main() {}\n"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "message": "MACRO is not defined", +--- +{"jsonrpc":"2.0","id":0,"method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabasePath":"INPUT_DIR/build-1"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "message": "MACRO is one", +--- +{"jsonrpc":"2.0","id":0,"method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabasePath":"INPUT_DIR/build-2"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "message": "MACRO is two", +--- +{"jsonrpc":"2.0","id":10000,"method":"shutdown"} From a6248030880b49b3fbafd977e5cf25a33f1e892d Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Fri, 3 Aug 2018 20:43:28 +0000 Subject: [PATCH 010/686] [clangd] capitalize diagnostic messages The diagnostic messages that are sent to the client from Clangd are now always capitalized. Differential Revision: https://reviews.llvm.org/D50154 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338919 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/Diagnostics.cpp | 11 +++++++++-- test/clangd/diagnostics.test | 2 +- test/clangd/did-change-configuration-params.test | 2 +- test/clangd/execute-command.test | 2 +- test/clangd/extra-flags.test | 4 ++-- test/clangd/fixits.test | 6 +++--- unittests/clangd/ClangdUnitTests.cpp | 4 ++-- 7 files changed, 19 insertions(+), 12 deletions(-) diff --git a/clangd/Diagnostics.cpp b/clangd/Diagnostics.cpp index 73ac475ef..5a61567f2 100644 --- a/clangd/Diagnostics.cpp +++ b/clangd/Diagnostics.cpp @@ -145,6 +145,13 @@ void printDiag(llvm::raw_string_ostream &OS, const DiagBase &D) { OS << diagLeveltoString(D.Severity) << ": " << D.Message; } +/// Capitalizes the first word in the diagnostic's message. +std::string capitalize(std::string Message) { + if (!Message.empty()) + Message[0] = llvm::toUpper(Message[0]); + return Message; +} + /// Returns a message sent to LSP for the main diagnostic in \p D. /// The message includes all the notes with their corresponding locations. /// However, notes with fix-its are excluded as those usually only contain a @@ -166,7 +173,7 @@ std::string mainMessage(const Diag &D) { printDiag(OS, Note); } OS.flush(); - return Result; + return capitalize(std::move(Result)); } /// Returns a message sent to LSP for the note of the main diagnostic. @@ -179,7 +186,7 @@ std::string noteMessage(const Diag &Main, const DiagBase &Note) { OS << "\n\n"; printDiag(OS, Main); OS.flush(); - return Result; + return capitalize(std::move(Result)); } } // namespace diff --git a/test/clangd/diagnostics.test b/test/clangd/diagnostics.test index afd1277b2..a191c0822 100644 --- a/test/clangd/diagnostics.test +++ b/test/clangd/diagnostics.test @@ -6,7 +6,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "message": "return type of 'main' is not 'int'", +# CHECK-NEXT: "message": "Return type of 'main' is not 'int'", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { # CHECK-NEXT: "character": 4, diff --git a/test/clangd/did-change-configuration-params.test b/test/clangd/did-change-configuration-params.test index d46c043da..e0f936399 100644 --- a/test/clangd/did-change-configuration-params.test +++ b/test/clangd/did-change-configuration-params.test @@ -24,7 +24,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "message": "variable 'i' is uninitialized when used here", +# CHECK-NEXT: "message": "Variable 'i' is uninitialized when used here", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { # CHECK-NEXT: "character": 28, diff --git a/test/clangd/execute-command.test b/test/clangd/execute-command.test index 9686d0400..492006fdf 100644 --- a/test/clangd/execute-command.test +++ b/test/clangd/execute-command.test @@ -6,7 +6,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "message": "using the result of an assignment as a condition without parentheses", +# CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { # CHECK-NEXT: "character": 37, diff --git a/test/clangd/extra-flags.test b/test/clangd/extra-flags.test index f360ccae4..f2460eb12 100644 --- a/test/clangd/extra-flags.test +++ b/test/clangd/extra-flags.test @@ -6,7 +6,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "message": "variable 'i' is uninitialized when used here", +# CHECK-NEXT: "message": "Variable 'i' is uninitialized when used here", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { # CHECK-NEXT: "character": 28, @@ -28,7 +28,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "message": "variable 'i' is uninitialized when used here", +# CHECK-NEXT: "message": "Variable 'i' is uninitialized when used here", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { # CHECK-NEXT: "character": 28, diff --git a/test/clangd/fixits.test b/test/clangd/fixits.test index f8eb20ca7..ce74d1c8b 100644 --- a/test/clangd/fixits.test +++ b/test/clangd/fixits.test @@ -6,7 +6,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "message": "using the result of an assignment as a condition without parentheses", +# CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { # CHECK-NEXT: "character": 37, @@ -23,7 +23,7 @@ # CHECK-NEXT: "uri": "file://{{.*}}/foo.c" # CHECK-NEXT: } --- -{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"using the result of an assignment as a condition without parentheses"}]}}} +{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses"}]}}} # CHECK: "id": 2, # CHECK-NEXT: "jsonrpc": "2.0", # CHECK-NEXT: "result": [ @@ -92,7 +92,7 @@ # CHECK-NEXT: } # CHECK-NEXT: ] --- -{"jsonrpc":"2.0","id":3,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"using the result of an assignment as a condition without parentheses"}]}}} +{"jsonrpc":"2.0","id":3,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses"}]}}} # Make sure unused "code" and "source" fields ignored gracefully # CHECK: "id": 3, # CHECK-NEXT: "jsonrpc": "2.0", diff --git a/unittests/clangd/ClangdUnitTests.cpp b/unittests/clangd/ClangdUnitTests.cpp index 6843c9e06..5e662cb87 100644 --- a/unittests/clangd/ClangdUnitTests.cpp +++ b/unittests/clangd/ClangdUnitTests.cpp @@ -172,7 +172,7 @@ TEST(DiagnosticsTest, ToLSP) { }; // Diagnostics should turn into these: - clangd::Diagnostic MainLSP = MatchingLSP(D, R"(something terrible happened + clangd::Diagnostic MainLSP = MatchingLSP(D, R"(Something terrible happened main.cpp:6:7: remark: declared somewhere in the main file @@ -180,7 +180,7 @@ main.cpp:6:7: remark: declared somewhere in the main file note: declared somewhere in the header file)"); clangd::Diagnostic NoteInMainLSP = - MatchingLSP(NoteInMain, R"(declared somewhere in the main file + MatchingLSP(NoteInMain, R"(Declared somewhere in the main file main.cpp:2:3: error: something terrible happened)"); From 89ef9f5dd68256963f7c130eefe6b744eb5cb928 Mon Sep 17 00:00:00 2001 From: Martin Bohme Date: Fri, 3 Aug 2018 22:20:04 +0000 Subject: [PATCH 011/686] [clang-tidy] Sequence init statements, declarations, and conditions correctly in if, switch, and while Summary: Fixes https://bugs.llvm.org/show_bug.cgi?id=36516. Reviewers: ilya-biryukov, alexfh, aaron.ballman, hokein Reviewed By: alexfh Subscribers: xazax.hun, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D49918 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338932 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/utils/ExprSequence.cpp | 21 ++++++++++++-- test/clang-tidy/bugprone-use-after-move.cpp | 31 ++++++++++++++++----- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/clang-tidy/utils/ExprSequence.cpp b/clang-tidy/utils/ExprSequence.cpp index 02d4a0bdd..48c3de545 100644 --- a/clang-tidy/utils/ExprSequence.cpp +++ b/clang-tidy/utils/ExprSequence.cpp @@ -139,11 +139,26 @@ const Stmt *ExprSequence::getSequenceSuccessor(const Stmt *S) const { if (S == ForRange->getLoopVarStmt()) return ForRange->getBody(); } else if (const auto *TheIfStmt = dyn_cast(Parent)) { - // If statement: If a variable is declared inside the condition, the - // expression used to initialize the variable is sequenced before the - // evaluation of the condition. + // If statement: + // - Sequence init statement before variable declaration. + // - Sequence variable declaration (along with the expression used to + // initialize it) before the evaluation of the condition. + if (S == TheIfStmt->getInit()) + return TheIfStmt->getConditionVariableDeclStmt(); if (S == TheIfStmt->getConditionVariableDeclStmt()) return TheIfStmt->getCond(); + } else if (const auto *TheSwitchStmt = dyn_cast(Parent)) { + // Ditto for switch statements. + if (S == TheSwitchStmt->getInit()) + return TheSwitchStmt->getConditionVariableDeclStmt(); + if (S == TheSwitchStmt->getConditionVariableDeclStmt()) + return TheSwitchStmt->getCond(); + } else if (const auto *TheWhileStmt = dyn_cast(Parent)) { + // While statement: Sequence variable declaration (along with the + // expression used to initialize it) before the evaluation of the + // condition. + if (S == TheWhileStmt->getConditionVariableDeclStmt()) + return TheWhileStmt->getCond(); } } diff --git a/test/clang-tidy/bugprone-use-after-move.cpp b/test/clang-tidy/bugprone-use-after-move.cpp index 43e632211..9307f17f4 100644 --- a/test/clang-tidy/bugprone-use-after-move.cpp +++ b/test/clang-tidy/bugprone-use-after-move.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s bugprone-use-after-move %t -- -- -std=c++11 -fno-delayed-template-parsing +// RUN: %check_clang_tidy %s bugprone-use-after-move %t -- -- -std=c++17 -fno-delayed-template-parsing typedef decltype(nullptr) nullptr_t; @@ -1132,13 +1132,30 @@ void forRangeSequences() { } } -// If a variable is declared in an if statement, the declaration of the variable -// (which is treated like a reinitialization by the check) is sequenced before -// the evaluation of the condition (which constitutes a use). -void ifStmtSequencesDeclAndCondition() { +// If a variable is declared in an if, while or switch statement, the init +// statement (for if and switch) is sequenced before the variable declaration, +// which in turn is sequenced before the evaluation of the condition. +void ifWhileAndSwitchSequenceInitDeclAndCondition() { for (int i = 0; i < 10; ++i) { - if (A a = A()) { - std::move(a); + A a1; + if (A a2 = std::move(a1)) { + std::move(a2); + } + } + for (int i = 0; i < 10; ++i) { + A a1; + if (A a2 = std::move(a1); A a3 = std::move(a2)) { + std::move(a3); + } + } + while (A a = A()) { + std::move(a); + } + for (int i = 0; i < 10; ++i) { + A a1; + switch (A a2 = a1; A a3 = std::move(a2)) { + case true: + std::move(a3); } } } From 1d827042b4ce80630cea1469b8cdace46b6b426d Mon Sep 17 00:00:00 2001 From: Matt Morehouse Date: Sat, 4 Aug 2018 01:51:10 +0000 Subject: [PATCH 012/686] [clangd] Fix fuzzer build. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@338947 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/fuzzer/ClangdFuzzer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clangd/fuzzer/ClangdFuzzer.cpp b/clangd/fuzzer/ClangdFuzzer.cpp index 86f610707..d521e62ad 100644 --- a/clangd/fuzzer/ClangdFuzzer.cpp +++ b/clangd/fuzzer/ClangdFuzzer.cpp @@ -30,7 +30,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { clang::clangd::ClangdServer::Options Opts; // Initialize and run ClangdLSPServer. - clang::clangd::ClangdLSPServer LSPServer(Out, CCOpts, llvm::None, Opts); + clang::clangd::ClangdLSPServer LSPServer(Out, CCOpts, llvm::None, false, + Opts); // fmemopen isn't portable, but I think we only run the fuzzer on Linux. LSPServer.run(fmemopen(data, size, "r")); return 0; From a85b09f457458c2c838a70a4477633b827ffc09f Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Mon, 6 Aug 2018 13:14:32 +0000 Subject: [PATCH 013/686] [clangd] Index Interfaces for Xrefs Summary: This is the first step of implementing Xrefs in clangd: - add index interfaces, and related data structures. Reviewers: sammccall Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D49658 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339011 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/FileIndex.cpp | 7 ++++ clangd/index/FileIndex.h | 4 +++ clangd/index/Index.h | 50 ++++++++++++++++++++++++-- clangd/index/MemIndex.cpp | 6 ++++ clangd/index/MemIndex.h | 6 +++- clangd/index/Merge.cpp | 7 ++++ unittests/clangd/CodeCompleteTests.cpp | 4 +++ 7 files changed, 81 insertions(+), 3 deletions(-) diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 407e86a2c..8ee5018ed 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -9,6 +9,7 @@ #include "FileIndex.h" #include "SymbolCollector.h" +#include "../Logger.h" #include "clang/Index/IndexingAction.h" #include "clang/Lex/Preprocessor.h" @@ -105,5 +106,11 @@ void FileIndex::lookup( Index.lookup(Req, Callback); } +void FileIndex::findOccurrences( + const OccurrencesRequest &Req, + llvm::function_ref Callback) const { + log("findOccurrences is not implemented."); +} + } // namespace clangd } // namespace clang diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index 1af37d19b..35c9f7efc 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -73,6 +73,10 @@ class FileIndex : public SymbolIndex { void lookup(const LookupRequest &Req, llvm::function_ref Callback) const override; + + void findOccurrences(const OccurrencesRequest &Req, + llvm::function_ref + Callback) const override; private: FileSymbols FSymbols; MemIndex Index; diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 18ed05f73..6d63335d7 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -287,6 +287,40 @@ class SymbolSlab { std::vector Symbols; // Sorted by SymbolID to allow lookup. }; +// Describes the kind of a symbol occurrence. +// +// This is a bitfield which can be combined from different kinds. +enum class SymbolOccurrenceKind : uint8_t { + Unknown = 0, + Declaration = static_cast(index::SymbolRole::Declaration), + Definition = static_cast(index::SymbolRole::Definition), + Reference = static_cast(index::SymbolRole::Reference), +}; +inline SymbolOccurrenceKind operator|(SymbolOccurrenceKind L, + SymbolOccurrenceKind R) { + return static_cast(static_cast(L) | + static_cast(R)); +} +inline SymbolOccurrenceKind &operator|=(SymbolOccurrenceKind &L, + SymbolOccurrenceKind R) { + return L = L | R; +} +inline SymbolOccurrenceKind operator&(SymbolOccurrenceKind A, + SymbolOccurrenceKind B) { + return static_cast(static_cast(A) & + static_cast(B)); +} + +// Represents a symbol occurrence in the source file. It could be a +// declaration/definition/reference occurrence. +// +// WARNING: Location does not own the underlying data - Copies are shallow. +struct SymbolOccurrence { + // The location of the occurrence. + SymbolLocation Location; + SymbolOccurrenceKind Kind = SymbolOccurrenceKind::Unknown; +}; + struct FuzzyFindRequest { /// \brief A query string for the fuzzy find. This is matched against symbols' /// un-qualified identifiers and should not contain qualifiers like "::". @@ -312,6 +346,11 @@ struct LookupRequest { llvm::DenseSet IDs; }; +struct OccurrencesRequest { + llvm::DenseSet IDs; + SymbolOccurrenceKind Filter; +}; + /// \brief Interface for symbol indexes that can be used for searching or /// matching symbols among a set of symbols based on names or unique IDs. class SymbolIndex { @@ -334,8 +373,15 @@ class SymbolIndex { lookup(const LookupRequest &Req, llvm::function_ref Callback) const = 0; - // FIXME: add interfaces for more index use cases: - // - getAllOccurrences(SymbolID); + /// CrossReference finds all symbol occurrences (e.g. references, + /// declarations, definitions) and applies \p Callback on each result. + /// + /// Resutls are returned in arbitrary order. + /// + /// The returned result must be deep-copied if it's used outside Callback. + virtual void findOccurrences( + const OccurrencesRequest &Req, + llvm::function_ref Callback) const = 0; }; } // namespace clangd diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index 1a38386bd..eca0bfe7f 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -87,5 +87,11 @@ std::unique_ptr MemIndex::build(SymbolSlab Slab) { return std::move(MemIdx); } +void MemIndex::findOccurrences( + const OccurrencesRequest &Req, + llvm::function_ref Callback) const { + log("findOccurrences is not implemented."); +} + } // namespace clangd } // namespace clang diff --git a/clangd/index/MemIndex.h b/clangd/index/MemIndex.h index 3147a6c21..0f59b8900 100644 --- a/clangd/index/MemIndex.h +++ b/clangd/index/MemIndex.h @@ -31,10 +31,14 @@ class MemIndex : public SymbolIndex { fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref Callback) const override; - virtual void + void lookup(const LookupRequest &Req, llvm::function_ref Callback) const override; + void findOccurrences(const OccurrencesRequest &Req, + llvm::function_ref + Callback) const override; + private: std::shared_ptr> Symbols; // Index is a set of symbols that are deduplicated by symbol IDs. diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index da31f8b63..58d156481 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -7,6 +7,7 @@ // //===---------------------------------------------------------------------===// #include "Merge.h" +#include "../Logger.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/raw_ostream.h" namespace clang { @@ -74,6 +75,12 @@ class MergedIndex : public SymbolIndex { Callback(*Sym); } + void findOccurrences(const OccurrencesRequest &Req, + llvm::function_ref + Callback) const override { + log("findOccurrences is not implemented."); + } + private: const SymbolIndex *Dynamic, *Static; }; diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 2f04bef2e..fefc7b21b 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -893,6 +893,10 @@ class IndexRequestCollector : public SymbolIndex { void lookup(const LookupRequest &, llvm::function_ref) const override {} + void findOccurrences(const OccurrencesRequest &Req, + llvm::function_ref + Callback) const override {} + const std::vector allRequests() const { return Requests; } private: From 55bfabcc1bd75447d6338ffe6ff27c1624a8c15a Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Tue, 7 Aug 2018 08:57:52 +0000 Subject: [PATCH 014/686] [clangd] Share getSymbolID implementation. Summary: And remove all duplicated implementation. Reviewers: ioeric Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50375 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339116 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/AST.cpp | 8 ++++++++ clangd/AST.h | 4 ++++ clangd/CodeComplete.cpp | 5 +---- clangd/XRefs.cpp | 9 --------- clangd/index/SymbolCollector.cpp | 17 ++++++++--------- 5 files changed, 21 insertions(+), 22 deletions(-) diff --git a/clangd/AST.cpp b/clangd/AST.cpp index ec1862cd7..1344931a9 100644 --- a/clangd/AST.cpp +++ b/clangd/AST.cpp @@ -12,6 +12,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/Basic/SourceManager.h" +#include "clang/Index/USRGeneration.h" namespace clang { namespace clangd { @@ -53,5 +54,12 @@ std::string printQualifiedName(const NamedDecl &ND) { return QName; } +llvm::Optional getSymbolID(const Decl *D) { + llvm::SmallString<128> USR; + if (index::generateUSRForDecl(D, USR)) + return None; + return SymbolID(USR); +} + } // namespace clangd } // namespace clang diff --git a/clangd/AST.h b/clangd/AST.h index cb47c100b..9f00f829c 100644 --- a/clangd/AST.h +++ b/clangd/AST.h @@ -16,6 +16,7 @@ #include "clang/AST/Decl.h" #include "clang/Basic/SourceLocation.h" +#include "index/Index.h" namespace clang { class SourceManager; @@ -33,6 +34,9 @@ SourceLocation findNameLoc(const clang::Decl *D); /// like inline namespaces. std::string printQualifiedName(const NamedDecl &ND); +/// Gets the symbol ID for a declaration, if possible. +llvm::Optional getSymbolID(const Decl *D); + } // namespace clangd } // namespace clang diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index a0406591f..e7e38d816 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -396,10 +396,7 @@ llvm::Optional getSymbolID(const CodeCompletionResult &R) { switch (R.Kind) { case CodeCompletionResult::RK_Declaration: case CodeCompletionResult::RK_Pattern: { - llvm::SmallString<128> USR; - if (/*Ignore=*/clang::index::generateUSRForDecl(R.Declaration, USR)) - return None; - return SymbolID(USR); + return clang::clangd::getSymbolID(R.Declaration); } case CodeCompletionResult::RK_Macro: // FIXME: Macros do have USRs, but the CCR doesn't contain enough info. diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index 99ccd21f1..284428cc9 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -202,15 +202,6 @@ makeLocation(ParsedAST &AST, const SourceRange &ValSourceRange) { return L; } -// Get the symbol ID for a declaration, if possible. -llvm::Optional getSymbolID(const Decl *D) { - llvm::SmallString<128> USR; - if (index::generateUSRForDecl(D, USR)) { - return None; - } - return SymbolID(USR); -} - } // namespace std::vector findDefinitions(ParsedAST &AST, Position Pos, diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp index 5c80f3845..6c6a79245 100644 --- a/clangd/index/SymbolCollector.cpp +++ b/clangd/index/SymbolCollector.cpp @@ -320,21 +320,20 @@ bool SymbolCollector::handleDeclOccurence( if (!shouldCollectSymbol(*ND, *ASTCtx, Opts)) return true; - llvm::SmallString<128> USR; - if (index::generateUSRForDecl(ND, USR)) + auto ID = getSymbolID(ND); + if (!ID) return true; - SymbolID ID(USR); const NamedDecl &OriginalDecl = *cast(ASTNode.OrigD); - const Symbol *BasicSymbol = Symbols.find(ID); + const Symbol *BasicSymbol = Symbols.find(*ID); if (!BasicSymbol) // Regardless of role, ND is the canonical declaration. - BasicSymbol = addDeclaration(*ND, std::move(ID)); + BasicSymbol = addDeclaration(*ND, std::move(*ID)); else if (isPreferredDeclaration(OriginalDecl, Roles)) // If OriginalDecl is preferred, replace the existing canonical // declaration (e.g. a class forward declaration). There should be at most // one duplicate as we expect to see only one preferred declaration per // TU, because in practice they are definitions. - BasicSymbol = addDeclaration(OriginalDecl, std::move(ID)); + BasicSymbol = addDeclaration(OriginalDecl, std::move(*ID)); if (Roles & static_cast(index::SymbolRole::Definition)) addDefinition(OriginalDecl, *BasicSymbol); @@ -423,9 +422,9 @@ void SymbolCollector::finish() { } }; for (const NamedDecl *ND : ReferencedDecls) { - llvm::SmallString<128> USR; - if (!index::generateUSRForDecl(ND, USR)) - IncRef(SymbolID(USR)); + if (auto ID = getSymbolID(ND)) { + IncRef(*ID); + } } if (Opts.CollectMacro) { assert(PP); From 86961faaaf0b56b1c5dac2ed979987879cbc5294 Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Wed, 8 Aug 2018 08:59:29 +0000 Subject: [PATCH 015/686] Added functionality to suggest FixIts for conversion of '->' to '.' and vice versa. Summary: Added functionality to suggest FixIts for conversion of '->' to '.' and vice versa. Reviewers: ilya-biryukov Reviewed By: ilya-biryukov Subscribers: yvvan, ioeric, jkorous, arphaman, cfe-commits, kadircet Differential Revision: https://reviews.llvm.org/D50193 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339224 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 17 ++++- clangd/CodeComplete.h | 8 +++ clangd/Diagnostics.cpp | 9 --- clangd/Protocol.h | 4 ++ clangd/Quality.cpp | 7 ++ clangd/Quality.h | 2 + clangd/SourceCode.cpp | 9 +++ clangd/SourceCode.h | 5 ++ unittests/clangd/CodeCompleteTests.cpp | 96 ++++++++++++++++++++++++++ unittests/clangd/QualityTests.cpp | 22 ++++++ 10 files changed, 167 insertions(+), 12 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index e7e38d816..112a6fe20 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -22,6 +22,7 @@ #include "AST.h" #include "CodeCompletionStrings.h" #include "Compiler.h" +#include "Diagnostics.h" #include "FileDistance.h" #include "FuzzyMatch.h" #include "Headers.h" @@ -280,6 +281,10 @@ struct CodeCompletionBuilder { } Completion.Kind = toCompletionItemKind(C.SemaResult->Kind, C.SemaResult->Declaration); + for (const auto &FixIt : C.SemaResult->FixIts) { + Completion.FixIts.push_back( + toTextEdit(FixIt, ASTCtx.getSourceManager(), ASTCtx.getLangOpts())); + } } if (C.IndexResult) { Completion.Origin |= C.IndexResult->Origin; @@ -906,6 +911,7 @@ clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const { // the index can provide results from the preamble. // Tell Sema not to deserialize the preamble to look for results. Result.LoadExternal = !Index; + Result.IncludeFixIts = IncludeFixIts; return Result; } @@ -1090,8 +1096,8 @@ class CodeCompleteFlow { // Groups overloads if desired, to form CompletionCandidate::Bundles. // The bundles are scored and top results are returned, best to worst. std::vector - mergeResults(const std::vector &SemaResults, - const SymbolSlab &IndexResults) { + mergeResults(const std::vector &SemaResults, + const SymbolSlab &IndexResults) { trace::Span Tracer("Merge and score results"); std::vector Bundles; llvm::DenseMap BundleLookup; @@ -1272,13 +1278,18 @@ CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const { LSP.documentation = Documentation; LSP.sortText = sortText(Score.Total, Name); LSP.filterText = Name; + // FIXME(kadircet): Use LSP.textEdit instead of insertText, because it causes + // undesired behaviours. Like completing "this.^" into "this-push_back". LSP.insertText = RequiredQualifier + Name; if (Opts.EnableSnippets) LSP.insertText += SnippetSuffix; LSP.insertTextFormat = Opts.EnableSnippets ? InsertTextFormat::Snippet : InsertTextFormat::PlainText; + LSP.additionalTextEdits.reserve(FixIts.size() + (HeaderInsertion ? 1 : 0)); + for (const auto &FixIt : FixIts) + LSP.additionalTextEdits.push_back(FixIt); if (HeaderInsertion) - LSP.additionalTextEdits = {*HeaderInsertion}; + LSP.additionalTextEdits.push_back(*HeaderInsertion); return LSP; } diff --git a/clangd/CodeComplete.h b/clangd/CodeComplete.h index 884149dfb..c623be34a 100644 --- a/clangd/CodeComplete.h +++ b/clangd/CodeComplete.h @@ -77,6 +77,10 @@ struct CodeCompleteOptions { /// FIXME(ioeric): we might want a better way to pass the index around inside /// clangd. const SymbolIndex *Index = nullptr; + + /// Include completions that require small corrections, e.g. change '.' to + /// '->' on member access etc. + bool IncludeFixIts = false; }; // Semi-structured representation of a code-complete suggestion for our C++ API. @@ -115,6 +119,10 @@ struct CodeCompletion { // Present if Header is set and should be inserted to use this item. llvm::Optional HeaderInsertion; + /// Holds information about small corrections that needs to be done. Like + /// converting '->' to '.' on member access. + std::vector FixIts; + // Scores are used to rank completion items. struct Scores { // The score that items are ranked by. diff --git a/clangd/Diagnostics.cpp b/clangd/Diagnostics.cpp index 5a61567f2..3c293dde3 100644 --- a/clangd/Diagnostics.cpp +++ b/clangd/Diagnostics.cpp @@ -70,15 +70,6 @@ Range diagnosticRange(const clang::Diagnostic &D, const LangOptions &L) { return halfOpenToRange(M, R); } -TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, - const LangOptions &L) { - TextEdit Result; - Result.range = - halfOpenToRange(M, Lexer::makeFileCharRange(FixIt.RemoveRange, M, L)); - Result.newText = FixIt.CodeToInsert; - return Result; -} - bool isInsideMainFile(const SourceLocation Loc, const SourceManager &M) { return Loc.isValid() && M.isInMainFile(Loc); } diff --git a/clangd/Protocol.h b/clangd/Protocol.h index 5056dbef8..b0d810e87 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -171,6 +171,10 @@ struct TextEdit { /// The string to be inserted. For delete operations use an /// empty string. std::string newText; + + bool operator==(const TextEdit &rhs) const { + return newText == rhs.newText && range == rhs.range; + } }; bool fromJSON(const llvm::json::Value &, TextEdit &); llvm::json::Value toJSON(const TextEdit &); diff --git a/clangd/Quality.cpp b/clangd/Quality.cpp index 61195b566..8cf794b0f 100644 --- a/clangd/Quality.cpp +++ b/clangd/Quality.cpp @@ -292,6 +292,8 @@ void SymbolRelevanceSignals::merge(const CodeCompletionResult &SemaCCResult) { // Declarations are scoped, others (like macros) are assumed global. if (SemaCCResult.Declaration) Scope = std::min(Scope, computeScope(SemaCCResult.Declaration)); + + NeedsFixIts = !SemaCCResult.FixIts.empty(); } static std::pair proximityScore(llvm::StringRef SymbolURI, @@ -343,6 +345,10 @@ float SymbolRelevanceSignals::evaluate() const { Score *= 0.5; } + // Penalize for FixIts. + if (NeedsFixIts) + Score *= 0.5; + return Score; } @@ -350,6 +356,7 @@ raw_ostream &operator<<(raw_ostream &OS, const SymbolRelevanceSignals &S) { OS << formatv("=== Symbol relevance: {0}\n", S.evaluate()); OS << formatv("\tName match: {0}\n", S.NameMatch); OS << formatv("\tForbidden: {0}\n", S.Forbidden); + OS << formatv("\tNeedsFixIts: {0}\n", S.NeedsFixIts); OS << formatv("\tIsInstanceMember: {0}\n", S.IsInstanceMember); OS << formatv("\tContext: {0}\n", getCompletionKindString(S.Context)); OS << formatv("\tSymbol URI: {0}\n", S.SymbolURI); diff --git a/clangd/Quality.h b/clangd/Quality.h index 0aed496be..54778b9fa 100644 --- a/clangd/Quality.h +++ b/clangd/Quality.h @@ -77,6 +77,8 @@ struct SymbolRelevanceSignals { /// 0-1+ fuzzy-match score for unqualified name. Must be explicitly assigned. float NameMatch = 1; bool Forbidden = false; // Unavailable (e.g const) or inaccessible (private). + /// Whether fixits needs to be applied for that completion or not. + bool NeedsFixIts = false; URIDistance *FileProximityMatch = nullptr; /// This is used to calculate proximity between the index symbol and the diff --git a/clangd/SourceCode.cpp b/clangd/SourceCode.cpp index d302518b0..88ec2c956 100644 --- a/clangd/SourceCode.cpp +++ b/clangd/SourceCode.cpp @@ -199,5 +199,14 @@ getAbsoluteFilePath(const FileEntry *F, const SourceManager &SourceMgr) { return FilePath.str().str(); } +TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, + const LangOptions &L) { + TextEdit Result; + Result.range = + halfOpenToRange(M, Lexer::makeFileCharRange(FixIt.RemoveRange, M, L)); + Result.newText = FixIt.CodeToInsert; + return Result; +} + } // namespace clangd } // namespace clang diff --git a/clangd/SourceCode.h b/clangd/SourceCode.h index b67fe6ecf..cb0c67287 100644 --- a/clangd/SourceCode.h +++ b/clangd/SourceCode.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SOURCECODE_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SOURCECODE_H #include "Protocol.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceLocation.h" #include "clang/Tooling/Core/Replacement.h" @@ -64,6 +65,10 @@ std::vector replacementsToEdits(StringRef Code, /// Get the absolute file path of a given file entry. llvm::Optional getAbsoluteFilePath(const FileEntry *F, const SourceManager &SourceMgr); + +TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, + const LangOptions &L); + } // namespace clangd } // namespace clang #endif diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index fefc7b21b..5fd17d0a2 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -79,6 +79,23 @@ std::unique_ptr memIndex(std::vector Symbols) { return MemIndex::build(std::move(Slab).build()); } +CodeCompleteResult completions(ClangdServer &Server, StringRef TestCode, + Position point, + std::vector IndexSymbols = {}, + clangd::CodeCompleteOptions Opts = {}) { + std::unique_ptr OverrideIndex; + if (!IndexSymbols.empty()) { + assert(!Opts.Index && "both Index and IndexSymbols given!"); + OverrideIndex = memIndex(std::move(IndexSymbols)); + Opts.Index = OverrideIndex.get(); + } + + auto File = testPath("foo.cpp"); + runAddDocument(Server, File, TestCode); + auto CompletionList = cantFail(runCodeComplete(Server, File, point, Opts)); + return CompletionList; +} + CodeCompleteResult completions(ClangdServer &Server, StringRef Text, std::vector IndexSymbols = {}, clangd::CodeCompleteOptions Opts = {}) { @@ -1342,6 +1359,85 @@ TEST(CompletionTest, CodeCompletionContext) { EXPECT_THAT(Results.Context, CodeCompletionContext::CCC_DotMemberAccess); } +TEST(CompletionTest, FixItForArrowToDot) { + MockFSProvider FS; + MockCompilationDatabase CDB; + IgnoreDiagnostics DiagConsumer; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + Annotations TestCode( + R"cpp( + class Auxilary { + public: + void AuxFunction(); + }; + class ClassWithPtr { + public: + void MemberFunction(); + Auxilary* operator->() const; + Auxilary* Aux; + }; + void f() { + ClassWithPtr x; + x[[->]]^; + } + )cpp"); + auto Results = + completions(Server, TestCode.code(), TestCode.point(), {}, Opts); + EXPECT_EQ(Results.Completions.size(), 3u); + + TextEdit ReplacementEdit; + ReplacementEdit.range = TestCode.range(); + ReplacementEdit.newText = "."; + for (const auto &C : Results.Completions) { + EXPECT_TRUE(C.FixIts.size() == 1u || C.Name == "AuxFunction"); + if (!C.FixIts.empty()) + EXPECT_THAT(C.FixIts, ElementsAre(ReplacementEdit)); + } +} + +TEST(CompletionTest, FixItForDotToArrow) { + MockFSProvider FS; + MockCompilationDatabase CDB; + IgnoreDiagnostics DiagConsumer; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + Annotations TestCode( + R"cpp( + class Auxilary { + public: + void AuxFunction(); + }; + class ClassWithPtr { + public: + void MemberFunction(); + Auxilary* operator->() const; + Auxilary* Aux; + }; + void f() { + ClassWithPtr x; + x[[.]]^; + } + )cpp"); + auto Results = + completions(Server, TestCode.code(), TestCode.point(), {}, Opts); + EXPECT_EQ(Results.Completions.size(), 3u); + + TextEdit ReplacementEdit; + ReplacementEdit.range = TestCode.range(); + ReplacementEdit.newText = "->"; + for (const auto &C : Results.Completions) { + EXPECT_TRUE(C.FixIts.empty() || C.Name == "AuxFunction"); + if (!C.FixIts.empty()) { + EXPECT_THAT(C.FixIts, ElementsAre(ReplacementEdit)); + } + } +} + } // namespace } // namespace clangd } // namespace clang diff --git a/unittests/clangd/QualityTests.cpp b/unittests/clangd/QualityTests.cpp index 153eef498..1dcb39117 100644 --- a/unittests/clangd/QualityTests.cpp +++ b/unittests/clangd/QualityTests.cpp @@ -346,6 +346,28 @@ TEST(QualityTests, ConstructorQuality) { EXPECT_EQ(Q.Category, SymbolQualitySignals::Constructor); } +TEST(QualityTests, ItemWithFixItsRankedDown) { + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + + auto Header = TestTU::withHeaderCode(R"cpp( + int x; + )cpp"); + auto AST = Header.build(); + + SymbolRelevanceSignals RelevanceWithFixIt; + RelevanceWithFixIt.merge(CodeCompletionResult(&findDecl(AST, "x"), 0, nullptr, + false, true, {FixItHint{}})); + EXPECT_TRUE(RelevanceWithFixIt.NeedsFixIts); + + SymbolRelevanceSignals RelevanceWithoutFixIt; + RelevanceWithoutFixIt.merge( + CodeCompletionResult(&findDecl(AST, "x"), 0, nullptr, false, true, {})); + EXPECT_FALSE(RelevanceWithoutFixIt.NeedsFixIts); + + EXPECT_LT(RelevanceWithFixIt.evaluate(), RelevanceWithoutFixIt.evaluate()); +} + } // namespace } // namespace clangd } // namespace clang From 72687f7e598b535e6707a2d5320f8daf5600f1c1 Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Thu, 9 Aug 2018 02:16:18 +0000 Subject: [PATCH 016/686] [CMake] Use normalized Windows target triples Changes the default Windows target triple returned by GetHostTriple.cmake from the old environment names (which we wanted to move away from) to newer, normalized ones. This also requires updating all tests to use the new systems names in constraints. Differential Revision: https://reviews.llvm.org/D47381 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339307 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clangd/did-change-configuration-params.test | 2 +- test/clangd/test-uri-posix.test | 2 +- test/clangd/test-uri-windows.test | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/clangd/did-change-configuration-params.test b/test/clangd/did-change-configuration-params.test index e0f936399..51b4a8747 100644 --- a/test/clangd/did-change-configuration-params.test +++ b/test/clangd/did-change-configuration-params.test @@ -1,6 +1,6 @@ # RUN: clangd -compile_args_from=lsp -lit-test < %s 2> %t | FileCheck -strict-whitespace %s # RUN: cat %t | FileCheck --check-prefix=ERR %s -# UNSUPPORTED: mingw32,win32 +# UNSUPPORTED: windows-gnu,windows-msvc {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} --- {"jsonrpc":"2.0","method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabaseChanges":{"/clangd-test/foo.c": {"workingDirectory":"/clangd-test", "compilationCommand": ["clang", "-c", "foo.c"]}}}}} diff --git a/test/clangd/test-uri-posix.test b/test/clangd/test-uri-posix.test index 15cc09f60..2b67fa03e 100644 --- a/test/clangd/test-uri-posix.test +++ b/test/clangd/test-uri-posix.test @@ -1,5 +1,5 @@ # RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s -# UNSUPPORTED: mingw32,win32 +# UNSUPPORTED: windows-gnu,windows-msvc # Test authority-less URI {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} --- diff --git a/test/clangd/test-uri-windows.test b/test/clangd/test-uri-windows.test index 7ec290e7b..381c48faf 100644 --- a/test/clangd/test-uri-windows.test +++ b/test/clangd/test-uri-windows.test @@ -1,5 +1,5 @@ # RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s -# REQUIRES: mingw32 || win32 +# REQUIRES: windows-gnu || windows-msvc # Test authority-less URI {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} --- From 6d085e0a3328726aeb2a8de7b5c6ce4724e297fd Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Thu, 9 Aug 2018 09:05:45 +0000 Subject: [PATCH 017/686] [clangd] Record the file being processed in a TUScheduler thread in context. Summary: This allows implementations like different symbol indexes to know what the current active file is. For example, some customized index implementation might decide to only return results for some files. Reviewers: ilya-biryukov Reviewed By: ilya-biryukov Subscribers: javed.absar, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50446 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339320 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/TUScheduler.cpp | 23 +++++++++++++----- clangd/TUScheduler.h | 8 +++++++ unittests/clangd/TUSchedulerTests.cpp | 34 ++++++++++++++++----------- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/clangd/TUScheduler.cpp b/clangd/TUScheduler.cpp index 81a75bbc2..cae4b0911 100644 --- a/clangd/TUScheduler.cpp +++ b/clangd/TUScheduler.cpp @@ -63,6 +63,14 @@ namespace { class ASTWorker; } +static const clang::clangd::Key kFileBeingProcessed; + +llvm::Optional TUScheduler::getFileBeingProcessedInContext() { + if (auto *File = Context::current().get(kFileBeingProcessed)) + return StringRef(*File); + return llvm::None; +} + /// An LRU cache of idle ASTs. /// Because we want to limit the overall number of these we retain, the cache /// owns ASTs (and may evict them) while their workers are idle. @@ -491,8 +499,9 @@ void ASTWorker::startTask(llvm::StringRef Name, { std::lock_guard Lock(Mutex); assert(!Done && "running a task after stop()"); - Requests.push_back({std::move(Task), Name, steady_clock::now(), - Context::current().clone(), UpdateType}); + Requests.push_back( + {std::move(Task), Name, steady_clock::now(), + Context::current().derive(kFileBeingProcessed, FileName), UpdateType}); } RequestsCV.notify_all(); } @@ -734,10 +743,12 @@ void TUScheduler::runWithPreamble( Action(InputsAndPreamble{Contents, Command, Preamble.get()}); }; - PreambleTasks->runAsync("task:" + llvm::sys::path::filename(File), - Bind(Task, std::string(Name), std::string(File), - It->second->Contents, It->second->Command, - Context::current().clone(), std::move(Action))); + PreambleTasks->runAsync( + "task:" + llvm::sys::path::filename(File), + Bind(Task, std::string(Name), std::string(File), It->second->Contents, + It->second->Command, + Context::current().derive(kFileBeingProcessed, File), + std::move(Action))); } std::vector> diff --git a/clangd/TUScheduler.h b/clangd/TUScheduler.h index af1ff3656..90f3fe7b5 100644 --- a/clangd/TUScheduler.h +++ b/clangd/TUScheduler.h @@ -122,6 +122,13 @@ class TUScheduler { /// an LRU cache. class ASTCache; + // The file being built/processed in the current thread. This is a hack in + // order to get the file name into the index implementations. Do not depend on + // this inside clangd. + // FIXME: remove this when there is proper index support via build system + // integration. + static llvm::Optional getFileBeingProcessedInContext(); + private: const bool StorePreamblesInMemory; const std::shared_ptr PCHOps; @@ -135,6 +142,7 @@ class TUScheduler { llvm::Optional WorkerThreads; std::chrono::steady_clock::duration UpdateDebounce; }; + } // namespace clangd } // namespace clang diff --git a/unittests/clangd/TUSchedulerTests.cpp b/unittests/clangd/TUSchedulerTests.cpp index 138a4e2ed..f485b0b8c 100644 --- a/unittests/clangd/TUSchedulerTests.cpp +++ b/unittests/clangd/TUSchedulerTests.cpp @@ -197,20 +197,22 @@ TEST_F(TUSchedulerTests, ManyUpdates) { { WithContextValue WithNonce(NonceKey, ++Nonce); S.update(File, Inputs, WantDiagnostics::Auto, - [Nonce, &Mut, + [File, Nonce, &Mut, &TotalUpdates](llvm::Optional> Diags) { EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce)); std::lock_guard Lock(Mut); ++TotalUpdates; + EXPECT_EQ(File, + *TUScheduler::getFileBeingProcessedInContext()); }); } { WithContextValue WithNonce(NonceKey, ++Nonce); S.runWithAST("CheckAST", File, - [Inputs, Nonce, &Mut, + [File, Inputs, Nonce, &Mut, &TotalASTReads](llvm::Expected AST) { EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce)); @@ -221,23 +223,27 @@ TEST_F(TUSchedulerTests, ManyUpdates) { std::lock_guard Lock(Mut); ++TotalASTReads; + EXPECT_EQ( + File, + *TUScheduler::getFileBeingProcessedInContext()); }); } { WithContextValue WithNonce(NonceKey, ++Nonce); - S.runWithPreamble("CheckPreamble", File, - [Inputs, Nonce, &Mut, &TotalPreambleReads]( - llvm::Expected Preamble) { - EXPECT_THAT(Context::current().get(NonceKey), - Pointee(Nonce)); - - ASSERT_TRUE((bool)Preamble); - EXPECT_EQ(Preamble->Contents, Inputs.Contents); - - std::lock_guard Lock(Mut); - ++TotalPreambleReads; - }); + S.runWithPreamble( + "CheckPreamble", File, + [File, Inputs, Nonce, &Mut, &TotalPreambleReads]( + llvm::Expected Preamble) { + EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce)); + + ASSERT_TRUE((bool)Preamble); + EXPECT_EQ(Preamble->Contents, Inputs.Contents); + + std::lock_guard Lock(Mut); + ++TotalPreambleReads; + EXPECT_EQ(File, *TUScheduler::getFileBeingProcessedInContext()); + }); } } } From 0a2b7a8dca76ba0f8fc24ebc81461ab6bc0d89fd Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Thu, 9 Aug 2018 09:25:26 +0000 Subject: [PATCH 018/686] [clangd] Try to fix buildbot after r339320. http://lab.llvm.org:8011/builders/clang-cmake-armv8-quick/builds/5487 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339322 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/TUScheduler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/TUScheduler.cpp b/clangd/TUScheduler.cpp index cae4b0911..f985e7d7f 100644 --- a/clangd/TUScheduler.cpp +++ b/clangd/TUScheduler.cpp @@ -63,7 +63,7 @@ namespace { class ASTWorker; } -static const clang::clangd::Key kFileBeingProcessed; +static clang::clangd::Key kFileBeingProcessed; llvm::Optional TUScheduler::getFileBeingProcessedInContext() { if (auto *File = Context::current().get(kFileBeingProcessed)) From 4cacbf2c444f87871805c14748142cd9d92ff716 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Thu, 9 Aug 2018 22:42:26 +0000 Subject: [PATCH 019/686] Port getLocStart -> getBeginLoc Reviewers: javed.absar Subscribers: nemanjai, kbarton, ilya-biryukov, ioeric, jkorous, arphaman, jfb, cfe-commits Differential Revision: https://reviews.llvm.org/D50354 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339400 91177308-0d34-0410-b5e6-96231b3b80d8 --- change-namespace/ChangeNamespace.cpp | 12 ++++---- clang-doc/Mapper.cpp | 4 +-- clang-move/ClangMove.cpp | 10 +++---- .../abseil/StringFindStartswithCheck.cpp | 6 ++-- clang-tidy/android/CloexecCheck.cpp | 6 ++-- .../ComparisonInTempFailureRetryCheck.cpp | 6 ++-- clang-tidy/boost/UseToStringCheck.cpp | 6 ++-- clang-tidy/bugprone/ArgumentCommentCheck.cpp | 4 +-- clang-tidy/bugprone/AssertSideEffectCheck.cpp | 2 +- .../BoolPointerImplicitConversionCheck.cpp | 6 ++-- .../bugprone/CopyConstructorInitCheck.cpp | 2 +- clang-tidy/bugprone/DanglingHandleCheck.cpp | 2 +- clang-tidy/bugprone/InaccurateEraseCheck.cpp | 2 +- .../bugprone/IncorrectRoundingsCheck.cpp | 2 +- clang-tidy/bugprone/IntegerDivisionCheck.cpp | 2 +- .../MisplacedOperatorInStrlenInAllocCheck.cpp | 5 ++-- .../bugprone/MisplacedWideningCastCheck.cpp | 6 ++-- .../bugprone/MoveForwardingReferenceCheck.cpp | 2 +- .../bugprone/MultipleStatementMacroCheck.cpp | 8 +++--- clang-tidy/bugprone/SizeofContainerCheck.cpp | 2 +- clang-tidy/bugprone/SizeofExpressionCheck.cpp | 28 +++++++++---------- .../bugprone/StringConstructorCheck.cpp | 2 +- .../bugprone/StringIntegerAssignmentCheck.cpp | 2 +- .../StringLiteralWithEmbeddedNulCheck.cpp | 4 +-- .../bugprone/SuspiciousMemsetUsageCheck.cpp | 6 ++-- .../bugprone/SuspiciousMissingCommaCheck.cpp | 2 +- .../bugprone/SuspiciousSemicolonCheck.cpp | 4 +-- .../bugprone/SuspiciousStringCompareCheck.cpp | 17 +++++------ clang-tidy/bugprone/SwappedArgumentsCheck.cpp | 2 +- .../bugprone/TerminatingContinueCheck.cpp | 2 +- .../bugprone/ThrowKeywordMissingCheck.cpp | 2 +- .../UndefinedMemoryManipulationCheck.cpp | 4 +-- .../bugprone/UndelegatedConstructorCheck.cpp | 2 +- clang-tidy/bugprone/UnusedRaiiCheck.cpp | 4 +-- .../bugprone/UnusedReturnValueCheck.cpp | 4 +-- clang-tidy/bugprone/VirtualNearMissCheck.cpp | 2 +- clang-tidy/cert/LimitedRandomnessCheck.cpp | 2 +- .../cppcoreguidelines/AvoidGotoCheck.cpp | 4 +-- .../NarrowingConversionsCheck.cpp | 4 +-- .../cppcoreguidelines/NoMallocCheck.cpp | 4 +-- .../cppcoreguidelines/OwningMemoryCheck.cpp | 24 ++++++++-------- .../ProTypeCstyleCastCheck.cpp | 8 +++--- .../ProTypeMemberInitCheck.cpp | 12 ++++---- clang-tidy/fuchsia/DefaultArgumentsCheck.cpp | 16 +++++------ .../fuchsia/MultipleInheritanceCheck.cpp | 5 ++-- .../fuchsia/OverloadedOperatorCheck.cpp | 4 +-- .../StaticallyConstructedObjectsCheck.cpp | 2 +- clang-tidy/fuchsia/TrailingReturnCheck.cpp | 2 +- .../fuchsia/VirtualInheritanceCheck.cpp | 2 +- clang-tidy/google/AvoidCStyleCastsCheck.cpp | 8 +++--- clang-tidy/google/ExplicitMakePairCheck.cpp | 6 ++-- .../google/GlobalNamesInHeadersCheck.cpp | 8 +++--- clang-tidy/google/IntegerTypesCheck.cpp | 2 +- clang-tidy/google/OverloadedUnaryAndCheck.cpp | 2 +- .../google/UnnamedNamespaceInHeaderCheck.cpp | 2 +- .../google/UsingNamespaceDirectiveCheck.cpp | 2 +- clang-tidy/hicpp/ExceptionBaseclassCheck.cpp | 4 +-- .../hicpp/MultiwayPathsCoveredCheck.cpp | 8 +++--- clang-tidy/hicpp/SignedBitwiseCheck.cpp | 6 ++-- clang-tidy/llvm/TwineLocalCheck.cpp | 2 +- clang-tidy/misc/DefinitionsInHeadersCheck.cpp | 2 +- clang-tidy/misc/RedundantExpressionCheck.cpp | 4 +-- clang-tidy/misc/StaticAssertCheck.cpp | 4 +-- .../ThrowByValueCatchByReferenceCheck.cpp | 8 +++--- .../UnconventionalAssignOperatorCheck.cpp | 4 +-- clang-tidy/misc/UnusedAliasDeclsCheck.cpp | 2 +- clang-tidy/misc/UnusedParametersCheck.cpp | 4 +-- clang-tidy/misc/UnusedUsingDeclsCheck.cpp | 2 +- clang-tidy/modernize/AvoidBindCheck.cpp | 4 +-- clang-tidy/modernize/MakeSmartPtrCheck.cpp | 4 +-- clang-tidy/modernize/PassByValueCheck.cpp | 4 +-- .../modernize/RawStringLiteralCheck.cpp | 6 ++-- .../modernize/RedundantVoidArgCheck.cpp | 10 +++---- .../modernize/ReplaceRandomShuffleCheck.cpp | 10 +++---- clang-tidy/modernize/ShrinkToFitCheck.cpp | 4 +-- .../modernize/UnaryStaticAssertCheck.cpp | 2 +- clang-tidy/modernize/UseBoolLiteralsCheck.cpp | 2 +- .../modernize/UseDefaultMemberInitCheck.cpp | 2 +- .../modernize/UseEqualsDefaultCheck.cpp | 2 +- clang-tidy/modernize/UseNullptrCheck.cpp | 10 +++---- .../modernize/UseTransparentFunctorsCheck.cpp | 4 +-- .../modernize/UseUncaughtExceptionsCheck.cpp | 6 ++-- clang-tidy/modernize/UseUsingCheck.cpp | 2 +- clang-tidy/objc/AvoidNSErrorInitCheck.cpp | 2 +- clang-tidy/objc/AvoidSpinlockCheck.cpp | 2 +- .../performance/FasterStringFindCheck.cpp | 11 ++++---- clang-tidy/performance/ForRangeCopyCheck.cpp | 2 +- .../ImplicitConversionInLoopCheck.cpp | 2 +- .../performance/InefficientAlgorithmCheck.cpp | 4 +-- .../InefficientVectorOperationCheck.cpp | 6 ++-- clang-tidy/performance/MoveConstArgCheck.cpp | 2 +- .../TypePromotionInMathFnCheck.cpp | 2 +- .../UnnecessaryValueParamCheck.cpp | 10 +++---- .../readability/AvoidConstParamsInDecls.cpp | 6 ++-- .../BracesAroundStatementsCheck.cpp | 4 +-- .../readability/ContainerSizeEmptyCheck.cpp | 4 +-- .../readability/DeleteNullPointerCheck.cpp | 4 +-- .../readability/DeletedDefaultCheck.cpp | 4 +-- clang-tidy/readability/FunctionSizeCheck.cpp | 6 ++-- .../readability/IdentifierNamingCheck.cpp | 2 +- .../ImplicitBoolConversionCheck.cpp | 16 +++++------ ...onsistentDeclarationParameterNameCheck.cpp | 2 +- .../MisleadingIndentationCheck.cpp | 6 ++-- .../readability/MisplacedArrayIndexCheck.cpp | 2 +- .../readability/NamedParameterCheck.cpp | 4 +-- .../readability/NamespaceCommentCheck.cpp | 4 +-- .../readability/NonConstParameterCheck.cpp | 2 +- .../readability/RedundantDeclarationCheck.cpp | 2 +- .../readability/RedundantSmartptrGetCheck.cpp | 2 +- .../readability/RedundantStringCStrCheck.cpp | 2 +- .../readability/SimplifyBooleanExprCheck.cpp | 22 +++++++-------- .../SimplifySubscriptExprCheck.cpp | 2 +- .../StaticAccessedThroughInstanceCheck.cpp | 4 +-- clang-tidy/readability/StringCompareCheck.cpp | 6 ++-- .../UniqueptrDeleteReleaseCheck.cpp | 6 ++-- clang-tidy/utils/ASTUtils.cpp | 4 +-- clang-tidy/utils/NamespaceAliaser.cpp | 2 +- clang-tidy/utils/UsingInserter.cpp | 2 +- clangd/CodeComplete.cpp | 2 +- clangd/CodeCompletionStrings.cpp | 4 +-- clangd/XRefs.cpp | 2 +- tool-template/ToolTemplate.cpp | 4 +-- unittests/clang-tidy/IncludeInserterTest.cpp | 2 +- unittests/clang-tidy/NamespaceAliaserTest.cpp | 10 +++---- .../OverlappingReplacementsTest.cpp | 6 ++-- unittests/clang-tidy/UsingInserterTest.cpp | 4 +-- 126 files changed, 309 insertions(+), 311 deletions(-) diff --git a/change-namespace/ChangeNamespace.cpp b/change-namespace/ChangeNamespace.cpp index 378ffe1f9..85b6b1364 100644 --- a/change-namespace/ChangeNamespace.cpp +++ b/change-namespace/ChangeNamespace.cpp @@ -46,7 +46,7 @@ SourceLocation startLocationForType(TypeLoc TLoc) { return NestedNameSpecifier.getBeginLoc(); TLoc = TLoc.getNextTypeLoc(); } - return TLoc.getLocStart(); + return TLoc.getBeginLoc(); } SourceLocation endLocationForType(TypeLoc TLoc) { @@ -275,7 +275,7 @@ bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) { // Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx. bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D, const DeclContext *DeclCtx, SourceLocation Loc) { - SourceLocation DeclLoc = SM.getSpellingLoc(D->getLocStart()); + SourceLocation DeclLoc = SM.getSpellingLoc(D->getBeginLoc()); Loc = SM.getSpellingLoc(Loc); return SM.isBeforeInTranslationUnit(DeclLoc, Loc) && (SM.getFileID(DeclLoc) == SM.getFileID(Loc) && @@ -634,7 +634,7 @@ static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl, const SourceManager &SM, const LangOptions &LangOpts) { std::unique_ptr Lex = - getLexerStartingFromLoc(NsDecl->getLocStart(), SM, LangOpts); + getLexerStartingFromLoc(NsDecl->getBeginLoc(), SM, LangOpts); assert(Lex.get() && "Failed to create lexer from the beginning of namespace."); if (!Lex.get()) @@ -709,7 +709,7 @@ void ChangeNamespaceTool::moveOldNamespace( void ChangeNamespaceTool::moveClassForwardDeclaration( const ast_matchers::MatchFinder::MatchResult &Result, const NamedDecl *FwdDecl) { - SourceLocation Start = FwdDecl->getLocStart(); + SourceLocation Start = FwdDecl->getBeginLoc(); SourceLocation End = FwdDecl->getLocEnd(); const SourceManager &SM = *Result.SourceManager; SourceLocation AfterSemi = Lexer::findLocationAfterToken( @@ -879,7 +879,7 @@ void ChangeNamespaceTool::fixTypeLoc( if (!llvm::StringRef(D->getQualifiedNameAsString()) .startswith(OldNamespace + "::")) return false; - auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getLocStart()); + auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getBeginLoc()); if (ExpansionLoc.isInvalid()) return false; llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc); @@ -910,7 +910,7 @@ void ChangeNamespaceTool::fixTypeLoc( void ChangeNamespaceTool::fixUsingShadowDecl( const ast_matchers::MatchFinder::MatchResult &Result, const UsingDecl *UsingDeclaration) { - SourceLocation Start = UsingDeclaration->getLocStart(); + SourceLocation Start = UsingDeclaration->getBeginLoc(); SourceLocation End = UsingDeclaration->getLocEnd(); if (Start.isInvalid() || End.isInvalid()) return; diff --git a/clang-doc/Mapper.cpp b/clang-doc/Mapper.cpp index 4456c1ed9..71e940476 100644 --- a/clang-doc/Mapper.cpp +++ b/clang-doc/Mapper.cpp @@ -78,13 +78,13 @@ MapASTVisitor::getComment(const NamedDecl *D, const ASTContext &Context) const { int MapASTVisitor::getLine(const NamedDecl *D, const ASTContext &Context) const { - return Context.getSourceManager().getPresumedLoc(D->getLocStart()).getLine(); + return Context.getSourceManager().getPresumedLoc(D->getBeginLoc()).getLine(); } llvm::StringRef MapASTVisitor::getFile(const NamedDecl *D, const ASTContext &Context) const { return Context.getSourceManager() - .getPresumedLoc(D->getLocStart()) + .getPresumedLoc(D->getBeginLoc()) .getFilename(); } diff --git a/clang-move/ClangMove.cpp b/clang-move/ClangMove.cpp index b7a9363dc..80ce8e619 100644 --- a/clang-move/ClangMove.cpp +++ b/clang-move/ClangMove.cpp @@ -117,7 +117,7 @@ AST_POLYMORPHIC_MATCHER_P(isExpansionInFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc), std::string, AbsoluteFilePath) { auto &SourceManager = Finder->getASTContext().getSourceManager(); - auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getLocStart()); + auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getBeginLoc()); if (ExpansionLoc.isInvalid()) return false; auto FileEntry = @@ -323,7 +323,7 @@ clang::CharSourceRange getFullRange(const clang::Decl *D, const clang::LangOptions &options = clang::LangOptions()) { const auto &SM = D->getASTContext().getSourceManager(); - clang::SourceRange Full(SM.getExpansionLoc(D->getLocStart()), + clang::SourceRange Full(SM.getExpansionLoc(D->getBeginLoc()), getLocForEndOfDecl(D)); // Expand to comments that are associated with the Decl. if (const auto *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) { @@ -331,8 +331,8 @@ getFullRange(const clang::Decl *D, Full.setEnd(Comment->getLocEnd()); // FIXME: Don't delete a preceding comment, if there are no other entities // it could refer to. - if (SM.isBeforeInTranslationUnit(Comment->getLocStart(), Full.getBegin())) - Full.setBegin(Comment->getLocStart()); + if (SM.isBeforeInTranslationUnit(Comment->getBeginLoc(), Full.getBegin())) + Full.setBegin(Comment->getBeginLoc()); } return clang::CharSourceRange::getCharRange(Full); @@ -351,7 +351,7 @@ bool isInHeaderFile(const clang::Decl *D, const auto &SM = D->getASTContext().getSourceManager(); if (OldHeader.empty()) return false; - auto ExpansionLoc = SM.getExpansionLoc(D->getLocStart()); + auto ExpansionLoc = SM.getExpansionLoc(D->getBeginLoc()); if (ExpansionLoc.isInvalid()) return false; diff --git a/clang-tidy/abseil/StringFindStartswithCheck.cpp b/clang-tidy/abseil/StringFindStartswithCheck.cpp index 7a94c1055..2701c24bb 100644 --- a/clang-tidy/abseil/StringFindStartswithCheck.cpp +++ b/clang-tidy/abseil/StringFindStartswithCheck.cpp @@ -71,7 +71,7 @@ void StringFindStartswithCheck::check(const MatchFinder::MatchResult &Result) { ->getImplicitObjectArgument(); assert(Haystack != nullptr); - if (ComparisonExpr->getLocStart().isMacroID()) + if (ComparisonExpr->getBeginLoc().isMacroID()) return; // Get the source code blocks (as characters) for both the string object @@ -94,7 +94,7 @@ void StringFindStartswithCheck::check(const MatchFinder::MatchResult &Result) { // Create the warning message and a FixIt hint replacing the original expr. auto Diagnostic = - diag(ComparisonExpr->getLocStart(), + diag(ComparisonExpr->getBeginLoc(), (StringRef("use ") + StartswithStr + " instead of find() " + ComparisonExpr->getOpcodeStr() + " 0") .str()); @@ -107,7 +107,7 @@ void StringFindStartswithCheck::check(const MatchFinder::MatchResult &Result) { // Create a preprocessor #include FixIt hint (CreateIncludeInsertion checks // whether this already exists). auto IncludeHint = IncludeInserter->CreateIncludeInsertion( - Source.getFileID(ComparisonExpr->getLocStart()), AbseilStringsMatchHeader, + Source.getFileID(ComparisonExpr->getBeginLoc()), AbseilStringsMatchHeader, false); if (IncludeHint) { Diagnostic << *IncludeHint; diff --git a/clang-tidy/android/CloexecCheck.cpp b/clang-tidy/android/CloexecCheck.cpp index 8473f1ab8..686c749b8 100644 --- a/clang-tidy/android/CloexecCheck.cpp +++ b/clang-tidy/android/CloexecCheck.cpp @@ -25,7 +25,7 @@ namespace { // end of the string. Else, add . std::string buildFixMsgForStringFlag(const Expr *Arg, const SourceManager &SM, const LangOptions &LangOpts, char Mode) { - if (Arg->getLocStart().isMacroID()) + if (Arg->getBeginLoc().isMacroID()) return (Lexer::getSourceText( CharSourceRange::getTokenRange(Arg->getSourceRange()), SM, LangOpts) + @@ -75,7 +75,7 @@ void CloexecCheck::insertMacroFlag(const MatchFinder::MatchResult &Result, void CloexecCheck::replaceFunc(const MatchFinder::MatchResult &Result, StringRef WarningMsg, StringRef FixMsg) { const auto *MatchedCall = Result.Nodes.getNodeAs(FuncBindingStr); - diag(MatchedCall->getLocStart(), WarningMsg) + diag(MatchedCall->getBeginLoc(), WarningMsg) << FixItHint::CreateReplacement(MatchedCall->getSourceRange(), FixMsg); } @@ -94,7 +94,7 @@ void CloexecCheck::insertStringFlag( const std::string &ReplacementText = buildFixMsgForStringFlag( ModeArg, *Result.SourceManager, Result.Context->getLangOpts(), Mode); - diag(ModeArg->getLocStart(), "use %0 mode '%1' to set O_CLOEXEC") + diag(ModeArg->getBeginLoc(), "use %0 mode '%1' to set O_CLOEXEC") << FD << std::string(1, Mode) << FixItHint::CreateReplacement(ModeArg->getSourceRange(), ReplacementText); diff --git a/clang-tidy/android/ComparisonInTempFailureRetryCheck.cpp b/clang-tidy/android/ComparisonInTempFailureRetryCheck.cpp index 49db53a03..a98eb8ca7 100644 --- a/clang-tidy/android/ComparisonInTempFailureRetryCheck.cpp +++ b/clang-tidy/android/ComparisonInTempFailureRetryCheck.cpp @@ -21,15 +21,15 @@ namespace android { namespace { AST_MATCHER(BinaryOperator, isRHSATempFailureRetryArg) { - if (!Node.getLocStart().isMacroID()) + if (!Node.getBeginLoc().isMacroID()) return false; const SourceManager &SM = Finder->getASTContext().getSourceManager(); - if (!SM.isMacroArgExpansion(Node.getRHS()->IgnoreParenCasts()->getLocStart())) + if (!SM.isMacroArgExpansion(Node.getRHS()->IgnoreParenCasts()->getBeginLoc())) return false; const LangOptions &Opts = Finder->getASTContext().getLangOpts(); - SourceLocation LocStart = Node.getLocStart(); + SourceLocation LocStart = Node.getBeginLoc(); while (LocStart.isMacroID()) { SourceLocation Invocation = SM.getImmediateMacroCallerLoc(LocStart); Token Tok; diff --git a/clang-tidy/boost/UseToStringCheck.cpp b/clang-tidy/boost/UseToStringCheck.cpp index 1775440d5..259f3dfd4 100644 --- a/clang-tidy/boost/UseToStringCheck.cpp +++ b/clang-tidy/boost/UseToStringCheck.cpp @@ -56,7 +56,7 @@ void UseToStringCheck::check(const MatchFinder::MatchResult &Result) { else return; - auto Loc = Call->getLocStart(); + auto Loc = Call->getBeginLoc(); auto Diag = diag(Loc, "use std::to_%0 instead of boost::lexical_cast") << StringType; @@ -65,8 +65,8 @@ void UseToStringCheck::check(const MatchFinder::MatchResult &Result) { return; Diag << FixItHint::CreateReplacement( - CharSourceRange::getCharRange(Call->getLocStart(), - Call->getArg(0)->getLocStart()), + CharSourceRange::getCharRange(Call->getBeginLoc(), + Call->getArg(0)->getBeginLoc()), (llvm::Twine("std::to_") + StringType + "(").str()); } diff --git a/clang-tidy/bugprone/ArgumentCommentCheck.cpp b/clang-tidy/bugprone/ArgumentCommentCheck.cpp index a8da703ed..34d1712e2 100644 --- a/clang-tidy/bugprone/ArgumentCommentCheck.cpp +++ b/clang-tidy/bugprone/ArgumentCommentCheck.cpp @@ -242,7 +242,7 @@ void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx, } CharSourceRange BeforeArgument = - makeFileCharRange(ArgBeginLoc, Args[I]->getLocStart()); + makeFileCharRange(ArgBeginLoc, Args[I]->getBeginLoc()); ArgBeginLoc = Args[I]->getLocEnd(); std::vector> Comments; @@ -251,7 +251,7 @@ void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx, } else { // Fall back to parsing back from the start of the argument. CharSourceRange ArgsRange = makeFileCharRange( - Args[I]->getLocStart(), Args[NumArgs - 1]->getLocEnd()); + Args[I]->getBeginLoc(), Args[NumArgs - 1]->getLocEnd()); Comments = getCommentsBeforeLoc(Ctx, ArgsRange.getBegin()); } diff --git a/clang-tidy/bugprone/AssertSideEffectCheck.cpp b/clang-tidy/bugprone/AssertSideEffectCheck.cpp index 244e75521..c747980b9 100644 --- a/clang-tidy/bugprone/AssertSideEffectCheck.cpp +++ b/clang-tidy/bugprone/AssertSideEffectCheck.cpp @@ -102,7 +102,7 @@ void AssertSideEffectCheck::registerMatchers(MatchFinder *Finder) { void AssertSideEffectCheck::check(const MatchFinder::MatchResult &Result) { const SourceManager &SM = *Result.SourceManager; const LangOptions LangOpts = getLangOpts(); - SourceLocation Loc = Result.Nodes.getNodeAs("condStmt")->getLocStart(); + SourceLocation Loc = Result.Nodes.getNodeAs("condStmt")->getBeginLoc(); StringRef AssertMacroName; while (Loc.isValid() && Loc.isMacroID()) { diff --git a/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp b/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp index ed2c2db95..5f33697d6 100644 --- a/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp +++ b/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp @@ -36,7 +36,7 @@ void BoolPointerImplicitConversionCheck::check( auto *Var = Result.Nodes.getNodeAs("expr"); // Ignore macros. - if (Var->getLocStart().isMacroID()) + if (Var->getBeginLoc().isMacroID()) return; // Only allow variable accesses for now, no function calls or member exprs. @@ -63,9 +63,9 @@ void BoolPointerImplicitConversionCheck::check( .empty()) return; - diag(Var->getLocStart(), "dubious check of 'bool *' against 'nullptr', did " + diag(Var->getBeginLoc(), "dubious check of 'bool *' against 'nullptr', did " "you mean to dereference it?") - << FixItHint::CreateInsertion(Var->getLocStart(), "*"); + << FixItHint::CreateInsertion(Var->getBeginLoc(), "*"); } } // namespace bugprone diff --git a/clang-tidy/bugprone/CopyConstructorInitCheck.cpp b/clang-tidy/bugprone/CopyConstructorInitCheck.cpp index 151e56c07..170ce4525 100644 --- a/clang-tidy/bugprone/CopyConstructorInitCheck.cpp +++ b/clang-tidy/bugprone/CopyConstructorInitCheck.cpp @@ -104,7 +104,7 @@ void CopyConstructorInitCheck::check(const MatchFinder::MatchResult &Result) { SourceLocation FixItLoc; // There is no initialization list in this constructor. if (!HasWrittenInitializer) { - FixItLoc = Ctor->getBody()->getLocStart(); + FixItLoc = Ctor->getBody()->getBeginLoc(); FixItMsg = " : " + FixItMsg; } else { // We apply the missing ctors at the beginning of the initialization list. diff --git a/clang-tidy/bugprone/DanglingHandleCheck.cpp b/clang-tidy/bugprone/DanglingHandleCheck.cpp index a22dcad65..81b799ede 100644 --- a/clang-tidy/bugprone/DanglingHandleCheck.cpp +++ b/clang-tidy/bugprone/DanglingHandleCheck.cpp @@ -178,7 +178,7 @@ void DanglingHandleCheck::registerMatchers(MatchFinder *Finder) { void DanglingHandleCheck::check(const MatchFinder::MatchResult &Result) { auto *Handle = Result.Nodes.getNodeAs("handle"); - diag(Result.Nodes.getNodeAs("bad_stmt")->getLocStart(), + diag(Result.Nodes.getNodeAs("bad_stmt")->getBeginLoc(), "%0 outlives its value") << Handle->getQualifiedNameAsString(); } diff --git a/clang-tidy/bugprone/InaccurateEraseCheck.cpp b/clang-tidy/bugprone/InaccurateEraseCheck.cpp index cf1be0e7b..02d23c499 100644 --- a/clang-tidy/bugprone/InaccurateEraseCheck.cpp +++ b/clang-tidy/bugprone/InaccurateEraseCheck.cpp @@ -57,7 +57,7 @@ void InaccurateEraseCheck::check(const MatchFinder::MatchResult &Result) { Result.Nodes.getNodeAs("erase"); const auto *EndExpr = Result.Nodes.getNodeAs("end"); - const SourceLocation Loc = MemberCall->getLocStart(); + const SourceLocation Loc = MemberCall->getBeginLoc(); FixItHint Hint; diff --git a/clang-tidy/bugprone/IncorrectRoundingsCheck.cpp b/clang-tidy/bugprone/IncorrectRoundingsCheck.cpp index ab7b28d69..549799f1e 100644 --- a/clang-tidy/bugprone/IncorrectRoundingsCheck.cpp +++ b/clang-tidy/bugprone/IncorrectRoundingsCheck.cpp @@ -61,7 +61,7 @@ void IncorrectRoundingsCheck::registerMatchers(MatchFinder *MatchFinder) { void IncorrectRoundingsCheck::check(const MatchFinder::MatchResult &Result) { const auto *CastExpr = Result.Nodes.getNodeAs("CastExpr"); - diag(CastExpr->getLocStart(), + diag(CastExpr->getBeginLoc(), "casting (double + 0.5) to integer leads to incorrect rounding; " "consider using lround (#include ) instead"); } diff --git a/clang-tidy/bugprone/IntegerDivisionCheck.cpp b/clang-tidy/bugprone/IntegerDivisionCheck.cpp index 1b4eaeabb..094d99132 100644 --- a/clang-tidy/bugprone/IntegerDivisionCheck.cpp +++ b/clang-tidy/bugprone/IntegerDivisionCheck.cpp @@ -48,7 +48,7 @@ void IntegerDivisionCheck::registerMatchers(MatchFinder *Finder) { void IntegerDivisionCheck::check(const MatchFinder::MatchResult &Result) { const auto *IntDiv = Result.Nodes.getNodeAs("IntDiv"); - diag(IntDiv->getLocStart(), "result of integer division used in a floating " + diag(IntDiv->getBeginLoc(), "result of integer division used in a floating " "point context; possible loss of precision"); } diff --git a/clang-tidy/bugprone/MisplacedOperatorInStrlenInAllocCheck.cpp b/clang-tidy/bugprone/MisplacedOperatorInStrlenInAllocCheck.cpp index f8db0b3ef..25544a503 100644 --- a/clang-tidy/bugprone/MisplacedOperatorInStrlenInAllocCheck.cpp +++ b/clang-tidy/bugprone/MisplacedOperatorInStrlenInAllocCheck.cpp @@ -102,9 +102,10 @@ void MisplacedOperatorInStrlenInAllocCheck::check( StrLen->getSourceRange(), (StrLenBegin + LHSText + StrLenEnd + " + " + RHSText).str()); - diag(Alloc->getLocStart(), + diag(Alloc->getBeginLoc(), "addition operator is applied to the argument of %0 instead of its " - "result") << StrLen->getDirectCallee()->getName() << Hint; + "result") + << StrLen->getDirectCallee()->getName() << Hint; } } // namespace bugprone diff --git a/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp b/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp index e263366b6..b9cbfebe8 100644 --- a/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp +++ b/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp @@ -185,11 +185,11 @@ void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) { const auto *Cast = Result.Nodes.getNodeAs("Cast"); if (!CheckImplicitCasts && isa(Cast)) return; - if (Cast->getLocStart().isMacroID()) + if (Cast->getBeginLoc().isMacroID()) return; const auto *Calc = Result.Nodes.getNodeAs("Calc"); - if (Calc->getLocStart().isMacroID()) + if (Calc->getBeginLoc().isMacroID()) return; if (Cast->isTypeDependent() || Cast->isValueDependent() || @@ -223,7 +223,7 @@ void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) { if (Context.getIntWidth(CalcType) >= getMaxCalculationWidth(Context, Calc)) return; - diag(Cast->getLocStart(), "either cast from %0 to %1 is ineffective, or " + diag(Cast->getBeginLoc(), "either cast from %0 to %1 is ineffective, or " "there is loss of precision before the conversion") << CalcType << CastType; } diff --git a/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp b/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp index 516ee1930..9699250bb 100644 --- a/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp +++ b/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp @@ -29,7 +29,7 @@ static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee, CharSourceRange CallRange = Lexer::makeFileCharRange(CharSourceRange::getTokenRange( - Callee->getLocStart(), Callee->getLocEnd()), + Callee->getBeginLoc(), Callee->getLocEnd()), SM, LangOpts); if (CallRange.isValid()) { diff --git a/clang-tidy/bugprone/MultipleStatementMacroCheck.cpp b/clang-tidy/bugprone/MultipleStatementMacroCheck.cpp index 942c8752f..5c498213d 100644 --- a/clang-tidy/bugprone/MultipleStatementMacroCheck.cpp +++ b/clang-tidy/bugprone/MultipleStatementMacroCheck.cpp @@ -19,7 +19,7 @@ namespace bugprone { namespace { -AST_MATCHER(Expr, isInMacro) { return Node.getLocStart().isMacroID(); } +AST_MATCHER(Expr, isInMacro) { return Node.getBeginLoc().isMacroID(); } /// \brief Find the next statement after `S`. const Stmt *nextStmt(const MatchFinder::MatchResult &Result, const Stmt *S) { @@ -73,13 +73,13 @@ void MultipleStatementMacroCheck::check( if (!Next) return; - SourceLocation OuterLoc = Outer->getLocStart(); + SourceLocation OuterLoc = Outer->getBeginLoc(); if (Result.Nodes.getNodeAs("else")) OuterLoc = cast(Outer)->getElseLoc(); - auto InnerRanges = getExpansionRanges(Inner->getLocStart(), Result); + auto InnerRanges = getExpansionRanges(Inner->getBeginLoc(), Result); auto OuterRanges = getExpansionRanges(OuterLoc, Result); - auto NextRanges = getExpansionRanges(Next->getLocStart(), Result); + auto NextRanges = getExpansionRanges(Next->getBeginLoc(), Result); // Remove all the common ranges, starting from the top (the last ones in the // list). diff --git a/clang-tidy/bugprone/SizeofContainerCheck.cpp b/clang-tidy/bugprone/SizeofContainerCheck.cpp index b4a019e99..f7d63b1ae 100644 --- a/clang-tidy/bugprone/SizeofContainerCheck.cpp +++ b/clang-tidy/bugprone/SizeofContainerCheck.cpp @@ -40,7 +40,7 @@ void SizeofContainerCheck::check(const MatchFinder::MatchResult &Result) { Result.Nodes.getNodeAs("sizeof"); auto Diag = - diag(SizeOf->getLocStart(), "sizeof() doesn't return the size of the " + diag(SizeOf->getBeginLoc(), "sizeof() doesn't return the size of the " "container; did you mean .size()?"); } diff --git a/clang-tidy/bugprone/SizeofExpressionCheck.cpp b/clang-tidy/bugprone/SizeofExpressionCheck.cpp index f05a90064..d662657a9 100644 --- a/clang-tidy/bugprone/SizeofExpressionCheck.cpp +++ b/clang-tidy/bugprone/SizeofExpressionCheck.cpp @@ -216,29 +216,29 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) { const ASTContext &Ctx = *Result.Context; if (const auto *E = Result.Nodes.getNodeAs("sizeof-constant")) { - diag(E->getLocStart(), + diag(E->getBeginLoc(), "suspicious usage of 'sizeof(K)'; did you mean 'K'?"); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-integer-call")) { - diag(E->getLocStart(), "suspicious usage of 'sizeof()' on an expression " + diag(E->getBeginLoc(), "suspicious usage of 'sizeof()' on an expression " "that results in an integer"); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-this")) { - diag(E->getLocStart(), + diag(E->getBeginLoc(), "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'"); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-charp")) { - diag(E->getLocStart(), + diag(E->getBeginLoc(), "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?"); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-pointer-to-aggregate")) { - diag(E->getLocStart(), + diag(E->getBeginLoc(), "suspicious usage of 'sizeof(A*)'; pointer to aggregate"); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-compare-constant")) { - diag(E->getLocStart(), + diag(E->getBeginLoc(), "suspicious comparison of 'sizeof(expr)' to a constant"); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-comma-expr")) { - diag(E->getLocStart(), "suspicious usage of 'sizeof(..., ...)'"); + diag(E->getBeginLoc(), "suspicious usage of 'sizeof(..., ...)'"); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-divide-expr")) { const auto *NumTy = Result.Nodes.getNodeAs("num-type"); @@ -252,30 +252,30 @@ void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) { if (DenominatorSize > CharUnits::Zero() && !NumeratorSize.isMultipleOf(DenominatorSize)) { - diag(E->getLocStart(), "suspicious usage of 'sizeof(...)/sizeof(...)';" + diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';" " numerator is not a multiple of denominator"); } else if (ElementSize > CharUnits::Zero() && DenominatorSize > CharUnits::Zero() && ElementSize != DenominatorSize) { - diag(E->getLocStart(), "suspicious usage of 'sizeof(...)/sizeof(...)';" + diag(E->getBeginLoc(), "suspicious usage of 'sizeof(...)/sizeof(...)';" " numerator is not a multiple of denominator"); } else if (NumTy && DenomTy && NumTy == DenomTy) { - diag(E->getLocStart(), + diag(E->getBeginLoc(), "suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'"); } else if (PointedTy && DenomTy && PointedTy == DenomTy) { - diag(E->getLocStart(), + diag(E->getBeginLoc(), "suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'"); } else if (NumTy && DenomTy && NumTy->isPointerType() && DenomTy->isPointerType()) { - diag(E->getLocStart(), + diag(E->getBeginLoc(), "suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'"); } } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-sizeof-expr")) { - diag(E->getLocStart(), "suspicious usage of 'sizeof(sizeof(...))'"); + diag(E->getBeginLoc(), "suspicious usage of 'sizeof(sizeof(...))'"); } else if (const auto *E = Result.Nodes.getNodeAs("sizeof-multiply-sizeof")) { - diag(E->getLocStart(), "suspicious 'sizeof' by 'sizeof' multiplication"); + diag(E->getBeginLoc(), "suspicious 'sizeof' by 'sizeof' multiplication"); } } diff --git a/clang-tidy/bugprone/StringConstructorCheck.cpp b/clang-tidy/bugprone/StringConstructorCheck.cpp index 420428fc3..d88823067 100644 --- a/clang-tidy/bugprone/StringConstructorCheck.cpp +++ b/clang-tidy/bugprone/StringConstructorCheck.cpp @@ -106,7 +106,7 @@ void StringConstructorCheck::check(const MatchFinder::MatchResult &Result) { const ASTContext &Ctx = *Result.Context; const auto *E = Result.Nodes.getNodeAs("constructor"); assert(E && "missing constructor expression"); - SourceLocation Loc = E->getLocStart(); + SourceLocation Loc = E->getBeginLoc(); if (Result.Nodes.getNodeAs("swapped-parameter")) { const Expr *P0 = E->getArg(0); diff --git a/clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp b/clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp index f3736489c..1481cc380 100644 --- a/clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp +++ b/clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp @@ -39,7 +39,7 @@ void StringIntegerAssignmentCheck::registerMatchers(MatchFinder *Finder) { void StringIntegerAssignmentCheck::check( const MatchFinder::MatchResult &Result) { const auto *Argument = Result.Nodes.getNodeAs("expr"); - SourceLocation Loc = Argument->getLocStart(); + SourceLocation Loc = Argument->getBeginLoc(); auto Diag = diag(Loc, "an integer is interpreted as a character code when assigning " diff --git a/clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.cpp b/clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.cpp index eaa610fc6..b440b6166 100644 --- a/clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.cpp +++ b/clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.cpp @@ -68,14 +68,14 @@ void StringLiteralWithEmbeddedNulCheck::check( SL->getCodeUnit(Offset + 1) == 'x' && isDigit(SL->getCodeUnit(Offset + 2)) && isDigit(SL->getCodeUnit(Offset + 3))) { - diag(SL->getLocStart(), "suspicious embedded NUL character"); + diag(SL->getBeginLoc(), "suspicious embedded NUL character"); return; } } } if (const auto *SL = Result.Nodes.getNodeAs("truncated")) { - diag(SL->getLocStart(), + diag(SL->getBeginLoc(), "truncated string literal with embedded NUL character"); } } diff --git a/clang-tidy/bugprone/SuspiciousMemsetUsageCheck.cpp b/clang-tidy/bugprone/SuspiciousMemsetUsageCheck.cpp index 8e11a4351..2e0a46c91 100644 --- a/clang-tidy/bugprone/SuspiciousMemsetUsageCheck.cpp +++ b/clang-tidy/bugprone/SuspiciousMemsetUsageCheck.cpp @@ -61,7 +61,7 @@ void SuspiciousMemsetUsageCheck::check(const MatchFinder::MatchResult &Result) { SourceRange CharRange = CharZeroFill->getSourceRange(); auto Diag = - diag(CharZeroFill->getLocStart(), "memset fill value is char '0', " + diag(CharZeroFill->getBeginLoc(), "memset fill value is char '0', " "potentially mistaken for int 0"); // Only suggest a fix if no macros are involved. @@ -82,7 +82,7 @@ void SuspiciousMemsetUsageCheck::check(const MatchFinder::MatchResult &Result) { (NumValue >= 0 && NumValue <= UCharMax)) return; - diag(NumFill->getLocStart(), "memset fill value is out of unsigned " + diag(NumFill->getBeginLoc(), "memset fill value is out of unsigned " "character range, gets truncated"); } @@ -110,7 +110,7 @@ void SuspiciousMemsetUsageCheck::check(const MatchFinder::MatchResult &Result) { // `byte_count` is known to be zero at compile time, and `fill_char` is // either not known or known to be a positive integer. Emit a warning // and fix-its to swap the arguments. - auto D = diag(Call->getLocStart(), + auto D = diag(Call->getBeginLoc(), "memset of size zero, potentially swapped arguments"); StringRef RHSString = tooling::fixit::getText(*ByteCount, *Result.Context); StringRef LHSString = tooling::fixit::getText(*FillChar, *Result.Context); diff --git a/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp b/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp index 3831dc3ea..a66cf431f 100644 --- a/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp +++ b/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp @@ -120,7 +120,7 @@ void SuspiciousMissingCommaCheck::check( if (double(Count) / Size > RatioThreshold) return; - diag(ConcatenatedLiteral->getLocStart(), + diag(ConcatenatedLiteral->getBeginLoc(), "suspicious string literal, probably missing a comma"); } diff --git a/clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp b/clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp index d7f51d0d8..f4e1496af 100644 --- a/clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp +++ b/clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp @@ -34,7 +34,7 @@ void SuspiciousSemicolonCheck::check(const MatchFinder::MatchResult &Result) { return; const auto *Semicolon = Result.Nodes.getNodeAs("semi"); - SourceLocation LocStart = Semicolon->getLocStart(); + SourceLocation LocStart = Semicolon->getBeginLoc(); if (LocStart.isMacroID()) return; @@ -60,7 +60,7 @@ void SuspiciousSemicolonCheck::check(const MatchFinder::MatchResult &Result) { if (Lexer.LexFromRawLexer(Token)) return; - unsigned BaseIndent = SM.getSpellingColumnNumber(Statement->getLocStart()); + unsigned BaseIndent = SM.getSpellingColumnNumber(Statement->getBeginLoc()); unsigned NewTokenIndent = SM.getSpellingColumnNumber(Token.getLocation()); unsigned NewTokenLine = SM.getSpellingLineNumber(Token.getLocation()); diff --git a/clang-tidy/bugprone/SuspiciousStringCompareCheck.cpp b/clang-tidy/bugprone/SuspiciousStringCompareCheck.cpp index 0cc62157b..a16da4af1 100644 --- a/clang-tidy/bugprone/SuspiciousStringCompareCheck.cpp +++ b/clang-tidy/bugprone/SuspiciousStringCompareCheck.cpp @@ -177,7 +177,7 @@ void SuspiciousStringCompareCheck::check( Call->getRParenLoc(), 0, Result.Context->getSourceManager(), getLangOpts()); - diag(Call->getLocStart(), + diag(Call->getBeginLoc(), "function %0 is called without explicitly comparing result") << Decl << FixItHint::CreateInsertion(EndLoc, " != 0"); } @@ -186,29 +186,30 @@ void SuspiciousStringCompareCheck::check( SourceLocation EndLoc = Lexer::getLocForEndOfToken( Call->getRParenLoc(), 0, Result.Context->getSourceManager(), getLangOpts()); - SourceLocation NotLoc = E->getLocStart(); + SourceLocation NotLoc = E->getBeginLoc(); - diag(Call->getLocStart(), + diag(Call->getBeginLoc(), "function %0 is compared using logical not operator") - << Decl << FixItHint::CreateRemoval( - CharSourceRange::getTokenRange(NotLoc, NotLoc)) + << Decl + << FixItHint::CreateRemoval( + CharSourceRange::getTokenRange(NotLoc, NotLoc)) << FixItHint::CreateInsertion(EndLoc, " == 0"); } if (Result.Nodes.getNodeAs("invalid-comparison")) { - diag(Call->getLocStart(), + diag(Call->getBeginLoc(), "function %0 is compared to a suspicious constant") << Decl; } if (const auto *BinOp = Result.Nodes.getNodeAs("suspicious-operator")) { - diag(Call->getLocStart(), "results of function %0 used by operator '%1'") + diag(Call->getBeginLoc(), "results of function %0 used by operator '%1'") << Decl << BinOp->getOpcodeStr(); } if (Result.Nodes.getNodeAs("invalid-conversion")) { - diag(Call->getLocStart(), "function %0 has suspicious implicit cast") + diag(Call->getBeginLoc(), "function %0 has suspicious implicit cast") << Decl; } } diff --git a/clang-tidy/bugprone/SwappedArgumentsCheck.cpp b/clang-tidy/bugprone/SwappedArgumentsCheck.cpp index 64d3eaf50..c1550f6d4 100644 --- a/clang-tidy/bugprone/SwappedArgumentsCheck.cpp +++ b/clang-tidy/bugprone/SwappedArgumentsCheck.cpp @@ -84,7 +84,7 @@ void SwappedArgumentsCheck::check(const MatchFinder::MatchResult &Result) { continue; // Emit a warning and fix-its that swap the arguments. - diag(Call->getLocStart(), "argument with implicit conversion from %0 " + diag(Call->getBeginLoc(), "argument with implicit conversion from %0 " "to %1 followed by argument converted from " "%2 to %3, potentially swapped arguments.") << LHS->getType() << LHSFrom->getType() << RHS->getType() diff --git a/clang-tidy/bugprone/TerminatingContinueCheck.cpp b/clang-tidy/bugprone/TerminatingContinueCheck.cpp index 3e11e06cf..e41fc1fad 100644 --- a/clang-tidy/bugprone/TerminatingContinueCheck.cpp +++ b/clang-tidy/bugprone/TerminatingContinueCheck.cpp @@ -39,7 +39,7 @@ void TerminatingContinueCheck::check(const MatchFinder::MatchResult &Result) { const auto *ContStmt = Result.Nodes.getNodeAs("continue"); auto Diag = - diag(ContStmt->getLocStart(), + diag(ContStmt->getBeginLoc(), "'continue' in loop with false condition is equivalent to 'break'") << tooling::fixit::createReplacement(*ContStmt, "break"); } diff --git a/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp b/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp index 350cf3bc6..695d9c5fa 100644 --- a/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp +++ b/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp @@ -42,7 +42,7 @@ void ThrowKeywordMissingCheck::check(const MatchFinder::MatchResult &Result) { const auto *TemporaryExpr = Result.Nodes.getNodeAs("temporary-exception-not-thrown"); - diag(TemporaryExpr->getLocStart(), "suspicious exception object created but " + diag(TemporaryExpr->getBeginLoc(), "suspicious exception object created but " "not thrown; did you mean 'throw %0'?") << TemporaryExpr->getType().getBaseTypeIdentifier()->getName(); } diff --git a/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp b/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp index ebfe517dd..0665e0801 100644 --- a/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp +++ b/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp @@ -52,7 +52,7 @@ void UndefinedMemoryManipulationCheck::check( QualType DestType = Call->getArg(0)->IgnoreImplicit()->getType(); if (!DestType->getPointeeType().isNull()) DestType = DestType->getPointeeType(); - diag(Call->getLocStart(), "undefined behavior, destination object type %0 " + diag(Call->getBeginLoc(), "undefined behavior, destination object type %0 " "is not TriviallyCopyable") << DestType; } @@ -60,7 +60,7 @@ void UndefinedMemoryManipulationCheck::check( QualType SourceType = Call->getArg(1)->IgnoreImplicit()->getType(); if (!SourceType->getPointeeType().isNull()) SourceType = SourceType->getPointeeType(); - diag(Call->getLocStart(), + diag(Call->getBeginLoc(), "undefined behavior, source object type %0 is not TriviallyCopyable") << SourceType; } diff --git a/clang-tidy/bugprone/UndelegatedConstructorCheck.cpp b/clang-tidy/bugprone/UndelegatedConstructorCheck.cpp index f47ad7e7c..90c07b9af 100644 --- a/clang-tidy/bugprone/UndelegatedConstructorCheck.cpp +++ b/clang-tidy/bugprone/UndelegatedConstructorCheck.cpp @@ -75,7 +75,7 @@ void UndelegatedConstructorCheck::registerMatchers(MatchFinder *Finder) { void UndelegatedConstructorCheck::check( const MatchFinder::MatchResult &Result) { const auto *E = Result.Nodes.getNodeAs("construct"); - diag(E->getLocStart(), "did you intend to call a delegated constructor? " + diag(E->getBeginLoc(), "did you intend to call a delegated constructor? " "A temporary object is created here instead"); } diff --git a/clang-tidy/bugprone/UnusedRaiiCheck.cpp b/clang-tidy/bugprone/UnusedRaiiCheck.cpp index e2882f371..e58f16684 100644 --- a/clang-tidy/bugprone/UnusedRaiiCheck.cpp +++ b/clang-tidy/bugprone/UnusedRaiiCheck.cpp @@ -52,7 +52,7 @@ void UnusedRaiiCheck::check(const MatchFinder::MatchResult &Result) { // We ignore code expanded from macros to reduce the number of false // positives. - if (E->getLocStart().isMacroID()) + if (E->getBeginLoc().isMacroID()) return; // Don't emit a warning for the last statement in the surrounding compund @@ -62,7 +62,7 @@ void UnusedRaiiCheck::check(const MatchFinder::MatchResult &Result) { return; // Emit a warning. - auto D = diag(E->getLocStart(), "object destroyed immediately after " + auto D = diag(E->getBeginLoc(), "object destroyed immediately after " "creation; did you mean to name the object?"); const char *Replacement = " give_me_a_name"; diff --git a/clang-tidy/bugprone/UnusedReturnValueCheck.cpp b/clang-tidy/bugprone/UnusedReturnValueCheck.cpp index d0c588570..b8de045e8 100644 --- a/clang-tidy/bugprone/UnusedReturnValueCheck.cpp +++ b/clang-tidy/bugprone/UnusedReturnValueCheck.cpp @@ -86,10 +86,10 @@ void UnusedReturnValueCheck::registerMatchers(MatchFinder *Finder) { void UnusedReturnValueCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *Matched = Result.Nodes.getNodeAs("match")) { - diag(Matched->getLocStart(), + diag(Matched->getBeginLoc(), "the value returned by this function should be used") << Matched->getSourceRange(); - diag(Matched->getLocStart(), + diag(Matched->getBeginLoc(), "cast the expression to void to silence this warning", DiagnosticIDs::Note); } diff --git a/clang-tidy/bugprone/VirtualNearMissCheck.cpp b/clang-tidy/bugprone/VirtualNearMissCheck.cpp index bb9c9b65c..ede1bf33c 100644 --- a/clang-tidy/bugprone/VirtualNearMissCheck.cpp +++ b/clang-tidy/bugprone/VirtualNearMissCheck.cpp @@ -257,7 +257,7 @@ void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) { bool ApplyFix = !BaseMD->isTemplateInstantiation() && !DerivedMD->isTemplateInstantiation(); auto Diag = - diag(DerivedMD->getLocStart(), + diag(DerivedMD->getBeginLoc(), "method '%0' has a similar name and the same signature as " "virtual method '%1'; did you mean to override it?") << DerivedMD->getQualifiedNameAsString() diff --git a/clang-tidy/cert/LimitedRandomnessCheck.cpp b/clang-tidy/cert/LimitedRandomnessCheck.cpp index f319f9fb3..3f6f948d2 100644 --- a/clang-tidy/cert/LimitedRandomnessCheck.cpp +++ b/clang-tidy/cert/LimitedRandomnessCheck.cpp @@ -30,7 +30,7 @@ void LimitedRandomnessCheck::check(const MatchFinder::MatchResult &Result) { msg = "; use C++11 random library instead"; const auto *MatchedDecl = Result.Nodes.getNodeAs("randomGenerator"); - diag(MatchedDecl->getLocStart(), "rand() has limited randomness" + msg); + diag(MatchedDecl->getBeginLoc(), "rand() has limited randomness" + msg); } } // namespace cert diff --git a/clang-tidy/cppcoreguidelines/AvoidGotoCheck.cpp b/clang-tidy/cppcoreguidelines/AvoidGotoCheck.cpp index 3e800cd59..eaed15f97 100644 --- a/clang-tidy/cppcoreguidelines/AvoidGotoCheck.cpp +++ b/clang-tidy/cppcoreguidelines/AvoidGotoCheck.cpp @@ -19,7 +19,7 @@ namespace cppcoreguidelines { namespace { AST_MATCHER(GotoStmt, isForwardJumping) { - return Node.getLocStart() < Node.getLabel()->getLocStart(); + return Node.getBeginLoc() < Node.getLabel()->getBeginLoc(); } } // namespace @@ -49,7 +49,7 @@ void AvoidGotoCheck::check(const MatchFinder::MatchResult &Result) { diag(Goto->getGotoLoc(), "avoid using 'goto' for flow control") << Goto->getSourceRange(); - diag(Goto->getLabel()->getLocStart(), "label defined here", + diag(Goto->getLabel()->getBeginLoc(), "label defined here", DiagnosticIDs::Note); } } // namespace cppcoreguidelines diff --git a/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp b/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp index 2c5676e76..3a22c1e9c 100644 --- a/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp +++ b/clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp @@ -52,14 +52,14 @@ void NarrowingConversionsCheck::registerMatchers(MatchFinder *Finder) { void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *Op = Result.Nodes.getNodeAs("op")) { - if (Op->getLocStart().isMacroID()) + if (Op->getBeginLoc().isMacroID()) return; diag(Op->getOperatorLoc(), "narrowing conversion from %0 to %1") << Op->getRHS()->getType() << Op->getLHS()->getType(); return; } const auto *Cast = Result.Nodes.getNodeAs("cast"); - if (Cast->getLocStart().isMacroID()) + if (Cast->getBeginLoc().isMacroID()) return; diag(Cast->getExprLoc(), "narrowing conversion from %0 to %1") << Cast->getSubExpr()->getType() << Cast->getType(); diff --git a/clang-tidy/cppcoreguidelines/NoMallocCheck.cpp b/clang-tidy/cppcoreguidelines/NoMallocCheck.cpp index 5a2b88246..cc3346816 100644 --- a/clang-tidy/cppcoreguidelines/NoMallocCheck.cpp +++ b/clang-tidy/cppcoreguidelines/NoMallocCheck.cpp @@ -73,8 +73,8 @@ void NoMallocCheck::check(const MatchFinder::MatchResult &Result) { assert(Call && "Unhandled binding in the Matcher"); - diag(Call->getLocStart(), "do not manage memory manually; %0") - << Recommendation << SourceRange(Call->getLocStart(), Call->getLocEnd()); + diag(Call->getBeginLoc(), "do not manage memory manually; %0") + << Recommendation << SourceRange(Call->getBeginLoc(), Call->getLocEnd()); } } // namespace cppcoreguidelines diff --git a/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp b/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp index 6c86e053b..ebebfda2b 100644 --- a/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp +++ b/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp @@ -203,7 +203,7 @@ bool OwningMemoryCheck::handleDeletion(const BoundNodes &Nodes) { // Deletion of non-owners, with `delete variable;` if (DeleteStmt) { - diag(DeleteStmt->getLocStart(), + diag(DeleteStmt->getBeginLoc(), "deleting a pointer through a type that is " "not marked 'gsl::owner<>'; consider using a " "smart pointer instead") @@ -212,7 +212,7 @@ bool OwningMemoryCheck::handleDeletion(const BoundNodes &Nodes) { // FIXME: The declaration of the variable that was deleted can be // rewritten. const ValueDecl *Decl = DeletedVariable->getDecl(); - diag(Decl->getLocStart(), "variable declared here", DiagnosticIDs::Note) + diag(Decl->getBeginLoc(), "variable declared here", DiagnosticIDs::Note) << Decl->getSourceRange(); return true; @@ -228,7 +228,7 @@ bool OwningMemoryCheck::handleLegacyConsumers(const BoundNodes &Nodes) { // as a pointer, which should not be an owner. The argument that is an owner // is known and the false positive coming from the filename can be avoided. if (LegacyConsumer) { - diag(LegacyConsumer->getLocStart(), + diag(LegacyConsumer->getBeginLoc(), "calling legacy resource function without passing a 'gsl::owner<>'") << LegacyConsumer->getSourceRange(); return true; @@ -242,7 +242,7 @@ bool OwningMemoryCheck::handleExpectedOwner(const BoundNodes &Nodes) { // Expected function argument to be owner. if (ExpectedOwner) { - diag(ExpectedOwner->getLocStart(), + diag(ExpectedOwner->getBeginLoc(), "expected argument of type 'gsl::owner<>'; got %0") << ExpectedOwner->getType() << ExpectedOwner->getSourceRange(); return true; @@ -261,7 +261,7 @@ bool OwningMemoryCheck::handleAssignmentAndInit(const BoundNodes &Nodes) { // Assignments to owners. if (OwnerAssignment) { - diag(OwnerAssignment->getLocStart(), + diag(OwnerAssignment->getBeginLoc(), "expected assignment source to be of type 'gsl::owner<>'; got %0") << OwnerAssignment->getRHS()->getType() << OwnerAssignment->getSourceRange(); @@ -270,7 +270,7 @@ bool OwningMemoryCheck::handleAssignmentAndInit(const BoundNodes &Nodes) { // Initialization of owners. if (OwnerInitialization) { - diag(OwnerInitialization->getLocStart(), + diag(OwnerInitialization->getBeginLoc(), "expected initialization with value of type 'gsl::owner<>'; got %0") << OwnerInitialization->getAnyInitializer()->getType() << OwnerInitialization->getSourceRange(); @@ -306,7 +306,7 @@ bool OwningMemoryCheck::handleAssignmentFromNewOwner(const BoundNodes &Nodes) { // Bad assignments to non-owners, where the RHS is a newly created owner. if (BadOwnerAssignment) { - diag(BadOwnerAssignment->getLocStart(), + diag(BadOwnerAssignment->getBeginLoc(), "assigning newly created 'gsl::owner<>' to non-owner %0") << BadOwnerAssignment->getLHS()->getType() << BadOwnerAssignment->getSourceRange(); @@ -315,7 +315,7 @@ bool OwningMemoryCheck::handleAssignmentFromNewOwner(const BoundNodes &Nodes) { // Bad initialization of non-owners, where the RHS is a newly created owner. if (BadOwnerInitialization) { - diag(BadOwnerInitialization->getLocStart(), + diag(BadOwnerInitialization->getBeginLoc(), "initializing non-owner %0 with a newly created 'gsl::owner<>'") << BadOwnerInitialization->getType() << BadOwnerInitialization->getSourceRange(); @@ -326,7 +326,7 @@ bool OwningMemoryCheck::handleAssignmentFromNewOwner(const BoundNodes &Nodes) { // If the type of the variable was deduced, the wrapping owner typedef is // eliminated, therefore the check emits a special note for that case. if (Nodes.getNodeAs("deduced_type")) { - diag(BadOwnerInitialization->getLocStart(), + diag(BadOwnerInitialization->getBeginLoc(), "type deduction did not result in an owner", DiagnosticIDs::Note); } return true; @@ -337,7 +337,7 @@ bool OwningMemoryCheck::handleAssignmentFromNewOwner(const BoundNodes &Nodes) { if (BadOwnerArgument) { assert(BadOwnerParameter && "parameter for the problematic argument not found"); - diag(BadOwnerArgument->getLocStart(), "initializing non-owner argument of " + diag(BadOwnerArgument->getBeginLoc(), "initializing non-owner argument of " "type %0 with a newly created " "'gsl::owner<>'") << BadOwnerParameter->getType() << BadOwnerArgument->getSourceRange(); @@ -356,7 +356,7 @@ bool OwningMemoryCheck::handleReturnValues(const BoundNodes &Nodes) { if (BadReturnType) { // The returned value is a resource or variable that was not annotated with // owner<> and the function return type is not owner<>. - diag(BadReturnType->getLocStart(), + diag(BadReturnType->getBeginLoc(), "returning a newly created resource of " "type %0 or 'gsl::owner<>' from a " "function whose return type is not 'gsl::owner<>'") @@ -380,7 +380,7 @@ bool OwningMemoryCheck::handleOwnerMembers(const BoundNodes &Nodes) { assert(DeclaredOwnerMember && "match on class with bad destructor but without a declared owner"); - diag(DeclaredOwnerMember->getLocStart(), + diag(DeclaredOwnerMember->getBeginLoc(), "member variable of type 'gsl::owner<>' requires the class %0 to " "implement a destructor to release the owned resource") << BadClass; diff --git a/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp b/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp index 52156ad22..8a43a816d 100644 --- a/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp +++ b/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp @@ -46,7 +46,7 @@ void ProTypeCstyleCastCheck::check(const MatchFinder::MatchResult &Result) { MatchedCast->getCastKind() == CK_IntegralToPointer || MatchedCast->getCastKind() == CK_PointerToIntegral || MatchedCast->getCastKind() == CK_ReinterpretMemberPointer) { - diag(MatchedCast->getLocStart(), + diag(MatchedCast->getBeginLoc(), "do not use C-style cast to convert between unrelated types"); return; } @@ -71,7 +71,7 @@ void ProTypeCstyleCastCheck::check(const MatchFinder::MatchResult &Result) { *Result.SourceManager, getLangOpts()); auto diag_builder = diag( - MatchedCast->getLocStart(), + MatchedCast->getBeginLoc(), "do not use C-style cast to downcast from a base to a derived class; " "use dynamic_cast instead"); @@ -90,7 +90,7 @@ void ProTypeCstyleCastCheck::check(const MatchFinder::MatchResult &Result) { diag_builder << FixItHint::CreateReplacement(ParenRange, CastText); } else { diag( - MatchedCast->getLocStart(), + MatchedCast->getBeginLoc(), "do not use C-style cast to downcast from a base to a derived class"); } return; @@ -98,7 +98,7 @@ void ProTypeCstyleCastCheck::check(const MatchFinder::MatchResult &Result) { if (MatchedCast->getCastKind() == CK_NoOp && needsConstCast(SourceType, MatchedCast->getType())) { - diag(MatchedCast->getLocStart(), + diag(MatchedCast->getBeginLoc(), "do not use C-style cast to cast away constness"); } } diff --git a/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp b/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp index 4d9be8151..142ac2ed7 100644 --- a/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp +++ b/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp @@ -120,7 +120,7 @@ struct IntializerInsertion { switch (Placement) { case InitializerPlacement::New: Location = utils::lexer::getPreviousToken( - Context, Constructor.getBody()->getLocStart()) + Context, Constructor.getBody()->getBeginLoc()) .getLocation(); break; case InitializerPlacement::Before: @@ -230,7 +230,7 @@ void fixInitializerList(const ASTContext &Context, DiagnosticBuilder &Diag, const CXXConstructorDecl *Ctor, const SmallPtrSetImpl &DeclsToInit) { // Do not propose fixes in macros since we cannot place them correctly. - if (Ctor->getLocStart().isMacroID()) + if (Ctor->getBeginLoc().isMacroID()) return; SmallVector OrderedDecls; @@ -384,7 +384,7 @@ void ProTypeMemberInitCheck::checkMissingMemberInitializer( return; DiagnosticBuilder Diag = - diag(Ctor ? Ctor->getLocStart() : ClassDecl.getLocation(), + diag(Ctor ? Ctor->getBeginLoc() : ClassDecl.getLocation(), IsUnion ? "union constructor should initialize one of these fields: %0" : "constructor does not initialize these fields: %0") @@ -392,7 +392,7 @@ void ProTypeMemberInitCheck::checkMissingMemberInitializer( // Do not propose fixes for constructors in macros since we cannot place them // correctly. - if (Ctor && Ctor->getLocStart().isMacroID()) + if (Ctor && Ctor->getBeginLoc().isMacroID()) return; // Collect all fields but only suggest a fix for the first member of unions, @@ -462,7 +462,7 @@ void ProTypeMemberInitCheck::checkMissingBaseClassInitializer( return; DiagnosticBuilder Diag = - diag(Ctor ? Ctor->getLocStart() : ClassDecl.getLocation(), + diag(Ctor ? Ctor->getBeginLoc() : ClassDecl.getLocation(), "constructor does not initialize these bases: %0") << toCommaSeparatedString(AllBases, BasesToInit); @@ -473,7 +473,7 @@ void ProTypeMemberInitCheck::checkMissingBaseClassInitializer( void ProTypeMemberInitCheck::checkUninitializedTrivialType( const ASTContext &Context, const VarDecl *Var) { DiagnosticBuilder Diag = - diag(Var->getLocStart(), "uninitialized record type: %0") << Var; + diag(Var->getBeginLoc(), "uninitialized record type: %0") << Var; Diag << FixItHint::CreateInsertion( getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()), diff --git a/clang-tidy/fuchsia/DefaultArgumentsCheck.cpp b/clang-tidy/fuchsia/DefaultArgumentsCheck.cpp index 493ce9a24..f6c64ce13 100644 --- a/clang-tidy/fuchsia/DefaultArgumentsCheck.cpp +++ b/clang-tidy/fuchsia/DefaultArgumentsCheck.cpp @@ -27,8 +27,7 @@ void DefaultArgumentsCheck::check(const MatchFinder::MatchResult &Result) { Result.Nodes.getNodeAs("stmt")) { diag(S->getUsedLocation(), "calling a function that uses a default argument is disallowed"); - diag(S->getParam()->getLocStart(), - "default parameter was declared here", + diag(S->getParam()->getBeginLoc(), "default parameter was declared here", DiagnosticIDs::Note); } else if (const ParmVarDecl *D = Result.Nodes.getNodeAs("decl")) { @@ -37,11 +36,11 @@ void DefaultArgumentsCheck::check(const MatchFinder::MatchResult &Result) { if (DefaultArgRange.getEnd() != D->getLocEnd()) { return; } else if (DefaultArgRange.getBegin().isMacroID()) { - diag(D->getLocStart(), + diag(D->getBeginLoc(), "declaring a parameter with a default argument is disallowed"); } else { - SourceLocation StartLocation = D->getName().empty() ? - D->getLocStart() : D->getLocation(); + SourceLocation StartLocation = + D->getName().empty() ? D->getBeginLoc() : D->getLocation(); SourceRange RemovalRange(Lexer::getLocForEndOfToken( StartLocation, 0, @@ -51,10 +50,9 @@ void DefaultArgumentsCheck::check(const MatchFinder::MatchResult &Result) { DefaultArgRange.getEnd() ); - diag(D->getLocStart(), - "declaring a parameter with a default argument is disallowed") - << D - << FixItHint::CreateRemoval(RemovalRange); + diag(D->getBeginLoc(), + "declaring a parameter with a default argument is disallowed") + << D << FixItHint::CreateRemoval(RemovalRange); } } } diff --git a/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp b/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp index 35504c982..a49c952f3 100644 --- a/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp +++ b/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp @@ -120,9 +120,8 @@ void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) { } if (NumConcrete > 1) { - diag(D->getLocStart(), - "inheriting mulitple classes that aren't " - "pure virtual is discouraged"); + diag(D->getBeginLoc(), "inheriting mulitple classes that aren't " + "pure virtual is discouraged"); } } } diff --git a/clang-tidy/fuchsia/OverloadedOperatorCheck.cpp b/clang-tidy/fuchsia/OverloadedOperatorCheck.cpp index 847f7635d..8e6c74f32 100644 --- a/clang-tidy/fuchsia/OverloadedOperatorCheck.cpp +++ b/clang-tidy/fuchsia/OverloadedOperatorCheck.cpp @@ -34,8 +34,8 @@ void OverloadedOperatorCheck::registerMatchers(MatchFinder *Finder) { void OverloadedOperatorCheck::check(const MatchFinder::MatchResult &Result) { const auto *D = Result.Nodes.getNodeAs("decl"); assert(D && "No FunctionDecl captured!"); - - SourceLocation Loc = D->getLocStart(); + + SourceLocation Loc = D->getBeginLoc(); if (Loc.isValid()) diag(Loc, "overloading %0 is disallowed") << D; } diff --git a/clang-tidy/fuchsia/StaticallyConstructedObjectsCheck.cpp b/clang-tidy/fuchsia/StaticallyConstructedObjectsCheck.cpp index 16a534b18..e33f90acb 100644 --- a/clang-tidy/fuchsia/StaticallyConstructedObjectsCheck.cpp +++ b/clang-tidy/fuchsia/StaticallyConstructedObjectsCheck.cpp @@ -51,7 +51,7 @@ void StaticallyConstructedObjectsCheck::registerMatchers(MatchFinder *Finder) { void StaticallyConstructedObjectsCheck::check( const MatchFinder::MatchResult &Result) { if (const auto *D = Result.Nodes.getNodeAs("decl")) - diag(D->getLocStart(), "static objects are disallowed; if possible, use a " + diag(D->getBeginLoc(), "static objects are disallowed; if possible, use a " "constexpr constructor instead"); } diff --git a/clang-tidy/fuchsia/TrailingReturnCheck.cpp b/clang-tidy/fuchsia/TrailingReturnCheck.cpp index 7af0c9c60..4ffa3f795 100644 --- a/clang-tidy/fuchsia/TrailingReturnCheck.cpp +++ b/clang-tidy/fuchsia/TrailingReturnCheck.cpp @@ -43,7 +43,7 @@ void TrailingReturnCheck::registerMatchers(MatchFinder *Finder) { void TrailingReturnCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *D = Result.Nodes.getNodeAs("decl")) - diag(D->getLocStart(), + diag(D->getBeginLoc(), "a trailing return type is disallowed for this type of declaration"); } diff --git a/clang-tidy/fuchsia/VirtualInheritanceCheck.cpp b/clang-tidy/fuchsia/VirtualInheritanceCheck.cpp index f3da2b6ef..82f441034 100644 --- a/clang-tidy/fuchsia/VirtualInheritanceCheck.cpp +++ b/clang-tidy/fuchsia/VirtualInheritanceCheck.cpp @@ -35,7 +35,7 @@ void VirtualInheritanceCheck::registerMatchers(MatchFinder *Finder) { void VirtualInheritanceCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *D = Result.Nodes.getNodeAs("decl")) - diag(D->getLocStart(), "direct virtual inheritance is disallowed"); + diag(D->getBeginLoc(), "direct virtual inheritance is disallowed"); } } // namespace fuchsia diff --git a/clang-tidy/google/AvoidCStyleCastsCheck.cpp b/clang-tidy/google/AvoidCStyleCastsCheck.cpp index 4c7c164b9..ffcfe11d5 100644 --- a/clang-tidy/google/AvoidCStyleCastsCheck.cpp +++ b/clang-tidy/google/AvoidCStyleCastsCheck.cpp @@ -82,7 +82,7 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { const QualType DestType = DestTypeAsWritten.getCanonicalType(); auto ReplaceRange = CharSourceRange::getCharRange( - CastExpr->getLParenLoc(), CastExpr->getSubExprAsWritten()->getLocStart()); + CastExpr->getLParenLoc(), CastExpr->getSubExprAsWritten()->getBeginLoc()); bool FnToFnCast = isFunction(SourceTypeAsWritten) && isFunction(DestTypeAsWritten); @@ -93,7 +93,7 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { // in this case. Don't emit "redundant cast" warnings for function // pointer/reference types. if (SourceTypeAsWritten == DestTypeAsWritten) { - diag(CastExpr->getLocStart(), "redundant cast to the same type") + diag(CastExpr->getBeginLoc(), "redundant cast to the same type") << FixItHint::CreateRemoval(ReplaceRange); return; } @@ -116,7 +116,7 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { // Ignore code in .c files #included in other files (which shouldn't be done, // but people still do this for test and other purposes). - if (SM.getFilename(SM.getSpellingLoc(CastExpr->getLocStart())).endswith(".c")) + if (SM.getFilename(SM.getSpellingLoc(CastExpr->getBeginLoc())).endswith(".c")) return; // Leave type spelling exactly as it was (unlike @@ -128,7 +128,7 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { SM, getLangOpts()); auto Diag = - diag(CastExpr->getLocStart(), "C-style casts are discouraged; use %0"); + diag(CastExpr->getBeginLoc(), "C-style casts are discouraged; use %0"); auto ReplaceWithCast = [&](std::string CastText) { const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts(); diff --git a/clang-tidy/google/ExplicitMakePairCheck.cpp b/clang-tidy/google/ExplicitMakePairCheck.cpp index 6c2f0a368..7e827514a 100644 --- a/clang-tidy/google/ExplicitMakePairCheck.cpp +++ b/clang-tidy/google/ExplicitMakePairCheck.cpp @@ -60,12 +60,12 @@ void ExplicitMakePairCheck::check(const MatchFinder::MatchResult &Result) { // make_pair. if (Arg0->getType() != Call->getArg(0)->getType() || Arg1->getType() != Call->getArg(1)->getType()) { - diag(Call->getLocStart(), "for C++11-compatibility, use pair directly") + diag(Call->getBeginLoc(), "for C++11-compatibility, use pair directly") << FixItHint::CreateReplacement( - SourceRange(DeclRef->getLocStart(), DeclRef->getLAngleLoc()), + SourceRange(DeclRef->getBeginLoc(), DeclRef->getLAngleLoc()), "std::pair<"); } else { - diag(Call->getLocStart(), + diag(Call->getBeginLoc(), "for C++11-compatibility, omit template arguments from make_pair") << FixItHint::CreateRemoval( SourceRange(DeclRef->getLAngleLoc(), DeclRef->getRAngleLoc())); diff --git a/clang-tidy/google/GlobalNamesInHeadersCheck.cpp b/clang-tidy/google/GlobalNamesInHeadersCheck.cpp index 45c5b7061..9f03f7d5a 100644 --- a/clang-tidy/google/GlobalNamesInHeadersCheck.cpp +++ b/clang-tidy/google/GlobalNamesInHeadersCheck.cpp @@ -48,15 +48,15 @@ void GlobalNamesInHeadersCheck::registerMatchers( void GlobalNamesInHeadersCheck::check(const MatchFinder::MatchResult &Result) { const auto *D = Result.Nodes.getNodeAs("using_decl"); // If it comes from a macro, we'll assume it is fine. - if (D->getLocStart().isMacroID()) + if (D->getBeginLoc().isMacroID()) return; // Ignore if it comes from the "main" file ... if (Result.SourceManager->isInMainFile( - Result.SourceManager->getExpansionLoc(D->getLocStart()))) { + Result.SourceManager->getExpansionLoc(D->getBeginLoc()))) { // unless that file is a header. if (!utils::isSpellingLocInHeaderFile( - D->getLocStart(), *Result.SourceManager, HeaderFileExtensions)) + D->getBeginLoc(), *Result.SourceManager, HeaderFileExtensions)) return; } @@ -70,7 +70,7 @@ void GlobalNamesInHeadersCheck::check(const MatchFinder::MatchResult &Result) { } } - diag(D->getLocStart(), + diag(D->getBeginLoc(), "using declarations in the global namespace in headers are prohibited"); } diff --git a/clang-tidy/google/IntegerTypesCheck.cpp b/clang-tidy/google/IntegerTypesCheck.cpp index 7f0b76581..07ee081a3 100644 --- a/clang-tidy/google/IntegerTypesCheck.cpp +++ b/clang-tidy/google/IntegerTypesCheck.cpp @@ -72,7 +72,7 @@ void IntegerTypesCheck::registerMatchers(MatchFinder *Finder) { void IntegerTypesCheck::check(const MatchFinder::MatchResult &Result) { auto TL = *Result.Nodes.getNodeAs("tl"); - SourceLocation Loc = TL.getLocStart(); + SourceLocation Loc = TL.getBeginLoc(); if (Loc.isInvalid() || Loc.isMacroID()) return; diff --git a/clang-tidy/google/OverloadedUnaryAndCheck.cpp b/clang-tidy/google/OverloadedUnaryAndCheck.cpp index 84abb5fd7..ff67b534d 100644 --- a/clang-tidy/google/OverloadedUnaryAndCheck.cpp +++ b/clang-tidy/google/OverloadedUnaryAndCheck.cpp @@ -43,7 +43,7 @@ void OverloadedUnaryAndCheck::registerMatchers( void OverloadedUnaryAndCheck::check(const MatchFinder::MatchResult &Result) { const auto *Decl = Result.Nodes.getNodeAs("overload"); - diag(Decl->getLocStart(), + diag(Decl->getBeginLoc(), "do not overload unary operator&, it is dangerous."); } diff --git a/clang-tidy/google/UnnamedNamespaceInHeaderCheck.cpp b/clang-tidy/google/UnnamedNamespaceInHeaderCheck.cpp index 32ff6e0b5..94e1729b3 100644 --- a/clang-tidy/google/UnnamedNamespaceInHeaderCheck.cpp +++ b/clang-tidy/google/UnnamedNamespaceInHeaderCheck.cpp @@ -48,7 +48,7 @@ void UnnamedNamespaceInHeaderCheck::registerMatchers( void UnnamedNamespaceInHeaderCheck::check( const MatchFinder::MatchResult &Result) { const auto *N = Result.Nodes.getNodeAs("anonymousNamespace"); - SourceLocation Loc = N->getLocStart(); + SourceLocation Loc = N->getBeginLoc(); if (!Loc.isValid()) return; diff --git a/clang-tidy/google/UsingNamespaceDirectiveCheck.cpp b/clang-tidy/google/UsingNamespaceDirectiveCheck.cpp index 60a46f88f..7490f0220 100644 --- a/clang-tidy/google/UsingNamespaceDirectiveCheck.cpp +++ b/clang-tidy/google/UsingNamespaceDirectiveCheck.cpp @@ -30,7 +30,7 @@ void UsingNamespaceDirectiveCheck::registerMatchers( void UsingNamespaceDirectiveCheck::check( const MatchFinder::MatchResult &Result) { const auto *U = Result.Nodes.getNodeAs("usingNamespace"); - SourceLocation Loc = U->getLocStart(); + SourceLocation Loc = U->getBeginLoc(); if (U->isImplicit() || !Loc.isValid()) return; diff --git a/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp b/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp index 298759f86..3ea56d2f7 100644 --- a/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp +++ b/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp @@ -35,14 +35,14 @@ void ExceptionBaseclassCheck::registerMatchers(MatchFinder *Finder) { void ExceptionBaseclassCheck::check(const MatchFinder::MatchResult &Result) { const auto *BadThrow = Result.Nodes.getNodeAs("bad_throw"); - diag(BadThrow->getSubExpr()->getLocStart(), "throwing an exception whose " + diag(BadThrow->getSubExpr()->getBeginLoc(), "throwing an exception whose " "type %0 is not derived from " "'std::exception'") << BadThrow->getSubExpr()->getType() << BadThrow->getSourceRange(); const auto *TypeDecl = Result.Nodes.getNodeAs("decl"); if (TypeDecl != nullptr) - diag(TypeDecl->getLocStart(), "type defined here", DiagnosticIDs::Note); + diag(TypeDecl->getBeginLoc(), "type defined here", DiagnosticIDs::Note); } } // namespace hicpp diff --git a/clang-tidy/hicpp/MultiwayPathsCoveredCheck.cpp b/clang-tidy/hicpp/MultiwayPathsCoveredCheck.cpp index 68ae54e0d..f4cad2e93 100644 --- a/clang-tidy/hicpp/MultiwayPathsCoveredCheck.cpp +++ b/clang-tidy/hicpp/MultiwayPathsCoveredCheck.cpp @@ -94,7 +94,7 @@ static std::size_t getNumberOfPossibleValues(QualType T, void MultiwayPathsCoveredCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *ElseIfWithoutElse = Result.Nodes.getNodeAs("else-if")) { - diag(ElseIfWithoutElse->getLocStart(), + diag(ElseIfWithoutElse->getBeginLoc(), "potentially uncovered codepath; add an ending else statement"); return; } @@ -120,7 +120,7 @@ void MultiwayPathsCoveredCheck::check(const MatchFinder::MatchResult &Result) { // FIXME: Evaluate, if emitting a fix-it to simplify that statement is // reasonable. if (!SwitchHasDefault && SwitchCaseCount == 0) { - diag(Switch->getLocStart(), + diag(Switch->getBeginLoc(), "switch statement without labels has no effect"); return; } @@ -132,7 +132,7 @@ void MultiwayPathsCoveredCheck::handleSwitchWithDefault( assert(CaseCount > 0 && "Switch statement with supposedly one default " "branch did not contain any case labels"); if (CaseCount == 1 || CaseCount == 2) - diag(Switch->getLocStart(), + diag(Switch->getBeginLoc(), CaseCount == 1 ? "degenerated switch with default label only" : "switch could be better written as an if/else statement"); @@ -172,7 +172,7 @@ void MultiwayPathsCoveredCheck::handleSwitchWithoutDefault( // FIXME: Transform the 'switch' into an 'if' for CaseCount == 1. if (CaseCount < MaxPathsPossible) - diag(Switch->getLocStart(), + diag(Switch->getBeginLoc(), CaseCount == 1 ? "switch with only one case; use an if statement" : "potential uncovered code path; add a default label"); } diff --git a/clang-tidy/hicpp/SignedBitwiseCheck.cpp b/clang-tidy/hicpp/SignedBitwiseCheck.cpp index 03900527e..5c76b8e52 100644 --- a/clang-tidy/hicpp/SignedBitwiseCheck.cpp +++ b/clang-tidy/hicpp/SignedBitwiseCheck.cpp @@ -75,14 +75,14 @@ void SignedBitwiseCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *UnaryOp = N.getNodeAs("unary-signed")) { IsUnary = true; - Location = UnaryOp->getLocStart(); + Location = UnaryOp->getBeginLoc(); } else { if (const auto *BinaryOp = N.getNodeAs("binary-no-sign-interference")) - Location = BinaryOp->getLocStart(); + Location = BinaryOp->getBeginLoc(); else if (const auto *BinaryOp = N.getNodeAs("binary-sign-interference")) - Location = BinaryOp->getLocStart(); + Location = BinaryOp->getBeginLoc(); else llvm_unreachable("unexpected matcher result"); } diff --git a/clang-tidy/llvm/TwineLocalCheck.cpp b/clang-tidy/llvm/TwineLocalCheck.cpp index 67c85a672..915a51db6 100644 --- a/clang-tidy/llvm/TwineLocalCheck.cpp +++ b/clang-tidy/llvm/TwineLocalCheck.cpp @@ -50,7 +50,7 @@ void TwineLocalCheck::check(const MatchFinder::MatchResult &Result) { SourceLocation EndLoc = Lexer::getLocForEndOfToken( VD->getInit()->getLocEnd(), 0, *Result.SourceManager, getLangOpts()); Diag << FixItHint::CreateReplacement(TypeRange, "std::string") - << FixItHint::CreateInsertion(VD->getInit()->getLocStart(), "(") + << FixItHint::CreateInsertion(VD->getInit()->getBeginLoc(), "(") << FixItHint::CreateInsertion(EndLoc, ").str()"); } else { // Just an implicit conversion. Insert the real type. diff --git a/clang-tidy/misc/DefinitionsInHeadersCheck.cpp b/clang-tidy/misc/DefinitionsInHeadersCheck.cpp index 69ef3ef3a..f4dab3974 100644 --- a/clang-tidy/misc/DefinitionsInHeadersCheck.cpp +++ b/clang-tidy/misc/DefinitionsInHeadersCheck.cpp @@ -22,7 +22,7 @@ namespace { AST_MATCHER_P(NamedDecl, usesHeaderFileExtension, utils::HeaderFileExtensionsSet, HeaderFileExtensions) { return utils::isExpansionLocInHeaderFile( - Node.getLocStart(), Finder->getASTContext().getSourceManager(), + Node.getBeginLoc(), Finder->getASTContext().getSourceManager(), HeaderFileExtensions); } diff --git a/clang-tidy/misc/RedundantExpressionCheck.cpp b/clang-tidy/misc/RedundantExpressionCheck.cpp index e259b6bfe..5f055d106 100644 --- a/clang-tidy/misc/RedundantExpressionCheck.cpp +++ b/clang-tidy/misc/RedundantExpressionCheck.cpp @@ -884,7 +884,7 @@ void RedundantExpressionCheck::checkBitwiseExpr( if (exprEvaluatesToZero(Opcode, Value)) { diag(Loc, "expression always evaluates to 0"); } else if (exprEvaluatesToBitwiseNegatedZero(Opcode, Value)) { - SourceRange ConstExprRange(ConstExpr->getLocStart(), + SourceRange ConstExprRange(ConstExpr->getBeginLoc(), ConstExpr->getLocEnd()); StringRef ConstExprText = Lexer::getSourceText( CharSourceRange::getTokenRange(ConstExprRange), *Result.SourceManager, @@ -893,7 +893,7 @@ void RedundantExpressionCheck::checkBitwiseExpr( diag(Loc, "expression always evaluates to '%0'") << ConstExprText; } else if (exprEvaluatesToSymbolic(Opcode, Value)) { - SourceRange SymExprRange(Sym->getLocStart(), Sym->getLocEnd()); + SourceRange SymExprRange(Sym->getBeginLoc(), Sym->getLocEnd()); StringRef ExprText = Lexer::getSourceText( CharSourceRange::getTokenRange(SymExprRange), *Result.SourceManager, diff --git a/clang-tidy/misc/StaticAssertCheck.cpp b/clang-tidy/misc/StaticAssertCheck.cpp index 77fb58de6..1d0d88d24 100644 --- a/clang-tidy/misc/StaticAssertCheck.cpp +++ b/clang-tidy/misc/StaticAssertCheck.cpp @@ -88,7 +88,7 @@ void StaticAssertCheck::check(const MatchFinder::MatchResult &Result) { const auto *AssertExprRoot = Result.Nodes.getNodeAs("assertExprRoot"); const auto *CastExpr = Result.Nodes.getNodeAs("castExpr"); - SourceLocation AssertExpansionLoc = CondStmt->getLocStart(); + SourceLocation AssertExpansionLoc = CondStmt->getBeginLoc(); if (!AssertExpansionLoc.isValid() || !AssertExpansionLoc.isMacroID()) return; @@ -129,7 +129,7 @@ void StaticAssertCheck::check(const MatchFinder::MatchResult &Result) { FixItHints.push_back(FixItHint::CreateRemoval( SourceRange(AssertExprRoot->getOperatorLoc()))); FixItHints.push_back(FixItHint::CreateRemoval( - SourceRange(AssertMSG->getLocStart(), AssertMSG->getLocEnd()))); + SourceRange(AssertMSG->getBeginLoc(), AssertMSG->getLocEnd()))); StaticAssertMSG = (Twine(", \"") + AssertMSG->getString() + "\"").str(); } diff --git a/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp b/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp index 90398ccd6..759c3f0e7 100644 --- a/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp +++ b/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp @@ -81,7 +81,7 @@ void ThrowByValueCatchByReferenceCheck::diagnoseThrowLocations( if (declRef && isCatchVariable(declRef)) { return; } - diag(subExpr->getLocStart(), "throw expression throws a pointer; it should " + diag(subExpr->getBeginLoc(), "throw expression throws a pointer; it should " "throw a non-pointer value instead"); } // If the throw statement does not throw by pointer then it throws by value @@ -124,7 +124,7 @@ void ThrowByValueCatchByReferenceCheck::diagnoseThrowLocations( } } if (emit) - diag(subExpr->getLocStart(), + diag(subExpr->getBeginLoc(), "throw expression should throw anonymous temporary values instead"); } } @@ -144,7 +144,7 @@ void ThrowByValueCatchByReferenceCheck::diagnoseCatchLocations( // We do not diagnose when catching pointer to strings since we also allow // throwing string literals. if (!PT->getPointeeType()->isAnyCharacterType()) - diag(varDecl->getLocStart(), diagMsgCatchReference); + diag(varDecl->getBeginLoc(), diagMsgCatchReference); } else if (!caughtType->isReferenceType()) { const char *diagMsgCatchReference = "catch handler catches by value; " "should catch by reference instead"; @@ -152,7 +152,7 @@ void ThrowByValueCatchByReferenceCheck::diagnoseCatchLocations( // value". In this case we should emit a diagnosis message unless the type // is trivial. if (!caughtType.isTrivialType(context)) - diag(varDecl->getLocStart(), diagMsgCatchReference); + diag(varDecl->getBeginLoc(), diagMsgCatchReference); } } diff --git a/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp b/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp index 0fd7e7712..84dd410d3 100644 --- a/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp +++ b/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp @@ -72,7 +72,7 @@ void UnconventionalAssignOperatorCheck::registerMatchers( void UnconventionalAssignOperatorCheck::check( const MatchFinder::MatchResult &Result) { if (const auto *RetStmt = Result.Nodes.getNodeAs("returnStmt")) { - diag(RetStmt->getLocStart(), "operator=() should always return '*this'"); + diag(RetStmt->getBeginLoc(), "operator=() should always return '*this'"); } else { static const char *const Messages[][2] = { {"ReturnType", "operator=() should return '%0&'"}, @@ -82,7 +82,7 @@ void UnconventionalAssignOperatorCheck::check( const auto *Method = Result.Nodes.getNodeAs("method"); for (const auto &Message : Messages) { if (Result.Nodes.getNodeAs(Message[0])) - diag(Method->getLocStart(), Message[1]) + diag(Method->getBeginLoc(), Message[1]) << Method->getParent()->getName() << (Method->isConst() ? "const" : "virtual"); } diff --git a/clang-tidy/misc/UnusedAliasDeclsCheck.cpp b/clang-tidy/misc/UnusedAliasDeclsCheck.cpp index 07165d65d..3ef5b205f 100644 --- a/clang-tidy/misc/UnusedAliasDeclsCheck.cpp +++ b/clang-tidy/misc/UnusedAliasDeclsCheck.cpp @@ -34,7 +34,7 @@ void UnusedAliasDeclsCheck::registerMatchers(MatchFinder *Finder) { void UnusedAliasDeclsCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *AliasDecl = Result.Nodes.getNodeAs("alias")) { FoundDecls[AliasDecl] = CharSourceRange::getCharRange( - AliasDecl->getLocStart(), + AliasDecl->getBeginLoc(), Lexer::findLocationAfterToken( AliasDecl->getLocEnd(), tok::semi, *Result.SourceManager, getLangOpts(), diff --git a/clang-tidy/misc/UnusedParametersCheck.cpp b/clang-tidy/misc/UnusedParametersCheck.cpp index 1e342d1f6..a5dae816f 100644 --- a/clang-tidy/misc/UnusedParametersCheck.cpp +++ b/clang-tidy/misc/UnusedParametersCheck.cpp @@ -41,8 +41,8 @@ static CharSourceRange removeNode(const MatchFinder::MatchResult &Result, const T *PrevNode, const T *Node, const T *NextNode) { if (NextNode) - return CharSourceRange::getCharRange(Node->getLocStart(), - NextNode->getLocStart()); + return CharSourceRange::getCharRange(Node->getBeginLoc(), + NextNode->getBeginLoc()); if (PrevNode) return CharSourceRange::getTokenRange( diff --git a/clang-tidy/misc/UnusedUsingDeclsCheck.cpp b/clang-tidy/misc/UnusedUsingDeclsCheck.cpp index b3eabdcd0..da2704711 100644 --- a/clang-tidy/misc/UnusedUsingDeclsCheck.cpp +++ b/clang-tidy/misc/UnusedUsingDeclsCheck.cpp @@ -68,7 +68,7 @@ void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) { UsingDeclContext Context(Using); Context.UsingDeclRange = CharSourceRange::getCharRange( - Using->getLocStart(), + Using->getBeginLoc(), Lexer::findLocationAfterToken( Using->getLocEnd(), tok::semi, *Result.SourceManager, getLangOpts(), /*SkipTrailingWhitespaceAndNewLine=*/true)); diff --git a/clang-tidy/modernize/AvoidBindCheck.cpp b/clang-tidy/modernize/AvoidBindCheck.cpp index 7cdbb66f4..e0bdd86df 100644 --- a/clang-tidy/modernize/AvoidBindCheck.cpp +++ b/clang-tidy/modernize/AvoidBindCheck.cpp @@ -59,7 +59,7 @@ buildBindArguments(const MatchFinder::MatchResult &Result, const CallExpr *C) { } B.Tokens = Lexer::getSourceText( - CharSourceRange::getTokenRange(E->getLocStart(), E->getLocEnd()), + CharSourceRange::getTokenRange(E->getBeginLoc(), E->getLocEnd()), *Result.SourceManager, Result.Context->getLangOpts()); SmallVector Matches; @@ -131,7 +131,7 @@ void AvoidBindCheck::registerMatchers(MatchFinder *Finder) { void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) { const auto *MatchedDecl = Result.Nodes.getNodeAs("bind"); - auto Diag = diag(MatchedDecl->getLocStart(), "prefer a lambda to std::bind"); + auto Diag = diag(MatchedDecl->getBeginLoc(), "prefer a lambda to std::bind"); const auto Args = buildBindArguments(Result, MatchedDecl); diff --git a/clang-tidy/modernize/MakeSmartPtrCheck.cpp b/clang-tidy/modernize/MakeSmartPtrCheck.cpp index 8deaa83a2..6f3cb1ff2 100644 --- a/clang-tidy/modernize/MakeSmartPtrCheck.cpp +++ b/clang-tidy/modernize/MakeSmartPtrCheck.cpp @@ -199,7 +199,7 @@ void MakeSmartPtrCheck::checkReset(SourceManager &SM, const auto *Expr = cast(Reset->getCallee()); SourceLocation OperatorLoc = Expr->getOperatorLoc(); SourceLocation ResetCallStart = Reset->getExprLoc(); - SourceLocation ExprStart = Expr->getLocStart(); + SourceLocation ExprStart = Expr->getBeginLoc(); SourceLocation ExprEnd = Lexer::getLocForEndOfToken(Expr->getLocEnd(), 0, SM, getLangOpts()); @@ -353,7 +353,7 @@ bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag, // Has to be replaced with: // smart_ptr(Pair{first, second}); InitRange = SourceRange( - New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(), + New->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(), New->getInitializer()->getSourceRange().getEnd()); } Diag << FixItHint::CreateRemoval( diff --git a/clang-tidy/modernize/PassByValueCheck.cpp b/clang-tidy/modernize/PassByValueCheck.cpp index 1ab73d50c..c4f10a3a3 100644 --- a/clang-tidy/modernize/PassByValueCheck.cpp +++ b/clang-tidy/modernize/PassByValueCheck.cpp @@ -194,7 +194,7 @@ void PassByValueCheck::check(const MatchFinder::MatchResult &Result) { *Result.Context)) return; - auto Diag = diag(ParamDecl->getLocStart(), "pass by value and use std::move"); + auto Diag = diag(ParamDecl->getBeginLoc(), "pass by value and use std::move"); // Iterate over all declarations of the constructor. for (const ParmVarDecl *ParmDecl : collectParamDecls(Ctor, ParamDecl)) { @@ -206,7 +206,7 @@ void PassByValueCheck::check(const MatchFinder::MatchResult &Result) { continue; TypeLoc ValueTL = RefTL.getPointeeLoc(); - auto TypeRange = CharSourceRange::getTokenRange(ParmDecl->getLocStart(), + auto TypeRange = CharSourceRange::getTokenRange(ParmDecl->getBeginLoc(), ParamTL.getLocEnd()); std::string ValueStr = Lexer::getSourceText(CharSourceRange::getTokenRange( ValueTL.getSourceRange()), diff --git a/clang-tidy/modernize/RawStringLiteralCheck.cpp b/clang-tidy/modernize/RawStringLiteralCheck.cpp index 868c5c7cf..a4aef41f7 100644 --- a/clang-tidy/modernize/RawStringLiteralCheck.cpp +++ b/clang-tidy/modernize/RawStringLiteralCheck.cpp @@ -129,14 +129,14 @@ void RawStringLiteralCheck::registerMatchers(MatchFinder *Finder) { void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) { const auto *Literal = Result.Nodes.getNodeAs("lit"); - if (Literal->getLocStart().isMacroID()) + if (Literal->getBeginLoc().isMacroID()) return; if (containsEscapedCharacters(Result, Literal, DisallowedChars)) { std::string Replacement = asRawStringLiteral(Literal, DelimiterStem); if (ReplaceShorterLiterals || Replacement.length() <= - Lexer::MeasureTokenLength(Literal->getLocStart(), + Lexer::MeasureTokenLength(Literal->getBeginLoc(), *Result.SourceManager, getLangOpts())) replaceWithRawStringLiteral(Result, Literal, Replacement); } @@ -148,7 +148,7 @@ void RawStringLiteralCheck::replaceWithRawStringLiteral( CharSourceRange CharRange = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(Literal->getSourceRange()), *Result.SourceManager, getLangOpts()); - diag(Literal->getLocStart(), + diag(Literal->getBeginLoc(), "escaped string literal can be written as a raw string literal") << FixItHint::CreateReplacement(CharRange, Replacement); } diff --git a/clang-tidy/modernize/RedundantVoidArgCheck.cpp b/clang-tidy/modernize/RedundantVoidArgCheck.cpp index 6701fa45c..a68033e5a 100644 --- a/clang-tidy/modernize/RedundantVoidArgCheck.cpp +++ b/clang-tidy/modernize/RedundantVoidArgCheck.cpp @@ -103,9 +103,9 @@ void RedundantVoidArgCheck::processFunctionDecl( const MatchFinder::MatchResult &Result, const FunctionDecl *Function) { if (Function->isThisDeclarationADefinition()) { const Stmt *Body = Function->getBody(); - SourceLocation Start = Function->getLocStart(); + SourceLocation Start = Function->getBeginLoc(); SourceLocation End = - Body ? Body->getLocStart().getLocWithOffset(-1) : Function->getLocEnd(); + Body ? Body->getBeginLoc().getLocWithOffset(-1) : Function->getLocEnd(); removeVoidArgumentTokens(Result, SourceRange(Start, End), "function definition"); } else { @@ -198,10 +198,10 @@ void RedundantVoidArgCheck::processFieldDecl( void RedundantVoidArgCheck::processVarDecl( const MatchFinder::MatchResult &Result, const VarDecl *Var) { if (protoTypeHasNoParms(Var->getType())) { - SourceLocation Begin = Var->getLocStart(); + SourceLocation Begin = Var->getBeginLoc(); if (Var->hasInit()) { SourceLocation InitStart = - Result.SourceManager->getExpansionLoc(Var->getInit()->getLocStart()) + Result.SourceManager->getExpansionLoc(Var->getInit()->getBeginLoc()) .getLocWithOffset(-1); removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart), "variable declaration with initializer"); @@ -237,7 +237,7 @@ void RedundantVoidArgCheck::processLambdaExpr( Lambda->hasExplicitParameters()) { SourceLocation Begin = Lambda->getIntroducerRange().getEnd().getLocWithOffset(1); - SourceLocation End = Lambda->getBody()->getLocStart().getLocWithOffset(-1); + SourceLocation End = Lambda->getBody()->getBeginLoc().getLocWithOffset(-1); removeVoidArgumentTokens(Result, SourceRange(Begin, End), "lambda expression"); } diff --git a/clang-tidy/modernize/ReplaceRandomShuffleCheck.cpp b/clang-tidy/modernize/ReplaceRandomShuffleCheck.cpp index cd9aa11d1..9a7156935 100644 --- a/clang-tidy/modernize/ReplaceRandomShuffleCheck.cpp +++ b/clang-tidy/modernize/ReplaceRandomShuffleCheck.cpp @@ -62,13 +62,13 @@ void ReplaceRandomShuffleCheck::check(const MatchFinder::MatchResult &Result) { const auto *MatchedArgumentThree = Result.Nodes.getNodeAs("randomFunc"); const auto *MatchedCallExpr = Result.Nodes.getNodeAs("match"); - if (MatchedCallExpr->getLocStart().isMacroID()) + if (MatchedCallExpr->getBeginLoc().isMacroID()) return; auto Diag = [&] { if (MatchedCallExpr->getNumArgs() == 3) { auto DiagL = - diag(MatchedCallExpr->getLocStart(), + diag(MatchedCallExpr->getBeginLoc(), "'std::random_shuffle' has been removed in C++17; use " "'std::shuffle' and an alternative random mechanism instead"); DiagL << FixItHint::CreateReplacement( @@ -76,7 +76,7 @@ void ReplaceRandomShuffleCheck::check(const MatchFinder::MatchResult &Result) { "std::mt19937(std::random_device()())"); return DiagL; } else { - auto DiagL = diag(MatchedCallExpr->getLocStart(), + auto DiagL = diag(MatchedCallExpr->getBeginLoc(), "'std::random_shuffle' has been removed in C++17; use " "'std::shuffle' instead"); DiagL << FixItHint::CreateInsertion( @@ -94,12 +94,12 @@ void ReplaceRandomShuffleCheck::check(const MatchFinder::MatchResult &Result) { NewName = "std::" + NewName; Diag << FixItHint::CreateRemoval(MatchedDecl->getSourceRange()); - Diag << FixItHint::CreateInsertion(MatchedDecl->getLocStart(), NewName); + Diag << FixItHint::CreateInsertion(MatchedDecl->getBeginLoc(), NewName); if (Optional IncludeFixit = IncludeInserter->CreateIncludeInsertion( Result.Context->getSourceManager().getFileID( - MatchedCallExpr->getLocStart()), + MatchedCallExpr->getBeginLoc()), "random", /*IsAngled=*/true)) Diag << IncludeFixit.getValue(); } diff --git a/clang-tidy/modernize/ShrinkToFitCheck.cpp b/clang-tidy/modernize/ShrinkToFitCheck.cpp index f197399b0..bc0749b95 100644 --- a/clang-tidy/modernize/ShrinkToFitCheck.cpp +++ b/clang-tidy/modernize/ShrinkToFitCheck.cpp @@ -59,7 +59,7 @@ void ShrinkToFitCheck::check(const MatchFinder::MatchResult &Result) { const auto *Container = Result.Nodes.getNodeAs("ContainerToShrink"); FixItHint Hint; - if (!MemberCall->getLocStart().isMacroID()) { + if (!MemberCall->getBeginLoc().isMacroID()) { const LangOptions &Opts = getLangOpts(); std::string ReplacementText; if (const auto *UnaryOp = llvm::dyn_cast(Container)) { @@ -79,7 +79,7 @@ void ShrinkToFitCheck::check(const MatchFinder::MatchResult &Result) { ReplacementText); } - diag(MemberCall->getLocStart(), "the shrink_to_fit method should be used " + diag(MemberCall->getBeginLoc(), "the shrink_to_fit method should be used " "to reduce the capacity of a shrinkable " "container") << Hint; diff --git a/clang-tidy/modernize/UnaryStaticAssertCheck.cpp b/clang-tidy/modernize/UnaryStaticAssertCheck.cpp index 67c00630a..5ddbb933c 100644 --- a/clang-tidy/modernize/UnaryStaticAssertCheck.cpp +++ b/clang-tidy/modernize/UnaryStaticAssertCheck.cpp @@ -32,7 +32,7 @@ void UnaryStaticAssertCheck::check(const MatchFinder::MatchResult &Result) { SourceLocation Loc = MatchedDecl->getLocation(); if (!AssertMessage || AssertMessage->getLength() || - AssertMessage->getLocStart().isMacroID() || Loc.isMacroID()) + AssertMessage->getBeginLoc().isMacroID() || Loc.isMacroID()) return; diag(Loc, diff --git a/clang-tidy/modernize/UseBoolLiteralsCheck.cpp b/clang-tidy/modernize/UseBoolLiteralsCheck.cpp index ece8cd586..13afbb43e 100644 --- a/clang-tidy/modernize/UseBoolLiteralsCheck.cpp +++ b/clang-tidy/modernize/UseBoolLiteralsCheck.cpp @@ -57,7 +57,7 @@ void UseBoolLiteralsCheck::check(const MatchFinder::MatchResult &Result) { const Expr *Expression = Cast ? Cast : Literal; - bool InMacro = Expression->getLocStart().isMacroID(); + bool InMacro = Expression->getBeginLoc().isMacroID(); if (InMacro && IgnoreMacros) return; diff --git a/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp b/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp index c14c6853b..374fcb34d 100644 --- a/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp +++ b/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp @@ -202,7 +202,7 @@ void UseDefaultMemberInitCheck::checkDefaultInit( const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) { const FieldDecl *Field = Init->getAnyMember(); - SourceLocation StartLoc = Field->getLocStart(); + SourceLocation StartLoc = Field->getBeginLoc(); if (StartLoc.isMacroID() && IgnoreMacros) return; diff --git a/clang-tidy/modernize/UseEqualsDefaultCheck.cpp b/clang-tidy/modernize/UseEqualsDefaultCheck.cpp index 88f4485c1..d67bb799e 100644 --- a/clang-tidy/modernize/UseEqualsDefaultCheck.cpp +++ b/clang-tidy/modernize/UseEqualsDefaultCheck.cpp @@ -298,7 +298,7 @@ void UseEqualsDefaultCheck::check(const MatchFinder::MatchResult &Result) { // expansion locations are reported. SourceLocation Location = SpecialFunctionDecl->getLocation(); if (Location.isMacroID()) - Location = Body->getLocStart(); + Location = Body->getBeginLoc(); auto Diag = diag(Location, "use '= default' to define a trivial " + SpecialFunctionName); diff --git a/clang-tidy/modernize/UseNullptrCheck.cpp b/clang-tidy/modernize/UseNullptrCheck.cpp index 476c549e2..5f5ee71cf 100644 --- a/clang-tidy/modernize/UseNullptrCheck.cpp +++ b/clang-tidy/modernize/UseNullptrCheck.cpp @@ -125,7 +125,7 @@ class MacroArgUsageVisitor : public RecursiveASTVisitor { } bool VisitStmt(Stmt *S) { - if (SM.getFileLoc(S->getLocStart()) != CastLoc) + if (SM.getFileLoc(S->getBeginLoc()) != CastLoc) return true; Visited = true; @@ -214,7 +214,7 @@ class CastSequenceVisitor : public RecursiveASTVisitor { return true; } - SourceLocation StartLoc = FirstSubExpr->getLocStart(); + SourceLocation StartLoc = FirstSubExpr->getBeginLoc(); SourceLocation EndLoc = FirstSubExpr->getLocEnd(); // If the location comes from a macro arg expansion, *all* uses of that @@ -269,7 +269,7 @@ class CastSequenceVisitor : public RecursiveASTVisitor { /// \brief Tests that all expansions of a macro arg, one of which expands to /// result in \p CE, yield NullTo(Member)Pointer casts. bool allArgUsesValid(const CastExpr *CE) { - SourceLocation CastLoc = CE->getLocStart(); + SourceLocation CastLoc = CE->getBeginLoc(); // Step 1: Get location of macro arg and location of the macro the arg was // provided to. @@ -437,9 +437,9 @@ class CastSequenceVisitor : public RecursiveASTVisitor { SourceLocation Loc; if (const auto *D = Parent.get()) - Loc = D->getLocStart(); + Loc = D->getBeginLoc(); else if (const auto *S = Parent.get()) - Loc = S->getLocStart(); + Loc = S->getBeginLoc(); // TypeLoc and NestedNameSpecifierLoc are members of the parent map. Skip // them and keep going up. diff --git a/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp b/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp index 7f5920076..0389a5ed7 100644 --- a/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp +++ b/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp @@ -85,8 +85,8 @@ void UseTransparentFunctorsCheck::check( Result.Nodes.getNodeAs("FunctorClass"); if (const auto *FuncInst = Result.Nodes.getNodeAs("FuncInst")) { - diag(FuncInst->getLocStart(), Message) - << (FuncClass->getName() + "<>").str(); + diag(FuncInst->getBeginLoc(), Message) + << (FuncClass->getName() + "<>").str(); return; } diff --git a/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp b/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp index 9ac6e1d53..8a659b7c0 100644 --- a/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp +++ b/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp @@ -57,14 +57,14 @@ void UseUncaughtExceptionsCheck::check(const MatchFinder::MatchResult &Result) { bool WarnOnly = false; if (C) { - BeginLoc = C->getLocStart(); + BeginLoc = C->getBeginLoc(); EndLoc = C->getLocEnd(); } else if (const auto *E = Result.Nodes.getNodeAs("call_expr")) { - BeginLoc = E->getLocStart(); + BeginLoc = E->getBeginLoc(); EndLoc = E->getLocEnd(); } else if (const auto *D = Result.Nodes.getNodeAs("decl_ref_expr")) { - BeginLoc = D->getLocStart(); + BeginLoc = D->getBeginLoc(); EndLoc = D->getLocEnd(); WarnOnly = true; } else { diff --git a/clang-tidy/modernize/UseUsingCheck.cpp b/clang-tidy/modernize/UseUsingCheck.cpp index cc6d77d90..5244aa60e 100644 --- a/clang-tidy/modernize/UseUsingCheck.cpp +++ b/clang-tidy/modernize/UseUsingCheck.cpp @@ -83,7 +83,7 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) { auto &Context = *Result.Context; auto &SM = *Result.SourceManager; - SourceLocation StartLoc = MatchedDecl->getLocStart(); + SourceLocation StartLoc = MatchedDecl->getBeginLoc(); if (StartLoc.isMacroID() && IgnoreMacros) return; diff --git a/clang-tidy/objc/AvoidNSErrorInitCheck.cpp b/clang-tidy/objc/AvoidNSErrorInitCheck.cpp index 86c4656c1..a4dc48eba 100644 --- a/clang-tidy/objc/AvoidNSErrorInitCheck.cpp +++ b/clang-tidy/objc/AvoidNSErrorInitCheck.cpp @@ -31,7 +31,7 @@ void AvoidNSErrorInitCheck::registerMatchers(MatchFinder *Finder) { void AvoidNSErrorInitCheck::check(const MatchFinder::MatchResult &Result) { const auto *MatchedExpr = Result.Nodes.getNodeAs("nserrorInit"); - diag(MatchedExpr->getLocStart(), + diag(MatchedExpr->getBeginLoc(), "use errorWithDomain:code:userInfo: or initWithDomain:code:userInfo: to " "create a new NSError"); } diff --git a/clang-tidy/objc/AvoidSpinlockCheck.cpp b/clang-tidy/objc/AvoidSpinlockCheck.cpp index 21ec36421..319d94567 100644 --- a/clang-tidy/objc/AvoidSpinlockCheck.cpp +++ b/clang-tidy/objc/AvoidSpinlockCheck.cpp @@ -27,7 +27,7 @@ void AvoidSpinlockCheck::registerMatchers(MatchFinder *Finder) { void AvoidSpinlockCheck::check(const MatchFinder::MatchResult &Result) { const auto *MatchedExpr = Result.Nodes.getNodeAs("spinlock"); - diag(MatchedExpr->getLocStart(), + diag(MatchedExpr->getBeginLoc(), "use os_unfair_lock_lock() or dispatch queue APIs instead of the " "deprecated OSSpinLock"); } diff --git a/clang-tidy/performance/FasterStringFindCheck.cpp b/clang-tidy/performance/FasterStringFindCheck.cpp index eddc52b63..71e5e4084 100644 --- a/clang-tidy/performance/FasterStringFindCheck.cpp +++ b/clang-tidy/performance/FasterStringFindCheck.cpp @@ -90,13 +90,14 @@ void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) { if (!Replacement) return; - diag(Literal->getLocStart(), "%0 called with a string literal consisting of " + diag(Literal->getBeginLoc(), "%0 called with a string literal consisting of " "a single character; consider using the more " "effective overload accepting a character") - << FindFunc << FixItHint::CreateReplacement( - CharSourceRange::getTokenRange(Literal->getLocStart(), - Literal->getLocEnd()), - *Replacement); + << FindFunc + << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(Literal->getBeginLoc(), + Literal->getLocEnd()), + *Replacement); } } // namespace performance diff --git a/clang-tidy/performance/ForRangeCopyCheck.cpp b/clang-tidy/performance/ForRangeCopyCheck.cpp index 2358aacb2..c8c906382 100644 --- a/clang-tidy/performance/ForRangeCopyCheck.cpp +++ b/clang-tidy/performance/ForRangeCopyCheck.cpp @@ -41,7 +41,7 @@ void ForRangeCopyCheck::registerMatchers(MatchFinder *Finder) { void ForRangeCopyCheck::check(const MatchFinder::MatchResult &Result) { const auto *Var = Result.Nodes.getNodeAs("loopVar"); // Ignore code in macros since we can't place the fixes correctly. - if (Var->getLocStart().isMacroID()) + if (Var->getBeginLoc().isMacroID()) return; if (handleConstValueCopy(*Var, *Result.Context)) return; diff --git a/clang-tidy/performance/ImplicitConversionInLoopCheck.cpp b/clang-tidy/performance/ImplicitConversionInLoopCheck.cpp index 1a5605f5f..6e7311901 100644 --- a/clang-tidy/performance/ImplicitConversionInLoopCheck.cpp +++ b/clang-tidy/performance/ImplicitConversionInLoopCheck.cpp @@ -96,7 +96,7 @@ void ImplicitConversionInLoopCheck::ReportAndFix( "change the type to the matching one (%1 but 'const auto&' is always a " "valid option) or remove the reference to make it explicit that you are " "creating a new value"; - diag(VD->getLocStart(), Message) << VD << ConstRefType; + diag(VD->getBeginLoc(), Message) << VD << ConstRefType; } } // namespace performance diff --git a/clang-tidy/performance/InefficientAlgorithmCheck.cpp b/clang-tidy/performance/InefficientAlgorithmCheck.cpp index 4471d0776..8cee28179 100644 --- a/clang-tidy/performance/InefficientAlgorithmCheck.cpp +++ b/clang-tidy/performance/InefficientAlgorithmCheck.cpp @@ -99,7 +99,7 @@ void InefficientAlgorithmCheck::check(const MatchFinder::MatchResult &Result) { .getUnqualifiedType() .getCanonicalType(); if (AlgCmp != ContainerCmp) { - diag(Arg->getLocStart(), + diag(Arg->getBeginLoc(), "different comparers used in the algorithm and the container"); return; } @@ -153,7 +153,7 @@ void InefficientAlgorithmCheck::check(const MatchFinder::MatchResult &Result) { Hint = FixItHint::CreateReplacement(CallRange, ReplacementText); } - diag(AlgCall->getLocStart(), + diag(AlgCall->getBeginLoc(), "this STL algorithm call should be replaced with a container method") << Hint; } diff --git a/clang-tidy/performance/InefficientVectorOperationCheck.cpp b/clang-tidy/performance/InefficientVectorOperationCheck.cpp index 33c68f014..5b0837619 100644 --- a/clang-tidy/performance/InefficientVectorOperationCheck.cpp +++ b/clang-tidy/performance/InefficientVectorOperationCheck.cpp @@ -168,7 +168,7 @@ void InefficientVectorOperationCheck::check( // FIXME: make it more intelligent to identify the pre-allocating operations // before the for loop. if (SM.isBeforeInTranslationUnit(Ref->getLocation(), - LoopStmt->getLocStart())) { + LoopStmt->getBeginLoc())) { return; } } @@ -200,13 +200,13 @@ void InefficientVectorOperationCheck::check( } auto Diag = - diag(VectorAppendCall->getLocStart(), + diag(VectorAppendCall->getBeginLoc(), "%0 is called inside a loop; " "consider pre-allocating the vector capacity before the loop") << VectorAppendCall->getMethodDecl()->getDeclName(); if (!ReserveStmt.empty()) - Diag << FixItHint::CreateInsertion(LoopStmt->getLocStart(), ReserveStmt); + Diag << FixItHint::CreateInsertion(LoopStmt->getBeginLoc(), ReserveStmt); } } // namespace performance diff --git a/clang-tidy/performance/MoveConstArgCheck.cpp b/clang-tidy/performance/MoveConstArgCheck.cpp index 8d4948029..c9c7be19f 100644 --- a/clang-tidy/performance/MoveConstArgCheck.cpp +++ b/clang-tidy/performance/MoveConstArgCheck.cpp @@ -23,7 +23,7 @@ static void ReplaceCallWithArg(const CallExpr *Call, DiagnosticBuilder &Diag, const Expr *Arg = Call->getArg(0); CharSourceRange BeforeArgumentsRange = Lexer::makeFileCharRange( - CharSourceRange::getCharRange(Call->getLocStart(), Arg->getLocStart()), + CharSourceRange::getCharRange(Call->getBeginLoc(), Arg->getBeginLoc()), SM, LangOpts); CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange( CharSourceRange::getCharRange(Call->getLocEnd(), diff --git a/clang-tidy/performance/TypePromotionInMathFnCheck.cpp b/clang-tidy/performance/TypePromotionInMathFnCheck.cpp index 441bad38c..8ff31a061 100644 --- a/clang-tidy/performance/TypePromotionInMathFnCheck.cpp +++ b/clang-tidy/performance/TypePromotionInMathFnCheck.cpp @@ -195,7 +195,7 @@ void TypePromotionInMathFnCheck::check(const MatchFinder::MatchResult &Result) { // declared in . if (FnInCmath) if (auto IncludeFixit = IncludeInserter->CreateIncludeInsertion( - Result.Context->getSourceManager().getFileID(Call->getLocStart()), + Result.Context->getSourceManager().getFileID(Call->getBeginLoc()), "cmath", /*IsAngled=*/true)) Diag << *IncludeFixit; } diff --git a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp index 588a29825..8c9259c79 100644 --- a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp +++ b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp @@ -156,7 +156,7 @@ void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) { // compilation unit as the signature change could introduce build errors. // 4. the function is an explicit template specialization. const auto *Method = llvm::dyn_cast(Function); - if (Param->getLocStart().isMacroID() || (Method && Method->isVirtual()) || + if (Param->getBeginLoc().isMacroID() || (Method && Method->isVirtual()) || isReferencedOutsideOfCallExpr(*Function, *Result.Context) || isExplicitTemplateSpecialization(*Function)) return; @@ -189,20 +189,20 @@ void UnnecessaryValueParamCheck::storeOptions( void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument, const ASTContext &Context) { - auto Diag = diag(CopyArgument.getLocStart(), + auto Diag = diag(CopyArgument.getBeginLoc(), "parameter %0 is passed by value and only copied once; " "consider moving it to avoid unnecessary copies") << &Var; // Do not propose fixes in macros since we cannot place them correctly. - if (CopyArgument.getLocStart().isMacroID()) + if (CopyArgument.getBeginLoc().isMacroID()) return; const auto &SM = Context.getSourceManager(); auto EndLoc = Lexer::getLocForEndOfToken(CopyArgument.getLocation(), 0, SM, Context.getLangOpts()); - Diag << FixItHint::CreateInsertion(CopyArgument.getLocStart(), "std::move(") + Diag << FixItHint::CreateInsertion(CopyArgument.getBeginLoc(), "std::move(") << FixItHint::CreateInsertion(EndLoc, ")"); if (auto IncludeFixit = Inserter->CreateIncludeInsertion( - SM.getFileID(CopyArgument.getLocStart()), "utility", + SM.getFileID(CopyArgument.getBeginLoc()), "utility", /*IsAngled=*/true)) Diag << *IncludeFixit; } diff --git a/clang-tidy/readability/AvoidConstParamsInDecls.cpp b/clang-tidy/readability/AvoidConstParamsInDecls.cpp index 70329e83b..8b2218817 100644 --- a/clang-tidy/readability/AvoidConstParamsInDecls.cpp +++ b/clang-tidy/readability/AvoidConstParamsInDecls.cpp @@ -22,7 +22,7 @@ namespace { SourceRange getTypeRange(const ParmVarDecl &Param) { if (Param.getIdentifier() != nullptr) - return SourceRange(Param.getLocStart(), + return SourceRange(Param.getBeginLoc(), Param.getLocEnd().getLocWithOffset(-1)); return Param.getSourceRange(); } @@ -82,7 +82,7 @@ void AvoidConstParamsInDecls::check(const MatchFinder::MatchResult &Result) { if (!Param->getType().isLocalConstQualified()) return; - auto Diag = diag(Param->getLocStart(), + auto Diag = diag(Param->getBeginLoc(), "parameter %0 is const-qualified in the function " "declaration; const-qualification of parameters only has an " "effect in function definitions"); @@ -97,7 +97,7 @@ void AvoidConstParamsInDecls::check(const MatchFinder::MatchResult &Result) { Diag << Param; } - if (Param->getLocStart().isMacroID() != Param->getLocEnd().isMacroID()) { + if (Param->getBeginLoc().isMacroID() != Param->getLocEnd().isMacroID()) { // Do not offer a suggestion if the part of the variable declaration comes // from a macro. return; diff --git a/clang-tidy/readability/BracesAroundStatementsCheck.cpp b/clang-tidy/readability/BracesAroundStatementsCheck.cpp index 1bff668df..a29e68a36 100644 --- a/clang-tidy/readability/BracesAroundStatementsCheck.cpp +++ b/clang-tidy/readability/BracesAroundStatementsCheck.cpp @@ -174,7 +174,7 @@ BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S, const SourceManager &SM, const ASTContext *Context) { // Skip macros. - if (S->getLocStart().isMacroID()) + if (S->getBeginLoc().isMacroID()) return SourceLocation(); SourceLocation CondEndLoc = S->getCond()->getLocEnd(); @@ -232,7 +232,7 @@ bool BracesAroundStatementsCheck::checkStmt( // level as the start of the statement. We also need file locations for // Lexer::getLocForEndOfToken working properly. InitialLoc = Lexer::makeFileCharRange( - CharSourceRange::getCharRange(InitialLoc, S->getLocStart()), + CharSourceRange::getCharRange(InitialLoc, S->getBeginLoc()), SM, Context->getLangOpts()) .getBegin(); if (InitialLoc.isInvalid()) diff --git a/clang-tidy/readability/ContainerSizeEmptyCheck.cpp b/clang-tidy/readability/ContainerSizeEmptyCheck.cpp index 5604354f1..60a153a80 100644 --- a/clang-tidy/readability/ContainerSizeEmptyCheck.cpp +++ b/clang-tidy/readability/ContainerSizeEmptyCheck.cpp @@ -201,12 +201,12 @@ void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) { } if (MemberCall) { - diag(MemberCall->getLocStart(), + diag(MemberCall->getBeginLoc(), "the 'empty' method should be used to check " "for emptiness instead of 'size'") << Hint; } else { - diag(BinCmp->getLocStart(), + diag(BinCmp->getBeginLoc(), "the 'empty' method should be used to check " "for emptiness instead of comparing to an empty object") << Hint; diff --git a/clang-tidy/readability/DeleteNullPointerCheck.cpp b/clang-tidy/readability/DeleteNullPointerCheck.cpp index 766dfdacb..4d3188c43 100644 --- a/clang-tidy/readability/DeleteNullPointerCheck.cpp +++ b/clang-tidy/readability/DeleteNullPointerCheck.cpp @@ -55,14 +55,14 @@ void DeleteNullPointerCheck::check(const MatchFinder::MatchResult &Result) { const auto *Compound = Result.Nodes.getNodeAs("compound"); auto Diag = diag( - IfWithDelete->getLocStart(), + IfWithDelete->getBeginLoc(), "'if' statement is unnecessary; deleting null pointer has no effect"); if (IfWithDelete->getElse()) return; // FIXME: generate fixit for this case. Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( - IfWithDelete->getLocStart(), + IfWithDelete->getBeginLoc(), Lexer::getLocForEndOfToken(IfWithDelete->getCond()->getLocEnd(), 0, *Result.SourceManager, Result.Context->getLangOpts()))); diff --git a/clang-tidy/readability/DeletedDefaultCheck.cpp b/clang-tidy/readability/DeletedDefaultCheck.cpp index a197e8dcd..e99ca8379 100644 --- a/clang-tidy/readability/DeletedDefaultCheck.cpp +++ b/clang-tidy/readability/DeletedDefaultCheck.cpp @@ -41,7 +41,7 @@ void DeletedDefaultCheck::check(const MatchFinder::MatchResult &Result) { "either be removed or explicitly deleted"; if (const auto *Constructor = Result.Nodes.getNodeAs("constructor")) { - auto Diag = diag(Constructor->getLocStart(), Message); + auto Diag = diag(Constructor->getBeginLoc(), Message); if (Constructor->isDefaultConstructor()) { Diag << "default constructor" << "a non-static data member or a base class is lacking a default " @@ -56,7 +56,7 @@ void DeletedDefaultCheck::check(const MatchFinder::MatchResult &Result) { } } else if (const auto *Assignment = Result.Nodes.getNodeAs("method-decl")) { - diag(Assignment->getLocStart(), Message) + diag(Assignment->getBeginLoc(), Message) << (Assignment->isCopyAssignmentOperator() ? "copy assignment operator" : "move assignment operator") << "a base class or a non-static data member is not assignable, e.g. " diff --git a/clang-tidy/readability/FunctionSizeCheck.cpp b/clang-tidy/readability/FunctionSizeCheck.cpp index 8ed6b9077..b1ceea11d 100644 --- a/clang-tidy/readability/FunctionSizeCheck.cpp +++ b/clang-tidy/readability/FunctionSizeCheck.cpp @@ -73,7 +73,7 @@ class FunctionASTVisitor : public RecursiveASTVisitor { // is already nested NestingThreshold levels deep, record the start location // of this new compound statement. if (CurrentNestingLevel == Info.NestingThreshold) - Info.NestingThresholders.push_back(Node->getLocStart()); + Info.NestingThresholders.push_back(Node->getBeginLoc()); ++CurrentNestingLevel; Base::TraverseCompoundStmt(Node); @@ -162,9 +162,9 @@ void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) { // Count the lines including whitespace and comments. Really simple. if (const Stmt *Body = Func->getBody()) { SourceManager *SM = Result.SourceManager; - if (SM->isWrittenInSameFile(Body->getLocStart(), Body->getLocEnd())) { + if (SM->isWrittenInSameFile(Body->getBeginLoc(), Body->getLocEnd())) { FI.Lines = SM->getSpellingLineNumber(Body->getLocEnd()) - - SM->getSpellingLineNumber(Body->getLocStart()); + SM->getSpellingLineNumber(Body->getBeginLoc()); } } diff --git a/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tidy/readability/IdentifierNamingCheck.cpp index 46919d3fc..90c34b372 100644 --- a/clang-tidy/readability/IdentifierNamingCheck.cpp +++ b/clang-tidy/readability/IdentifierNamingCheck.cpp @@ -841,7 +841,7 @@ void IdentifierNamingCheck::check(const MatchFinder::MatchResult &Result) { if (StringRef(Fixup).equals(Name)) { if (!IgnoreFailedSplit) { LLVM_DEBUG(llvm::dbgs() - << Decl->getLocStart().printToString(*Result.SourceManager) + << Decl->getBeginLoc().printToString(*Result.SourceManager) << llvm::format(": unable to split words for %s '%s'\n", KindName.c_str(), Name.str().c_str())); } diff --git a/clang-tidy/readability/ImplicitBoolConversionCheck.cpp b/clang-tidy/readability/ImplicitBoolConversionCheck.cpp index 79022d425..2a449ff6e 100644 --- a/clang-tidy/readability/ImplicitBoolConversionCheck.cpp +++ b/clang-tidy/readability/ImplicitBoolConversionCheck.cpp @@ -24,14 +24,14 @@ namespace { AST_MATCHER(Stmt, isMacroExpansion) { SourceManager &SM = Finder->getASTContext().getSourceManager(); - SourceLocation Loc = Node.getLocStart(); + SourceLocation Loc = Node.getBeginLoc(); return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc); } bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) { SourceManager &SM = Context.getSourceManager(); const LangOptions &LO = Context.getLangOpts(); - SourceLocation Loc = Statement->getLocStart(); + SourceLocation Loc = Statement->getBeginLoc(); return SM.isMacroBodyExpansion(Loc) && Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL"; } @@ -97,9 +97,9 @@ void fixGenericExprCastToBool(DiagnosticBuilder &Diag, bool InvertComparison = Parent != nullptr && isUnaryLogicalNotOperator(Parent); if (InvertComparison) { - SourceLocation ParentStartLoc = Parent->getLocStart(); + SourceLocation ParentStartLoc = Parent->getBeginLoc(); SourceLocation ParentEndLoc = - cast(Parent)->getSubExpr()->getLocStart(); + cast(Parent)->getSubExpr()->getBeginLoc(); Diag << FixItHint::CreateRemoval( CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc)); @@ -122,7 +122,7 @@ void fixGenericExprCastToBool(DiagnosticBuilder &Diag, } if (!StartLocInsertion.empty()) { - Diag << FixItHint::CreateInsertion(Cast->getLocStart(), StartLocInsertion); + Diag << FixItHint::CreateInsertion(Cast->getBeginLoc(), StartLocInsertion); } std::string EndLocInsertion; @@ -183,7 +183,7 @@ void fixGenericExprCastFromBool(DiagnosticBuilder &Diag, bool NeedParens = !isa(SubExpr); Diag << FixItHint::CreateInsertion( - Cast->getLocStart(), + Cast->getBeginLoc(), (Twine("static_cast<") + OtherType + ">" + (NeedParens ? "(" : "")) .str()); @@ -354,7 +354,7 @@ void ImplicitBoolConversionCheck::handleCastToBool(const ImplicitCastExpr *Cast, return; } - auto Diag = diag(Cast->getLocStart(), "implicit conversion %0 -> bool") + auto Diag = diag(Cast->getBeginLoc(), "implicit conversion %0 -> bool") << Cast->getSubExpr()->getType(); StringRef EquivalentLiteral = @@ -371,7 +371,7 @@ void ImplicitBoolConversionCheck::handleCastFromBool( ASTContext &Context) { QualType DestType = NextImplicitCast ? NextImplicitCast->getType() : Cast->getType(); - auto Diag = diag(Cast->getLocStart(), "implicit conversion bool -> %0") + auto Diag = diag(Cast->getBeginLoc(), "implicit conversion bool -> %0") << DestType; if (const auto *BoolLiteral = diff --git a/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp b/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp index f1d0036c6..280c354fa 100644 --- a/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp +++ b/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp @@ -324,7 +324,7 @@ void InconsistentDeclarationParameterNameCheck::check( return; } - SourceLocation StartLoc = OriginalDeclaration->getLocStart(); + SourceLocation StartLoc = OriginalDeclaration->getBeginLoc(); if (StartLoc.isMacroID() && IgnoreMacros) { markRedeclarationsAsVisited(OriginalDeclaration); return; diff --git a/clang-tidy/readability/MisleadingIndentationCheck.cpp b/clang-tidy/readability/MisleadingIndentationCheck.cpp index 8a6c8c475..5abace500 100644 --- a/clang-tidy/readability/MisleadingIndentationCheck.cpp +++ b/clang-tidy/readability/MisleadingIndentationCheck.cpp @@ -79,15 +79,15 @@ void MisleadingIndentationCheck::missingBracesCheck(const SourceManager &SM, if (isa(Inner)) continue; - SourceLocation InnerLoc = Inner->getLocStart(); - SourceLocation OuterLoc = CurrentStmt->getLocStart(); + SourceLocation InnerLoc = Inner->getBeginLoc(); + SourceLocation OuterLoc = CurrentStmt->getBeginLoc(); if (SM.getExpansionLineNumber(InnerLoc) == SM.getExpansionLineNumber(OuterLoc)) continue; const Stmt *NextStmt = CStmt->body_begin()[i + 1]; - SourceLocation NextLoc = NextStmt->getLocStart(); + SourceLocation NextLoc = NextStmt->getBeginLoc(); if (InnerLoc.isMacroID() || OuterLoc.isMacroID() || NextLoc.isMacroID()) continue; diff --git a/clang-tidy/readability/MisplacedArrayIndexCheck.cpp b/clang-tidy/readability/MisplacedArrayIndexCheck.cpp index f5e09fabb..3d1971bed 100644 --- a/clang-tidy/readability/MisplacedArrayIndexCheck.cpp +++ b/clang-tidy/readability/MisplacedArrayIndexCheck.cpp @@ -30,7 +30,7 @@ void MisplacedArrayIndexCheck::check(const MatchFinder::MatchResult &Result) { const auto *ArraySubscriptE = Result.Nodes.getNodeAs("expr"); - auto Diag = diag(ArraySubscriptE->getLocStart(), "confusing array subscript " + auto Diag = diag(ArraySubscriptE->getBeginLoc(), "confusing array subscript " "expression, usually the " "index is inside the []"); diff --git a/clang-tidy/readability/NamedParameterCheck.cpp b/clang-tidy/readability/NamedParameterCheck.cpp index ffdc813be..6fa9e68f2 100644 --- a/clang-tidy/readability/NamedParameterCheck.cpp +++ b/clang-tidy/readability/NamedParameterCheck.cpp @@ -59,7 +59,7 @@ void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) { // Sanity check the source locations. if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() || - !SM.isWrittenInSameFile(Parm->getLocStart(), Parm->getLocation())) + !SM.isWrittenInSameFile(Parm->getBeginLoc(), Parm->getLocation())) continue; // Skip gmock testing::Unused parameters. @@ -73,7 +73,7 @@ void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) { // Look for comments. We explicitly want to allow idioms like // void foo(int /*unused*/) - const char *Begin = SM.getCharacterData(Parm->getLocStart()); + const char *Begin = SM.getCharacterData(Parm->getBeginLoc()); const char *End = SM.getCharacterData(Parm->getLocation()); StringRef Data(Begin, End - Begin); if (Data.find("/*") != StringRef::npos) diff --git a/clang-tidy/readability/NamespaceCommentCheck.cpp b/clang-tidy/readability/NamespaceCommentCheck.cpp index 409aba90e..229cc626e 100644 --- a/clang-tidy/readability/NamespaceCommentCheck.cpp +++ b/clang-tidy/readability/NamespaceCommentCheck.cpp @@ -69,12 +69,12 @@ void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) { const auto *ND = Result.Nodes.getNodeAs("namespace"); const SourceManager &Sources = *Result.SourceManager; - if (!locationsInSameFile(Sources, ND->getLocStart(), ND->getRBraceLoc())) + if (!locationsInSameFile(Sources, ND->getBeginLoc(), ND->getRBraceLoc())) return; // Don't require closing comments for namespaces spanning less than certain // number of lines. - unsigned StartLine = Sources.getSpellingLineNumber(ND->getLocStart()); + unsigned StartLine = Sources.getSpellingLineNumber(ND->getBeginLoc()); unsigned EndLine = Sources.getSpellingLineNumber(ND->getRBraceLoc()); if (EndLine - StartLine + 1 <= ShortNamespaceLines) return; diff --git a/clang-tidy/readability/NonConstParameterCheck.cpp b/clang-tidy/readability/NonConstParameterCheck.cpp index bc6507c86..e33191cf9 100644 --- a/clang-tidy/readability/NonConstParameterCheck.cpp +++ b/clang-tidy/readability/NonConstParameterCheck.cpp @@ -146,7 +146,7 @@ void NonConstParameterCheck::diagnoseNonConstParameters() { unsigned Index = Par->getFunctionScopeIndex(); for (FunctionDecl *FnDecl : Function->redecls()) Fixes.push_back(FixItHint::CreateInsertion( - FnDecl->getParamDecl(Index)->getLocStart(), "const ")); + FnDecl->getParamDecl(Index)->getBeginLoc(), "const ")); diag(Par->getLocation(), "pointer parameter '%0' can be pointer to const") << Par->getName() << Fixes; diff --git a/clang-tidy/readability/RedundantDeclarationCheck.cpp b/clang-tidy/readability/RedundantDeclarationCheck.cpp index 1df3f0555..c5b6cc322 100644 --- a/clang-tidy/readability/RedundantDeclarationCheck.cpp +++ b/clang-tidy/readability/RedundantDeclarationCheck.cpp @@ -59,7 +59,7 @@ void RedundantDeclarationCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *VD = dyn_cast(D)) { // Is this a multivariable declaration? for (const auto Other : VD->getDeclContext()->decls()) { - if (Other != D && Other->getLocStart() == VD->getLocStart()) { + if (Other != D && Other->getBeginLoc() == VD->getBeginLoc()) { MultiVar = true; break; } diff --git a/clang-tidy/readability/RedundantSmartptrGetCheck.cpp b/clang-tidy/readability/RedundantSmartptrGetCheck.cpp index 1abd70bd0..b03b546fd 100644 --- a/clang-tidy/readability/RedundantSmartptrGetCheck.cpp +++ b/clang-tidy/readability/RedundantSmartptrGetCheck.cpp @@ -138,7 +138,7 @@ void RedundantSmartptrGetCheck::check(const MatchFinder::MatchResult &Result) { *Result.SourceManager, getLangOpts()); // Replace foo->get() with *foo, and foo.get() with foo. std::string Replacement = Twine(IsPtrToPtr ? "*" : "", SmartptrText).str(); - diag(GetCall->getLocStart(), "redundant get() call on smart pointer") + diag(GetCall->getBeginLoc(), "redundant get() call on smart pointer") << FixItHint::CreateReplacement(GetCall->getSourceRange(), Replacement); } diff --git a/clang-tidy/readability/RedundantStringCStrCheck.cpp b/clang-tidy/readability/RedundantStringCStrCheck.cpp index 21f4a8a5d..f0c378f5b 100644 --- a/clang-tidy/readability/RedundantStringCStrCheck.cpp +++ b/clang-tidy/readability/RedundantStringCStrCheck.cpp @@ -189,7 +189,7 @@ void RedundantStringCStrCheck::check(const MatchFinder::MatchResult &Result) { if (ArgText.empty()) return; - diag(Call->getLocStart(), "redundant call to %0") + diag(Call->getBeginLoc(), "redundant call to %0") << Member->getMemberDecl() << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText); } diff --git a/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/clang-tidy/readability/SimplifyBooleanExprCheck.cpp index ba8e5b42c..97243a9d1 100644 --- a/clang-tidy/readability/SimplifyBooleanExprCheck.cpp +++ b/clang-tidy/readability/SimplifyBooleanExprCheck.cpp @@ -62,7 +62,7 @@ const char SimplifyConditionalReturnDiagnostic[] = const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result, StringRef Id) { const auto *Literal = Result.Nodes.getNodeAs(Id); - return (Literal && Literal->getLocStart().isMacroID()) ? nullptr : Literal; + return (Literal && Literal->getBeginLoc().isMacroID()) ? nullptr : Literal; } internal::Matcher returnsBool(bool Value, StringRef Id = "ignored") { @@ -372,7 +372,7 @@ void SimplifyBooleanExprCheck::reportBinOp( else return; - if (Bool->getLocStart().isMacroID()) + if (Bool->getBeginLoc().isMacroID()) return; // FIXME: why do we need this? @@ -385,8 +385,8 @@ void SimplifyBooleanExprCheck::reportBinOp( const Expr *ReplaceWith, bool Negated) { std::string Replacement = replacementExpression(Result, Negated, ReplaceWith); - SourceRange Range(LHS->getLocStart(), RHS->getLocEnd()); - issueDiag(Result, Bool->getLocStart(), SimplifyOperatorDiagnostic, Range, + SourceRange Range(LHS->getBeginLoc(), RHS->getLocEnd()); + issueDiag(Result, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range, Replacement); }; @@ -577,7 +577,7 @@ void SimplifyBooleanExprCheck::replaceWithThenStatement( const MatchFinder::MatchResult &Result, const CXXBoolLiteralExpr *TrueConditionRemoved) { const auto *IfStatement = Result.Nodes.getNodeAs(IfStmtId); - issueDiag(Result, TrueConditionRemoved->getLocStart(), + issueDiag(Result, TrueConditionRemoved->getBeginLoc(), SimplifyConditionDiagnostic, IfStatement->getSourceRange(), getText(Result, *IfStatement->getThen())); } @@ -587,7 +587,7 @@ void SimplifyBooleanExprCheck::replaceWithElseStatement( const CXXBoolLiteralExpr *FalseConditionRemoved) { const auto *IfStatement = Result.Nodes.getNodeAs(IfStmtId); const Stmt *ElseStatement = IfStatement->getElse(); - issueDiag(Result, FalseConditionRemoved->getLocStart(), + issueDiag(Result, FalseConditionRemoved->getBeginLoc(), SimplifyConditionDiagnostic, IfStatement->getSourceRange(), ElseStatement ? getText(Result, *ElseStatement) : ""); } @@ -597,7 +597,7 @@ void SimplifyBooleanExprCheck::replaceWithCondition( bool Negated) { std::string Replacement = replacementExpression(Result, Negated, Ternary->getCond()); - issueDiag(Result, Ternary->getTrueExpr()->getLocStart(), + issueDiag(Result, Ternary->getTrueExpr()->getBeginLoc(), "redundant boolean literal in ternary expression result", Ternary->getSourceRange(), Replacement); } @@ -608,7 +608,7 @@ void SimplifyBooleanExprCheck::replaceWithReturnCondition( std::string Condition = replacementExpression(Result, Negated, If->getCond()); std::string Replacement = ("return " + Condition + Terminator).str(); SourceLocation Start = - Result.Nodes.getNodeAs(ThenLiteralId)->getLocStart(); + Result.Nodes.getNodeAs(ThenLiteralId)->getBeginLoc(); issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic, If->getSourceRange(), Replacement); } @@ -640,8 +640,8 @@ void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( std::string Replacement = "return " + replacementExpression(Result, Negated, Condition); issueDiag( - Result, Lit->getLocStart(), SimplifyConditionalReturnDiagnostic, - SourceRange(If->getLocStart(), Ret->getLocEnd()), Replacement); + Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic, + SourceRange(If->getBeginLoc(), Ret->getLocEnd()), Replacement); return; } @@ -665,7 +665,7 @@ void SimplifyBooleanExprCheck::replaceWithAssignment( std::string Replacement = (VariableName + " = " + Condition + Terminator).str(); SourceLocation Location = - Result.Nodes.getNodeAs(IfAssignLocId)->getLocStart(); + Result.Nodes.getNodeAs(IfAssignLocId)->getBeginLoc(); issueDiag(Result, Location, "redundant boolean literal in conditional assignment", Range, Replacement); diff --git a/clang-tidy/readability/SimplifySubscriptExprCheck.cpp b/clang-tidy/readability/SimplifySubscriptExprCheck.cpp index 28cc576dd..036489f30 100644 --- a/clang-tidy/readability/SimplifySubscriptExprCheck.cpp +++ b/clang-tidy/readability/SimplifySubscriptExprCheck.cpp @@ -60,7 +60,7 @@ void SimplifySubscriptExprCheck::check(const MatchFinder::MatchResult &Result) { "accessing an element of the container does not require a call to " "'data()'; did you mean to use 'operator[]'?"); if (Member->isArrow()) - DiagBuilder << FixItHint::CreateInsertion(Member->getLocStart(), "(*") + DiagBuilder << FixItHint::CreateInsertion(Member->getBeginLoc(), "(*") << FixItHint::CreateInsertion(Member->getOperatorLoc(), ")"); DiagBuilder << FixItHint::CreateRemoval( {Member->getOperatorLoc(), Call->getLocEnd()}); diff --git a/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp b/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp index b1365772b..92d787960 100644 --- a/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp +++ b/clang-tidy/readability/StaticAccessedThroughInstanceCheck.cpp @@ -51,7 +51,7 @@ void StaticAccessedThroughInstanceCheck::check( const auto *MemberExpression = Result.Nodes.getNodeAs("memberExpression"); - if (MemberExpression->getLocStart().isMacroID()) + if (MemberExpression->getBeginLoc().isMacroID()) return; const Expr *BaseExpr = MemberExpression->getBase(); @@ -71,7 +71,7 @@ void StaticAccessedThroughInstanceCheck::check( std::string BaseTypeName = BaseType.getAsString(PrintingPolicyWithSupressedTag); - SourceLocation MemberExprStartLoc = MemberExpression->getLocStart(); + SourceLocation MemberExprStartLoc = MemberExpression->getBeginLoc(); auto Diag = diag(MemberExprStartLoc, "static member accessed through instance"); diff --git a/clang-tidy/readability/StringCompareCheck.cpp b/clang-tidy/readability/StringCompareCheck.cpp index e75e80bb2..38ac43f2d 100644 --- a/clang-tidy/readability/StringCompareCheck.cpp +++ b/clang-tidy/readability/StringCompareCheck.cpp @@ -51,7 +51,7 @@ void StringCompareCheck::registerMatchers(MatchFinder *Finder) { void StringCompareCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *Matched = Result.Nodes.getNodeAs("match1")) { - diag(Matched->getLocStart(), CompareMessage); + diag(Matched->getBeginLoc(), CompareMessage); return; } @@ -63,10 +63,10 @@ void StringCompareCheck::check(const MatchFinder::MatchResult &Result) { const auto *Str2 = Result.Nodes.getNodeAs("str2"); const auto *Compare = Result.Nodes.getNodeAs("compare"); - auto Diag = diag(Matched->getLocStart(), CompareMessage); + auto Diag = diag(Matched->getBeginLoc(), CompareMessage); if (Str1->isArrow()) - Diag << FixItHint::CreateInsertion(Str1->getLocStart(), "*"); + Diag << FixItHint::CreateInsertion(Str1->getBeginLoc(), "*"); Diag << tooling::fixit::createReplacement(*Zero, *Str2, Ctx) << tooling::fixit::createReplacement(*Compare, *Str1->getBase(), diff --git a/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp b/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp index 3ad346cdd..4476f370e 100644 --- a/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp +++ b/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp @@ -42,7 +42,7 @@ void UniqueptrDeleteReleaseCheck::check( const auto *PtrExpr = Result.Nodes.getNodeAs("uptr"); const auto *DeleteExpr = Result.Nodes.getNodeAs("delete"); - if (PtrExpr->getLocStart().isMacroID()) + if (PtrExpr->getBeginLoc().isMacroID()) return; // Ignore dependent types. @@ -54,11 +54,11 @@ void UniqueptrDeleteReleaseCheck::check( SourceLocation AfterPtr = Lexer::getLocForEndOfToken( PtrExpr->getLocEnd(), 0, *Result.SourceManager, getLangOpts()); - diag(DeleteExpr->getLocStart(), + diag(DeleteExpr->getBeginLoc(), "prefer '= nullptr' to 'delete x.release()' to reset unique_ptr<> " "objects") << FixItHint::CreateRemoval(CharSourceRange::getCharRange( - DeleteExpr->getLocStart(), PtrExpr->getLocStart())) + DeleteExpr->getBeginLoc(), PtrExpr->getBeginLoc())) << FixItHint::CreateReplacement( CharSourceRange::getTokenRange(AfterPtr, DeleteExpr->getLocEnd()), " = nullptr"); diff --git a/clang-tidy/utils/ASTUtils.cpp b/clang-tidy/utils/ASTUtils.cpp index ab6077a74..5c6b98439 100644 --- a/clang-tidy/utils/ASTUtils.cpp +++ b/clang-tidy/utils/ASTUtils.cpp @@ -45,8 +45,8 @@ bool exprHasBitFlagWithSpelling(const Expr *Flags, const SourceManager &SM, StringRef FlagName) { // If the Flag is an integer constant, check it. if (isa(Flags)) { - if (!SM.isMacroBodyExpansion(Flags->getLocStart()) && - !SM.isMacroArgExpansion(Flags->getLocStart())) + if (!SM.isMacroBodyExpansion(Flags->getBeginLoc()) && + !SM.isMacroArgExpansion(Flags->getBeginLoc())) return false; // Get the macro name. diff --git a/clang-tidy/utils/NamespaceAliaser.cpp b/clang-tidy/utils/NamespaceAliaser.cpp index 1a5120deb..251823040 100644 --- a/clang-tidy/utils/NamespaceAliaser.cpp +++ b/clang-tidy/utils/NamespaceAliaser.cpp @@ -70,7 +70,7 @@ NamespaceAliaser::createAlias(ASTContext &Context, const Stmt &Statement, (llvm::Twine("\nnamespace ") + Abbreviation + " = " + Namespace + ";") .str(); SourceLocation Loc = - Lexer::getLocForEndOfToken(Function->getBody()->getLocStart(), 0, + Lexer::getLocForEndOfToken(Function->getBody()->getBeginLoc(), 0, SourceMgr, Context.getLangOpts()); AddedAliases[Function][Namespace.str()] = Abbreviation; return FixItHint::CreateInsertion(Loc, Declaration); diff --git a/clang-tidy/utils/UsingInserter.cpp b/clang-tidy/utils/UsingInserter.cpp index e7200c99c..e479d5916 100644 --- a/clang-tidy/utils/UsingInserter.cpp +++ b/clang-tidy/utils/UsingInserter.cpp @@ -41,7 +41,7 @@ Optional UsingInserter::createUsingDeclaration( return None; SourceLocation InsertLoc = Lexer::getLocForEndOfToken( - Function->getBody()->getLocStart(), 0, SourceMgr, Context.getLangOpts()); + Function->getBody()->getBeginLoc(), 0, SourceMgr, Context.getLangOpts()); // Only use using declarations in the main file, not in includes. if (SourceMgr.getFileID(InsertLoc) != SourceMgr.getMainFileID()) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 112a6fe20..90ebccb14 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -235,7 +235,7 @@ struct CompletionCandidate { // file e.g. the symbol is forward declared. auto &SM = SemaResult->Declaration->getASTContext().getSourceManager(); for (const Decl *RD : SemaResult->Declaration->redecls()) - if (SM.isInMainFile(SM.getExpansionLoc(RD->getLocStart()))) + if (SM.isInMainFile(SM.getExpansionLoc(RD->getBeginLoc()))) return llvm::None; } return IndexResult->Detail->IncludeHeader; diff --git a/clangd/CodeCompletionStrings.cpp b/clangd/CodeCompletionStrings.cpp index 88b37daf8..e601e0359 100644 --- a/clangd/CodeCompletionStrings.cpp +++ b/clangd/CodeCompletionStrings.cpp @@ -65,7 +65,7 @@ std::string getDocComment(const ASTContext &Ctx, // Sanity check that the comment does not come from the PCH. We choose to not // write them into PCH, because they are racy and slow to load. - assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getLocStart())); + assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc())); std::string Doc = RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); if (!looksLikeDocComment(Doc)) return ""; @@ -84,7 +84,7 @@ getParameterDocComment(const ASTContext &Ctx, return ""; // Sanity check that the comment does not come from the PCH. We choose to not // write them into PCH, because they are racy and slow to load. - assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getLocStart())); + assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc())); std::string Doc = RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); if (!looksLikeDocComment(Doc)) return ""; diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index 284428cc9..e95730ba7 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -540,7 +540,7 @@ class DeducedTypeVisitor : public RecursiveASTVisitor { //- auto& i = 1; bool VisitDeclaratorDecl(DeclaratorDecl *D) { if (!D->getTypeSourceInfo() || - D->getTypeSourceInfo()->getTypeLoc().getLocStart() != SearchedLocation) + D->getTypeSourceInfo()->getTypeLoc().getBeginLoc() != SearchedLocation) return true; auto DeclT = D->getType(); diff --git a/tool-template/ToolTemplate.cpp b/tool-template/ToolTemplate.cpp index 5345d77f0..66ec2e841 100644 --- a/tool-template/ToolTemplate.cpp +++ b/tool-template/ToolTemplate.cpp @@ -66,8 +66,8 @@ class ToolTemplateCallback : public MatchFinder::MatchCallback { auto *D = Result.Nodes.getNodeAs("decl"); assert(D); // Use AtomicChange to get a key. - if (D->getLocStart().isValid()) { - AtomicChange Change(*Result.SourceManager, D->getLocStart()); + if (D->getBeginLoc().isValid()) { + AtomicChange Change(*Result.SourceManager, D->getBeginLoc()); Context.reportResult(Change.getKey(), D->getQualifiedNameAsString()); } } diff --git a/unittests/clang-tidy/IncludeInserterTest.cpp b/unittests/clang-tidy/IncludeInserterTest.cpp index a5f5898bb..7a70f66a4 100644 --- a/unittests/clang-tidy/IncludeInserterTest.cpp +++ b/unittests/clang-tidy/IncludeInserterTest.cpp @@ -45,7 +45,7 @@ class IncludeInserterCheckBase : public ClangTidyCheck { } void check(const ast_matchers::MatchFinder::MatchResult &Result) override { - auto Diag = diag(Result.Nodes.getNodeAs("stmt")->getLocStart(), + auto Diag = diag(Result.Nodes.getNodeAs("stmt")->getBeginLoc(), "foo, bar"); for (StringRef header : HeadersToInclude()) { auto Fixit = Inserter->CreateIncludeInsertion( diff --git a/unittests/clang-tidy/NamespaceAliaserTest.cpp b/unittests/clang-tidy/NamespaceAliaserTest.cpp index 71286e297..e4f8ebce5 100644 --- a/unittests/clang-tidy/NamespaceAliaserTest.cpp +++ b/unittests/clang-tidy/NamespaceAliaserTest.cpp @@ -36,13 +36,11 @@ class InsertAliasCheck : public ClangTidyCheck { auto Hint = Aliaser->createAlias(*Result.Context, *Call, "::foo::bar", {"b", "some_alias"}); if (Hint.hasValue()) - diag(Call->getLocStart(), "Fix for testing") << Hint.getValue(); + diag(Call->getBeginLoc(), "Fix for testing") << Hint.getValue(); - diag(Call->getLocStart(), "insert call") - << FixItHint::CreateInsertion( - Call->getLocStart(), - Aliaser->getNamespaceName(*Result.Context, *Call, "::foo::bar") + - "::"); + diag(Call->getBeginLoc(), "insert call") << FixItHint::CreateInsertion( + Call->getBeginLoc(), + Aliaser->getNamespaceName(*Result.Context, *Call, "::foo::bar") + "::"); } private: diff --git a/unittests/clang-tidy/OverlappingReplacementsTest.cpp b/unittests/clang-tidy/OverlappingReplacementsTest.cpp index b7c4805c3..b501e7a22 100644 --- a/unittests/clang-tidy/OverlappingReplacementsTest.cpp +++ b/unittests/clang-tidy/OverlappingReplacementsTest.cpp @@ -33,8 +33,8 @@ class UseCharCheck : public ClangTidyCheck { } void check(const ast_matchers::MatchFinder::MatchResult &Result) override { auto *VD = Result.Nodes.getNodeAs(BoundDecl); - diag(VD->getLocStart(), "use char") << FixItHint::CreateReplacement( - CharSourceRange::getTokenRange(VD->getLocStart(), VD->getLocStart()), + diag(VD->getBeginLoc(), "use char") << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(VD->getBeginLoc(), VD->getBeginLoc()), "char"); } }; @@ -52,7 +52,7 @@ class IfFalseCheck : public ClangTidyCheck { auto *Cond = If->getCond(); SourceRange Range = Cond->getSourceRange(); if (auto *D = If->getConditionVariable()) { - Range = SourceRange(D->getLocStart(), D->getLocEnd()); + Range = SourceRange(D->getBeginLoc(), D->getLocEnd()); } diag(Range.getBegin(), "the cake is a lie") << FixItHint::CreateReplacement( CharSourceRange::getTokenRange(Range), "false"); diff --git a/unittests/clang-tidy/UsingInserterTest.cpp b/unittests/clang-tidy/UsingInserterTest.cpp index 9e465a0cb..16d251924 100644 --- a/unittests/clang-tidy/UsingInserterTest.cpp +++ b/unittests/clang-tidy/UsingInserterTest.cpp @@ -39,9 +39,9 @@ class InsertUsingCheck : public clang::tidy::ClangTidyCheck { Inserter->createUsingDeclaration(*Result.Context, *Call, "::foo::func"); if (Hint.hasValue()) - diag(Call->getLocStart(), "Fix for testing") << Hint.getValue(); + diag(Call->getBeginLoc(), "Fix for testing") << Hint.getValue(); - diag(Call->getLocStart(), "insert call") + diag(Call->getBeginLoc(), "insert call") << clang::FixItHint::CreateReplacement( Call->getCallee()->getSourceRange(), Inserter->getShortName(*Result.Context, *Call, "::foo::func")); From a32ea61ae09dc772fd7f688a89a0bd07c1bcc4f1 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Thu, 9 Aug 2018 22:43:02 +0000 Subject: [PATCH 020/686] Port getLocEnd -> getEndLoc Subscribers: nemanjai, ioeric, kbarton, cfe-commits Differential Revision: https://reviews.llvm.org/D50355 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339401 91177308-0d34-0410-b5e6-96231b3b80d8 --- change-namespace/ChangeNamespace.cpp | 4 ++-- clang-move/ClangMove.cpp | 6 +++--- clang-tidy/android/CloexecCheck.cpp | 2 +- clang-tidy/bugprone/ArgumentCommentCheck.cpp | 6 +++--- clang-tidy/bugprone/CopyConstructorInitCheck.cpp | 2 +- clang-tidy/bugprone/InaccurateEraseCheck.cpp | 2 +- clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp | 2 +- clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp | 2 +- clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp | 2 +- clang-tidy/bugprone/UnusedRaiiCheck.cpp | 2 +- clang-tidy/cppcoreguidelines/NoMallocCheck.cpp | 2 +- .../cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp | 2 +- clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp | 2 +- clang-tidy/fuchsia/DefaultArgumentsCheck.cpp | 2 +- clang-tidy/google/AvoidCStyleCastsCheck.cpp | 2 +- clang-tidy/google/ExplicitConstructorCheck.cpp | 2 +- clang-tidy/llvm/TwineLocalCheck.cpp | 2 +- clang-tidy/misc/RedundantExpressionCheck.cpp | 4 ++-- clang-tidy/misc/StaticAssertCheck.cpp | 2 +- clang-tidy/misc/UnusedAliasDeclsCheck.cpp | 2 +- clang-tidy/misc/UnusedParametersCheck.cpp | 4 ++-- clang-tidy/misc/UnusedUsingDeclsCheck.cpp | 2 +- clang-tidy/modernize/AvoidBindCheck.cpp | 2 +- clang-tidy/modernize/MakeSmartPtrCheck.cpp | 2 +- clang-tidy/modernize/PassByValueCheck.cpp | 2 +- clang-tidy/modernize/RedundantVoidArgCheck.cpp | 2 +- clang-tidy/modernize/UseEmplaceCheck.cpp | 2 +- clang-tidy/modernize/UseEqualsDeleteCheck.cpp | 2 +- clang-tidy/modernize/UseNullptrCheck.cpp | 2 +- clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp | 6 +++--- clang-tidy/performance/FasterStringFindCheck.cpp | 2 +- clang-tidy/performance/MoveConstArgCheck.cpp | 4 ++-- clang-tidy/readability/AvoidConstParamsInDecls.cpp | 4 ++-- clang-tidy/readability/BracesAroundStatementsCheck.cpp | 4 ++-- clang-tidy/readability/DeleteNullPointerCheck.cpp | 2 +- clang-tidy/readability/FunctionSizeCheck.cpp | 4 ++-- clang-tidy/readability/ImplicitBoolConversionCheck.cpp | 4 ++-- clang-tidy/readability/MisleadingIndentationCheck.cpp | 2 +- clang-tidy/readability/RedundantControlFlowCheck.cpp | 2 +- clang-tidy/readability/SimplifyBooleanExprCheck.cpp | 4 ++-- clang-tidy/readability/SimplifySubscriptExprCheck.cpp | 2 +- clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp | 4 ++-- unittests/clang-tidy/OverlappingReplacementsTest.cpp | 2 +- 43 files changed, 59 insertions(+), 59 deletions(-) diff --git a/change-namespace/ChangeNamespace.cpp b/change-namespace/ChangeNamespace.cpp index 85b6b1364..f941e33d8 100644 --- a/change-namespace/ChangeNamespace.cpp +++ b/change-namespace/ChangeNamespace.cpp @@ -710,7 +710,7 @@ void ChangeNamespaceTool::moveClassForwardDeclaration( const ast_matchers::MatchFinder::MatchResult &Result, const NamedDecl *FwdDecl) { SourceLocation Start = FwdDecl->getBeginLoc(); - SourceLocation End = FwdDecl->getLocEnd(); + SourceLocation End = FwdDecl->getEndLoc(); const SourceManager &SM = *Result.SourceManager; SourceLocation AfterSemi = Lexer::findLocationAfterToken( End, tok::semi, SM, Result.Context->getLangOpts(), @@ -911,7 +911,7 @@ void ChangeNamespaceTool::fixUsingShadowDecl( const ast_matchers::MatchFinder::MatchResult &Result, const UsingDecl *UsingDeclaration) { SourceLocation Start = UsingDeclaration->getBeginLoc(); - SourceLocation End = UsingDeclaration->getLocEnd(); + SourceLocation End = UsingDeclaration->getEndLoc(); if (Start.isInvalid() || End.isInvalid()) return; diff --git a/clang-move/ClangMove.cpp b/clang-move/ClangMove.cpp index 80ce8e619..eac6fcd0e 100644 --- a/clang-move/ClangMove.cpp +++ b/clang-move/ClangMove.cpp @@ -292,7 +292,7 @@ getLocForEndOfDecl(const clang::Decl *D, // If the expansion range is a character range, this is the location of // the first character past the end. Otherwise it's the location of the // first character in the final token in the range. - auto EndExpansionLoc = SM.getExpansionRange(D->getLocEnd()).getEnd(); + auto EndExpansionLoc = SM.getExpansionRange(D->getEndLoc()).getEnd(); std::pair LocInfo = SM.getDecomposedLoc(EndExpansionLoc); // Try to load the file buffer. bool InvalidTemp = false; @@ -327,8 +327,8 @@ getFullRange(const clang::Decl *D, getLocForEndOfDecl(D)); // Expand to comments that are associated with the Decl. if (const auto *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) { - if (SM.isBeforeInTranslationUnit(Full.getEnd(), Comment->getLocEnd())) - Full.setEnd(Comment->getLocEnd()); + if (SM.isBeforeInTranslationUnit(Full.getEnd(), Comment->getEndLoc())) + Full.setEnd(Comment->getEndLoc()); // FIXME: Don't delete a preceding comment, if there are no other entities // it could refer to. if (SM.isBeforeInTranslationUnit(Comment->getBeginLoc(), Full.getBegin())) diff --git a/clang-tidy/android/CloexecCheck.cpp b/clang-tidy/android/CloexecCheck.cpp index 686c749b8..4cc0f614d 100644 --- a/clang-tidy/android/CloexecCheck.cpp +++ b/clang-tidy/android/CloexecCheck.cpp @@ -64,7 +64,7 @@ void CloexecCheck::insertMacroFlag(const MatchFinder::MatchResult &Result, return; SourceLocation EndLoc = - Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getLocEnd()), 0, SM, + Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getEndLoc()), 0, SM, Result.Context->getLangOpts()); diag(EndLoc, "%0 should use %1 where possible") diff --git a/clang-tidy/bugprone/ArgumentCommentCheck.cpp b/clang-tidy/bugprone/ArgumentCommentCheck.cpp index 34d1712e2..068f2c57d 100644 --- a/clang-tidy/bugprone/ArgumentCommentCheck.cpp +++ b/clang-tidy/bugprone/ArgumentCommentCheck.cpp @@ -243,7 +243,7 @@ void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx, CharSourceRange BeforeArgument = makeFileCharRange(ArgBeginLoc, Args[I]->getBeginLoc()); - ArgBeginLoc = Args[I]->getLocEnd(); + ArgBeginLoc = Args[I]->getEndLoc(); std::vector> Comments; if (BeforeArgument.isValid()) { @@ -251,7 +251,7 @@ void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx, } else { // Fall back to parsing back from the start of the argument. CharSourceRange ArgsRange = makeFileCharRange( - Args[I]->getBeginLoc(), Args[NumArgs - 1]->getLocEnd()); + Args[I]->getBeginLoc(), Args[NumArgs - 1]->getEndLoc()); Comments = getCommentsBeforeLoc(Ctx, ArgsRange.getBegin()); } @@ -287,7 +287,7 @@ void ArgumentCommentCheck::check(const MatchFinder::MatchResult &Result) { if (!Callee) return; - checkCallArgs(Result.Context, Callee, Call->getCallee()->getLocEnd(), + checkCallArgs(Result.Context, Callee, Call->getCallee()->getEndLoc(), llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs())); } else { const auto *Construct = cast(E); diff --git a/clang-tidy/bugprone/CopyConstructorInitCheck.cpp b/clang-tidy/bugprone/CopyConstructorInitCheck.cpp index 170ce4525..f04882547 100644 --- a/clang-tidy/bugprone/CopyConstructorInitCheck.cpp +++ b/clang-tidy/bugprone/CopyConstructorInitCheck.cpp @@ -81,7 +81,7 @@ void CopyConstructorInitCheck::check(const MatchFinder::MatchResult &Result) { if (CtorInitIsWritten) { if (!ParamName.empty()) SafeFixIts.push_back( - FixItHint::CreateInsertion(CExpr->getLocEnd(), ParamName)); + FixItHint::CreateInsertion(CExpr->getEndLoc(), ParamName)); } else { if (Init->getSourceLocation().isMacroID() || Ctor->getLocation().isMacroID() || ShouldNotDoFixit) diff --git a/clang-tidy/bugprone/InaccurateEraseCheck.cpp b/clang-tidy/bugprone/InaccurateEraseCheck.cpp index 02d23c499..c1e65b58e 100644 --- a/clang-tidy/bugprone/InaccurateEraseCheck.cpp +++ b/clang-tidy/bugprone/InaccurateEraseCheck.cpp @@ -67,7 +67,7 @@ void InaccurateEraseCheck::check(const MatchFinder::MatchResult &Result) { CharSourceRange::getTokenRange(EndExpr->getSourceRange()), *Result.SourceManager, getLangOpts()); const SourceLocation EndLoc = Lexer::getLocForEndOfToken( - AlgCall->getLocEnd(), 0, *Result.SourceManager, getLangOpts()); + AlgCall->getEndLoc(), 0, *Result.SourceManager, getLangOpts()); Hint = FixItHint::CreateInsertion(EndLoc, ", " + ReplacementText); } diff --git a/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp b/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp index 9699250bb..f75a194fa 100644 --- a/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp +++ b/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp @@ -29,7 +29,7 @@ static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee, CharSourceRange CallRange = Lexer::makeFileCharRange(CharSourceRange::getTokenRange( - Callee->getBeginLoc(), Callee->getLocEnd()), + Callee->getBeginLoc(), Callee->getEndLoc()), SM, LangOpts); if (CallRange.isValid()) { diff --git a/clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp b/clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp index 1481cc380..f49a57095 100644 --- a/clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp +++ b/clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp @@ -62,7 +62,7 @@ void StringIntegerAssignmentCheck::check( } SourceLocation EndLoc = Lexer::getLocForEndOfToken( - Argument->getLocEnd(), 0, *Result.SourceManager, getLangOpts()); + Argument->getEndLoc(), 0, *Result.SourceManager, getLangOpts()); if (IsOneDigit) { Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L'" : "'") << FixItHint::CreateInsertion(EndLoc, "'"); diff --git a/clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp b/clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp index f4e1496af..ae6f2ee64 100644 --- a/clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp +++ b/clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp @@ -51,7 +51,7 @@ void SuspiciousSemicolonCheck::check(const MatchFinder::MatchResult &Result) { SM.getSpellingLineNumber(Token.getLocation()) != SemicolonLine) return; - SourceLocation LocEnd = Semicolon->getLocEnd(); + SourceLocation LocEnd = Semicolon->getEndLoc(); FileID FID = SM.getFileID(LocEnd); llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, LocEnd); Lexer Lexer(SM.getLocForStartOfFile(FID), Ctxt.getLangOpts(), diff --git a/clang-tidy/bugprone/UnusedRaiiCheck.cpp b/clang-tidy/bugprone/UnusedRaiiCheck.cpp index e58f16684..e9089b77c 100644 --- a/clang-tidy/bugprone/UnusedRaiiCheck.cpp +++ b/clang-tidy/bugprone/UnusedRaiiCheck.cpp @@ -84,7 +84,7 @@ void UnusedRaiiCheck::check(const MatchFinder::MatchResult &Result) { match(expr(hasDescendant(typeLoc().bind("t"))), *E, *Result.Context); const auto *TL = selectFirst("t", Matches); D << FixItHint::CreateInsertion( - Lexer::getLocForEndOfToken(TL->getLocEnd(), 0, *Result.SourceManager, + Lexer::getLocForEndOfToken(TL->getEndLoc(), 0, *Result.SourceManager, getLangOpts()), Replacement); } diff --git a/clang-tidy/cppcoreguidelines/NoMallocCheck.cpp b/clang-tidy/cppcoreguidelines/NoMallocCheck.cpp index cc3346816..f9cec51e6 100644 --- a/clang-tidy/cppcoreguidelines/NoMallocCheck.cpp +++ b/clang-tidy/cppcoreguidelines/NoMallocCheck.cpp @@ -74,7 +74,7 @@ void NoMallocCheck::check(const MatchFinder::MatchResult &Result) { assert(Call && "Unhandled binding in the Matcher"); diag(Call->getBeginLoc(), "do not manage memory manually; %0") - << Recommendation << SourceRange(Call->getBeginLoc(), Call->getLocEnd()); + << Recommendation << SourceRange(Call->getBeginLoc(), Call->getEndLoc()); } } // namespace cppcoreguidelines diff --git a/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp b/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp index a81496e1c..9fbb1219c 100644 --- a/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp +++ b/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp @@ -93,7 +93,7 @@ void ProBoundsConstantArrayIndexCheck::check( SourceRange(BaseRange.getEnd().getLocWithOffset(1), IndexRange.getBegin().getLocWithOffset(-1)), ", ") - << FixItHint::CreateReplacement(Matched->getLocEnd(), ")"); + << FixItHint::CreateReplacement(Matched->getEndLoc(), ")"); Optional Insertion = Inserter->CreateIncludeInsertion( Result.SourceManager->getMainFileID(), GslHeader, diff --git a/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp b/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp index 8a43a816d..bc2418d6a 100644 --- a/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp +++ b/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp @@ -81,7 +81,7 @@ void ProTypeCstyleCastCheck::check(const MatchFinder::MatchResult &Result) { if (!isa(SubExpr)) { CastText.push_back('('); diag_builder << FixItHint::CreateInsertion( - Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, + Lexer::getLocForEndOfToken(SubExpr->getEndLoc(), 0, *Result.SourceManager, getLangOpts()), ")"); } diff --git a/clang-tidy/fuchsia/DefaultArgumentsCheck.cpp b/clang-tidy/fuchsia/DefaultArgumentsCheck.cpp index f6c64ce13..e6a67e0ad 100644 --- a/clang-tidy/fuchsia/DefaultArgumentsCheck.cpp +++ b/clang-tidy/fuchsia/DefaultArgumentsCheck.cpp @@ -33,7 +33,7 @@ void DefaultArgumentsCheck::check(const MatchFinder::MatchResult &Result) { Result.Nodes.getNodeAs("decl")) { SourceRange DefaultArgRange = D->getDefaultArgRange(); - if (DefaultArgRange.getEnd() != D->getLocEnd()) { + if (DefaultArgRange.getEnd() != D->getEndLoc()) { return; } else if (DefaultArgRange.getBegin().isMacroID()) { diag(D->getBeginLoc(), diff --git a/clang-tidy/google/AvoidCStyleCastsCheck.cpp b/clang-tidy/google/AvoidCStyleCastsCheck.cpp index ffcfe11d5..83b54ad87 100644 --- a/clang-tidy/google/AvoidCStyleCastsCheck.cpp +++ b/clang-tidy/google/AvoidCStyleCastsCheck.cpp @@ -135,7 +135,7 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { if (!isa(SubExpr)) { CastText.push_back('('); Diag << FixItHint::CreateInsertion( - Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, SM, + Lexer::getLocForEndOfToken(SubExpr->getEndLoc(), 0, SM, getLangOpts()), ")"); } diff --git a/clang-tidy/google/ExplicitConstructorCheck.cpp b/clang-tidy/google/ExplicitConstructorCheck.cpp index a03fb56dc..778ce8909 100644 --- a/clang-tidy/google/ExplicitConstructorCheck.cpp +++ b/clang-tidy/google/ExplicitConstructorCheck.cpp @@ -118,7 +118,7 @@ void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) { }; SourceRange ExplicitTokenRange = FindToken(*Result.SourceManager, getLangOpts(), - Ctor->getOuterLocStart(), Ctor->getLocEnd(), isKWExplicit); + Ctor->getOuterLocStart(), Ctor->getEndLoc(), isKWExplicit); StringRef ConstructorDescription; if (Ctor->isMoveConstructor()) ConstructorDescription = "move"; diff --git a/clang-tidy/llvm/TwineLocalCheck.cpp b/clang-tidy/llvm/TwineLocalCheck.cpp index 915a51db6..744ddd9df 100644 --- a/clang-tidy/llvm/TwineLocalCheck.cpp +++ b/clang-tidy/llvm/TwineLocalCheck.cpp @@ -48,7 +48,7 @@ void TwineLocalCheck::check(const MatchFinder::MatchResult &Result) { if (VD->getType()->getCanonicalTypeUnqualified() == C->getType()->getCanonicalTypeUnqualified()) { SourceLocation EndLoc = Lexer::getLocForEndOfToken( - VD->getInit()->getLocEnd(), 0, *Result.SourceManager, getLangOpts()); + VD->getInit()->getEndLoc(), 0, *Result.SourceManager, getLangOpts()); Diag << FixItHint::CreateReplacement(TypeRange, "std::string") << FixItHint::CreateInsertion(VD->getInit()->getBeginLoc(), "(") << FixItHint::CreateInsertion(EndLoc, ").str()"); diff --git a/clang-tidy/misc/RedundantExpressionCheck.cpp b/clang-tidy/misc/RedundantExpressionCheck.cpp index 5f055d106..e9b0f4d0c 100644 --- a/clang-tidy/misc/RedundantExpressionCheck.cpp +++ b/clang-tidy/misc/RedundantExpressionCheck.cpp @@ -885,7 +885,7 @@ void RedundantExpressionCheck::checkBitwiseExpr( diag(Loc, "expression always evaluates to 0"); } else if (exprEvaluatesToBitwiseNegatedZero(Opcode, Value)) { SourceRange ConstExprRange(ConstExpr->getBeginLoc(), - ConstExpr->getLocEnd()); + ConstExpr->getEndLoc()); StringRef ConstExprText = Lexer::getSourceText( CharSourceRange::getTokenRange(ConstExprRange), *Result.SourceManager, Result.Context->getLangOpts()); @@ -893,7 +893,7 @@ void RedundantExpressionCheck::checkBitwiseExpr( diag(Loc, "expression always evaluates to '%0'") << ConstExprText; } else if (exprEvaluatesToSymbolic(Opcode, Value)) { - SourceRange SymExprRange(Sym->getBeginLoc(), Sym->getLocEnd()); + SourceRange SymExprRange(Sym->getBeginLoc(), Sym->getEndLoc()); StringRef ExprText = Lexer::getSourceText( CharSourceRange::getTokenRange(SymExprRange), *Result.SourceManager, diff --git a/clang-tidy/misc/StaticAssertCheck.cpp b/clang-tidy/misc/StaticAssertCheck.cpp index 1d0d88d24..583ed7add 100644 --- a/clang-tidy/misc/StaticAssertCheck.cpp +++ b/clang-tidy/misc/StaticAssertCheck.cpp @@ -129,7 +129,7 @@ void StaticAssertCheck::check(const MatchFinder::MatchResult &Result) { FixItHints.push_back(FixItHint::CreateRemoval( SourceRange(AssertExprRoot->getOperatorLoc()))); FixItHints.push_back(FixItHint::CreateRemoval( - SourceRange(AssertMSG->getBeginLoc(), AssertMSG->getLocEnd()))); + SourceRange(AssertMSG->getBeginLoc(), AssertMSG->getEndLoc()))); StaticAssertMSG = (Twine(", \"") + AssertMSG->getString() + "\"").str(); } diff --git a/clang-tidy/misc/UnusedAliasDeclsCheck.cpp b/clang-tidy/misc/UnusedAliasDeclsCheck.cpp index 3ef5b205f..4beb4320e 100644 --- a/clang-tidy/misc/UnusedAliasDeclsCheck.cpp +++ b/clang-tidy/misc/UnusedAliasDeclsCheck.cpp @@ -36,7 +36,7 @@ void UnusedAliasDeclsCheck::check(const MatchFinder::MatchResult &Result) { FoundDecls[AliasDecl] = CharSourceRange::getCharRange( AliasDecl->getBeginLoc(), Lexer::findLocationAfterToken( - AliasDecl->getLocEnd(), tok::semi, *Result.SourceManager, + AliasDecl->getEndLoc(), tok::semi, *Result.SourceManager, getLangOpts(), /*SkipTrailingWhitespaceAndNewLine=*/true)); return; diff --git a/clang-tidy/misc/UnusedParametersCheck.cpp b/clang-tidy/misc/UnusedParametersCheck.cpp index a5dae816f..a70a53ead 100644 --- a/clang-tidy/misc/UnusedParametersCheck.cpp +++ b/clang-tidy/misc/UnusedParametersCheck.cpp @@ -46,10 +46,10 @@ static CharSourceRange removeNode(const MatchFinder::MatchResult &Result, if (PrevNode) return CharSourceRange::getTokenRange( - Lexer::getLocForEndOfToken(PrevNode->getLocEnd(), 0, + Lexer::getLocForEndOfToken(PrevNode->getEndLoc(), 0, *Result.SourceManager, Result.Context->getLangOpts()), - Node->getLocEnd()); + Node->getEndLoc()); return CharSourceRange::getTokenRange(Node->getSourceRange()); } diff --git a/clang-tidy/misc/UnusedUsingDeclsCheck.cpp b/clang-tidy/misc/UnusedUsingDeclsCheck.cpp index da2704711..48009b5a5 100644 --- a/clang-tidy/misc/UnusedUsingDeclsCheck.cpp +++ b/clang-tidy/misc/UnusedUsingDeclsCheck.cpp @@ -70,7 +70,7 @@ void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) { Context.UsingDeclRange = CharSourceRange::getCharRange( Using->getBeginLoc(), Lexer::findLocationAfterToken( - Using->getLocEnd(), tok::semi, *Result.SourceManager, getLangOpts(), + Using->getEndLoc(), tok::semi, *Result.SourceManager, getLangOpts(), /*SkipTrailingWhitespaceAndNewLine=*/true)); for (const auto *UsingShadow : Using->shadows()) { const auto *TargetDecl = UsingShadow->getTargetDecl()->getCanonicalDecl(); diff --git a/clang-tidy/modernize/AvoidBindCheck.cpp b/clang-tidy/modernize/AvoidBindCheck.cpp index e0bdd86df..bd477026f 100644 --- a/clang-tidy/modernize/AvoidBindCheck.cpp +++ b/clang-tidy/modernize/AvoidBindCheck.cpp @@ -59,7 +59,7 @@ buildBindArguments(const MatchFinder::MatchResult &Result, const CallExpr *C) { } B.Tokens = Lexer::getSourceText( - CharSourceRange::getTokenRange(E->getBeginLoc(), E->getLocEnd()), + CharSourceRange::getTokenRange(E->getBeginLoc(), E->getEndLoc()), *Result.SourceManager, Result.Context->getLangOpts()); SmallVector Matches; diff --git a/clang-tidy/modernize/MakeSmartPtrCheck.cpp b/clang-tidy/modernize/MakeSmartPtrCheck.cpp index 6f3cb1ff2..97580fb78 100644 --- a/clang-tidy/modernize/MakeSmartPtrCheck.cpp +++ b/clang-tidy/modernize/MakeSmartPtrCheck.cpp @@ -201,7 +201,7 @@ void MakeSmartPtrCheck::checkReset(SourceManager &SM, SourceLocation ResetCallStart = Reset->getExprLoc(); SourceLocation ExprStart = Expr->getBeginLoc(); SourceLocation ExprEnd = - Lexer::getLocForEndOfToken(Expr->getLocEnd(), 0, SM, getLangOpts()); + Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM, getLangOpts()); bool InMacro = ExprStart.isMacroID(); diff --git a/clang-tidy/modernize/PassByValueCheck.cpp b/clang-tidy/modernize/PassByValueCheck.cpp index c4f10a3a3..f54c57542 100644 --- a/clang-tidy/modernize/PassByValueCheck.cpp +++ b/clang-tidy/modernize/PassByValueCheck.cpp @@ -207,7 +207,7 @@ void PassByValueCheck::check(const MatchFinder::MatchResult &Result) { TypeLoc ValueTL = RefTL.getPointeeLoc(); auto TypeRange = CharSourceRange::getTokenRange(ParmDecl->getBeginLoc(), - ParamTL.getLocEnd()); + ParamTL.getEndLoc()); std::string ValueStr = Lexer::getSourceText(CharSourceRange::getTokenRange( ValueTL.getSourceRange()), SM, getLangOpts()) diff --git a/clang-tidy/modernize/RedundantVoidArgCheck.cpp b/clang-tidy/modernize/RedundantVoidArgCheck.cpp index a68033e5a..f0d1fdb86 100644 --- a/clang-tidy/modernize/RedundantVoidArgCheck.cpp +++ b/clang-tidy/modernize/RedundantVoidArgCheck.cpp @@ -105,7 +105,7 @@ void RedundantVoidArgCheck::processFunctionDecl( const Stmt *Body = Function->getBody(); SourceLocation Start = Function->getBeginLoc(); SourceLocation End = - Body ? Body->getBeginLoc().getLocWithOffset(-1) : Function->getLocEnd(); + Body ? Body->getBeginLoc().getLocWithOffset(-1) : Function->getEndLoc(); removeVoidArgumentTokens(Result, SourceRange(Start, End), "function definition"); } else { diff --git a/clang-tidy/modernize/UseEmplaceCheck.cpp b/clang-tidy/modernize/UseEmplaceCheck.cpp index 4d5d80146..b6c142d17 100644 --- a/clang-tidy/modernize/UseEmplaceCheck.cpp +++ b/clang-tidy/modernize/UseEmplaceCheck.cpp @@ -141,7 +141,7 @@ void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) { Diag << FixItHint::CreateReplacement(FunctionNameSourceRange, EmplacePrefix); const SourceRange CallParensRange = - MakeCall ? SourceRange(MakeCall->getCallee()->getLocEnd(), + MakeCall ? SourceRange(MakeCall->getCallee()->getEndLoc(), MakeCall->getRParenLoc()) : CtorCall->getParenOrBraceRange(); diff --git a/clang-tidy/modernize/UseEqualsDeleteCheck.cpp b/clang-tidy/modernize/UseEqualsDeleteCheck.cpp index 18190b19e..f5adb13f5 100644 --- a/clang-tidy/modernize/UseEqualsDeleteCheck.cpp +++ b/clang-tidy/modernize/UseEqualsDeleteCheck.cpp @@ -55,7 +55,7 @@ void UseEqualsDeleteCheck::check(const MatchFinder::MatchResult &Result) { if (const auto *Func = Result.Nodes.getNodeAs(SpecialFunction)) { SourceLocation EndLoc = Lexer::getLocForEndOfToken( - Func->getLocEnd(), 0, *Result.SourceManager, getLangOpts()); + Func->getEndLoc(), 0, *Result.SourceManager, getLangOpts()); // FIXME: Improve FixItHint to make the method public. diag(Func->getLocation(), diff --git a/clang-tidy/modernize/UseNullptrCheck.cpp b/clang-tidy/modernize/UseNullptrCheck.cpp index 5f5ee71cf..3bf09c1f8 100644 --- a/clang-tidy/modernize/UseNullptrCheck.cpp +++ b/clang-tidy/modernize/UseNullptrCheck.cpp @@ -215,7 +215,7 @@ class CastSequenceVisitor : public RecursiveASTVisitor { } SourceLocation StartLoc = FirstSubExpr->getBeginLoc(); - SourceLocation EndLoc = FirstSubExpr->getLocEnd(); + SourceLocation EndLoc = FirstSubExpr->getEndLoc(); // If the location comes from a macro arg expansion, *all* uses of that // arg must be checked to result in NullTo(Member)Pointer casts. diff --git a/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp b/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp index 8a659b7c0..0367b1393 100644 --- a/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp +++ b/clang-tidy/modernize/UseUncaughtExceptionsCheck.cpp @@ -58,14 +58,14 @@ void UseUncaughtExceptionsCheck::check(const MatchFinder::MatchResult &Result) { if (C) { BeginLoc = C->getBeginLoc(); - EndLoc = C->getLocEnd(); + EndLoc = C->getEndLoc(); } else if (const auto *E = Result.Nodes.getNodeAs("call_expr")) { BeginLoc = E->getBeginLoc(); - EndLoc = E->getLocEnd(); + EndLoc = E->getEndLoc(); } else if (const auto *D = Result.Nodes.getNodeAs("decl_ref_expr")) { BeginLoc = D->getBeginLoc(); - EndLoc = D->getLocEnd(); + EndLoc = D->getEndLoc(); WarnOnly = true; } else { const auto *U = Result.Nodes.getNodeAs("using_decl"); diff --git a/clang-tidy/performance/FasterStringFindCheck.cpp b/clang-tidy/performance/FasterStringFindCheck.cpp index 71e5e4084..0c3d249fd 100644 --- a/clang-tidy/performance/FasterStringFindCheck.cpp +++ b/clang-tidy/performance/FasterStringFindCheck.cpp @@ -96,7 +96,7 @@ void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) { << FindFunc << FixItHint::CreateReplacement( CharSourceRange::getTokenRange(Literal->getBeginLoc(), - Literal->getLocEnd()), + Literal->getEndLoc()), *Replacement); } diff --git a/clang-tidy/performance/MoveConstArgCheck.cpp b/clang-tidy/performance/MoveConstArgCheck.cpp index c9c7be19f..c64769f6c 100644 --- a/clang-tidy/performance/MoveConstArgCheck.cpp +++ b/clang-tidy/performance/MoveConstArgCheck.cpp @@ -26,8 +26,8 @@ static void ReplaceCallWithArg(const CallExpr *Call, DiagnosticBuilder &Diag, CharSourceRange::getCharRange(Call->getBeginLoc(), Arg->getBeginLoc()), SM, LangOpts); CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange( - CharSourceRange::getCharRange(Call->getLocEnd(), - Call->getLocEnd().getLocWithOffset(1)), + CharSourceRange::getCharRange(Call->getEndLoc(), + Call->getEndLoc().getLocWithOffset(1)), SM, LangOpts); if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) { diff --git a/clang-tidy/readability/AvoidConstParamsInDecls.cpp b/clang-tidy/readability/AvoidConstParamsInDecls.cpp index 8b2218817..51fc48950 100644 --- a/clang-tidy/readability/AvoidConstParamsInDecls.cpp +++ b/clang-tidy/readability/AvoidConstParamsInDecls.cpp @@ -23,7 +23,7 @@ namespace { SourceRange getTypeRange(const ParmVarDecl &Param) { if (Param.getIdentifier() != nullptr) return SourceRange(Param.getBeginLoc(), - Param.getLocEnd().getLocWithOffset(-1)); + Param.getEndLoc().getLocWithOffset(-1)); return Param.getSourceRange(); } @@ -97,7 +97,7 @@ void AvoidConstParamsInDecls::check(const MatchFinder::MatchResult &Result) { Diag << Param; } - if (Param->getBeginLoc().isMacroID() != Param->getLocEnd().isMacroID()) { + if (Param->getBeginLoc().isMacroID() != Param->getEndLoc().isMacroID()) { // Do not offer a suggestion if the part of the variable declaration comes // from a macro. return; diff --git a/clang-tidy/readability/BracesAroundStatementsCheck.cpp b/clang-tidy/readability/BracesAroundStatementsCheck.cpp index a29e68a36..5f5294c2c 100644 --- a/clang-tidy/readability/BracesAroundStatementsCheck.cpp +++ b/clang-tidy/readability/BracesAroundStatementsCheck.cpp @@ -177,9 +177,9 @@ BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S, if (S->getBeginLoc().isMacroID()) return SourceLocation(); - SourceLocation CondEndLoc = S->getCond()->getLocEnd(); + SourceLocation CondEndLoc = S->getCond()->getEndLoc(); if (const DeclStmt *CondVar = S->getConditionVariableDeclStmt()) - CondEndLoc = CondVar->getLocEnd(); + CondEndLoc = CondVar->getEndLoc(); if (!CondEndLoc.isValid()) { return SourceLocation(); diff --git a/clang-tidy/readability/DeleteNullPointerCheck.cpp b/clang-tidy/readability/DeleteNullPointerCheck.cpp index 4d3188c43..02b9bbe8f 100644 --- a/clang-tidy/readability/DeleteNullPointerCheck.cpp +++ b/clang-tidy/readability/DeleteNullPointerCheck.cpp @@ -63,7 +63,7 @@ void DeleteNullPointerCheck::check(const MatchFinder::MatchResult &Result) { Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( IfWithDelete->getBeginLoc(), - Lexer::getLocForEndOfToken(IfWithDelete->getCond()->getLocEnd(), 0, + Lexer::getLocForEndOfToken(IfWithDelete->getCond()->getEndLoc(), 0, *Result.SourceManager, Result.Context->getLangOpts()))); if (Compound) { diff --git a/clang-tidy/readability/FunctionSizeCheck.cpp b/clang-tidy/readability/FunctionSizeCheck.cpp index b1ceea11d..9547afb89 100644 --- a/clang-tidy/readability/FunctionSizeCheck.cpp +++ b/clang-tidy/readability/FunctionSizeCheck.cpp @@ -162,8 +162,8 @@ void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) { // Count the lines including whitespace and comments. Really simple. if (const Stmt *Body = Func->getBody()) { SourceManager *SM = Result.SourceManager; - if (SM->isWrittenInSameFile(Body->getBeginLoc(), Body->getLocEnd())) { - FI.Lines = SM->getSpellingLineNumber(Body->getLocEnd()) - + if (SM->isWrittenInSameFile(Body->getBeginLoc(), Body->getEndLoc())) { + FI.Lines = SM->getSpellingLineNumber(Body->getEndLoc()) - SM->getSpellingLineNumber(Body->getBeginLoc()); } } diff --git a/clang-tidy/readability/ImplicitBoolConversionCheck.cpp b/clang-tidy/readability/ImplicitBoolConversionCheck.cpp index 2a449ff6e..e88c14360 100644 --- a/clang-tidy/readability/ImplicitBoolConversionCheck.cpp +++ b/clang-tidy/readability/ImplicitBoolConversionCheck.cpp @@ -145,7 +145,7 @@ void fixGenericExprCastToBool(DiagnosticBuilder &Diag, } SourceLocation EndLoc = Lexer::getLocForEndOfToken( - Cast->getLocEnd(), 0, Context.getSourceManager(), Context.getLangOpts()); + Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts()); Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion); } @@ -189,7 +189,7 @@ void fixGenericExprCastFromBool(DiagnosticBuilder &Diag, if (NeedParens) { SourceLocation EndLoc = Lexer::getLocForEndOfToken( - Cast->getLocEnd(), 0, Context.getSourceManager(), + Cast->getEndLoc(), 0, Context.getSourceManager(), Context.getLangOpts()); Diag << FixItHint::CreateInsertion(EndLoc, ")"); diff --git a/clang-tidy/readability/MisleadingIndentationCheck.cpp b/clang-tidy/readability/MisleadingIndentationCheck.cpp index 5abace500..9791531fd 100644 --- a/clang-tidy/readability/MisleadingIndentationCheck.cpp +++ b/clang-tidy/readability/MisleadingIndentationCheck.cpp @@ -40,7 +40,7 @@ void MisleadingIndentationCheck::danglingElseCheck(const SourceManager &SM, if (IfLoc.isMacroID() || ElseLoc.isMacroID()) return; - if (SM.getExpansionLineNumber(If->getThen()->getLocEnd()) == + if (SM.getExpansionLineNumber(If->getThen()->getEndLoc()) == SM.getExpansionLineNumber(ElseLoc)) return; diff --git a/clang-tidy/readability/RedundantControlFlowCheck.cpp b/clang-tidy/readability/RedundantControlFlowCheck.cpp index 0788c42e7..d5898ed9f 100644 --- a/clang-tidy/readability/RedundantControlFlowCheck.cpp +++ b/clang-tidy/readability/RedundantControlFlowCheck.cpp @@ -81,7 +81,7 @@ void RedundantControlFlowCheck::issueDiagnostic( SourceLocation Start; if (Previous != Block->body_rend()) Start = Lexer::findLocationAfterToken( - dyn_cast(*Previous)->getLocEnd(), tok::semi, SM, getLangOpts(), + dyn_cast(*Previous)->getEndLoc(), tok::semi, SM, getLangOpts(), /*SkipTrailingWhitespaceAndNewLine=*/true); if (!Start.isValid()) Start = StmtRange.getBegin(); diff --git a/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/clang-tidy/readability/SimplifyBooleanExprCheck.cpp index 97243a9d1..9420c6c3b 100644 --- a/clang-tidy/readability/SimplifyBooleanExprCheck.cpp +++ b/clang-tidy/readability/SimplifyBooleanExprCheck.cpp @@ -385,7 +385,7 @@ void SimplifyBooleanExprCheck::reportBinOp( const Expr *ReplaceWith, bool Negated) { std::string Replacement = replacementExpression(Result, Negated, ReplaceWith); - SourceRange Range(LHS->getBeginLoc(), RHS->getLocEnd()); + SourceRange Range(LHS->getBeginLoc(), RHS->getEndLoc()); issueDiag(Result, Bool->getBeginLoc(), SimplifyOperatorDiagnostic, Range, Replacement); }; @@ -641,7 +641,7 @@ void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition( "return " + replacementExpression(Result, Negated, Condition); issueDiag( Result, Lit->getBeginLoc(), SimplifyConditionalReturnDiagnostic, - SourceRange(If->getBeginLoc(), Ret->getLocEnd()), Replacement); + SourceRange(If->getBeginLoc(), Ret->getEndLoc()), Replacement); return; } diff --git a/clang-tidy/readability/SimplifySubscriptExprCheck.cpp b/clang-tidy/readability/SimplifySubscriptExprCheck.cpp index 036489f30..f4c306e9c 100644 --- a/clang-tidy/readability/SimplifySubscriptExprCheck.cpp +++ b/clang-tidy/readability/SimplifySubscriptExprCheck.cpp @@ -63,7 +63,7 @@ void SimplifySubscriptExprCheck::check(const MatchFinder::MatchResult &Result) { DiagBuilder << FixItHint::CreateInsertion(Member->getBeginLoc(), "(*") << FixItHint::CreateInsertion(Member->getOperatorLoc(), ")"); DiagBuilder << FixItHint::CreateRemoval( - {Member->getOperatorLoc(), Call->getLocEnd()}); + {Member->getOperatorLoc(), Call->getEndLoc()}); } void SimplifySubscriptExprCheck::storeOptions( diff --git a/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp b/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp index 4476f370e..bb2c69017 100644 --- a/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp +++ b/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp @@ -52,7 +52,7 @@ void UniqueptrDeleteReleaseCheck::check( return; SourceLocation AfterPtr = Lexer::getLocForEndOfToken( - PtrExpr->getLocEnd(), 0, *Result.SourceManager, getLangOpts()); + PtrExpr->getEndLoc(), 0, *Result.SourceManager, getLangOpts()); diag(DeleteExpr->getBeginLoc(), "prefer '= nullptr' to 'delete x.release()' to reset unique_ptr<> " @@ -60,7 +60,7 @@ void UniqueptrDeleteReleaseCheck::check( << FixItHint::CreateRemoval(CharSourceRange::getCharRange( DeleteExpr->getBeginLoc(), PtrExpr->getBeginLoc())) << FixItHint::CreateReplacement( - CharSourceRange::getTokenRange(AfterPtr, DeleteExpr->getLocEnd()), + CharSourceRange::getTokenRange(AfterPtr, DeleteExpr->getEndLoc()), " = nullptr"); } diff --git a/unittests/clang-tidy/OverlappingReplacementsTest.cpp b/unittests/clang-tidy/OverlappingReplacementsTest.cpp index b501e7a22..87213b175 100644 --- a/unittests/clang-tidy/OverlappingReplacementsTest.cpp +++ b/unittests/clang-tidy/OverlappingReplacementsTest.cpp @@ -52,7 +52,7 @@ class IfFalseCheck : public ClangTidyCheck { auto *Cond = If->getCond(); SourceRange Range = Cond->getSourceRange(); if (auto *D = If->getConditionVariable()) { - Range = SourceRange(D->getBeginLoc(), D->getLocEnd()); + Range = SourceRange(D->getBeginLoc(), D->getEndLoc()); } diag(Range.getBegin(), "the cake is a lie") << FixItHint::CreateReplacement( CharSourceRange::getTokenRange(Range), "false"); From 46c3aff567f62842fe6253061630d1075ffc0e94 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Fri, 10 Aug 2018 08:25:51 +0000 Subject: [PATCH 021/686] [clang-tidy] Omit cases where loop variable is not used in loop body in performance-for-range-copy check. Summary: The upstream change r336737 make the check too smart to fix the case where loop variable could be used as `const auto&`. But for the case below, changing to `const auto _` will introduce an unused complier warning. ``` for (auto _ : state) { // no references for _. } ``` This patch omit this case, and it is safe to do it as the case is very rare. Reviewers: ilya-biryukov, alexfh Subscribers: xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D50447 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339415 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/performance/ForRangeCopyCheck.cpp | 31 +++++++++++++------ .../clang-tidy/performance-for-range-copy.cpp | 5 +++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/clang-tidy/performance/ForRangeCopyCheck.cpp b/clang-tidy/performance/ForRangeCopyCheck.cpp index c8c906382..867d95d8e 100644 --- a/clang-tidy/performance/ForRangeCopyCheck.cpp +++ b/clang-tidy/performance/ForRangeCopyCheck.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "ForRangeCopyCheck.h" +#include "../utils/DeclRefExprUtils.h" #include "../utils/ExprMutationAnalyzer.h" #include "../utils/FixItHintUtils.h" #include "../utils/TypeTraits.h" @@ -79,15 +80,27 @@ bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced( utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context); if (LoopVar.getType().isConstQualified() || !Expensive || !*Expensive) return false; - if (utils::ExprMutationAnalyzer(ForRange.getBody(), &Context) - .isMutated(&LoopVar)) - return false; - diag(LoopVar.getLocation(), - "loop variable is copied but only used as const reference; consider " - "making it a const reference") - << utils::fixit::changeVarDeclToConst(LoopVar) - << utils::fixit::changeVarDeclToReference(LoopVar, Context); - return true; + // We omit the case where the loop variable is not used in the loop body. E.g. + // + // for (auto _ : benchmark_state) { + // } + // + // Because the fix (changing to `const auto &`) will introduce an unused + // compiler warning which can't be suppressed. + // Since this case is very rare, it is safe to ignore it. + if (!utils::ExprMutationAnalyzer(ForRange.getBody(), &Context) + .isMutated(&LoopVar) && + !utils::decl_ref_expr::allDeclRefExprs(LoopVar, *ForRange.getBody(), + Context) + .empty()) { + diag(LoopVar.getLocation(), + "loop variable is copied but only used as const reference; consider " + "making it a const reference") + << utils::fixit::changeVarDeclToConst(LoopVar) + << utils::fixit::changeVarDeclToReference(LoopVar, Context); + return true; + } + return false; } } // namespace performance diff --git a/test/clang-tidy/performance-for-range-copy.cpp b/test/clang-tidy/performance-for-range-copy.cpp index 1c77996c6..8a43ac0c2 100644 --- a/test/clang-tidy/performance-for-range-copy.cpp +++ b/test/clang-tidy/performance-for-range-copy.cpp @@ -260,3 +260,8 @@ void PositiveConstNonMemberOperatorInvoked() { bool result = ConstOperatorInvokee != Mutable(); } } + +void IgnoreLoopVariableNotUsedInLoopBody() { + for (auto _ : View>()) { + } +} From 997b4bc0bc264dbc8ee2b62e1e703fcb9d6d3514 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Fri, 10 Aug 2018 08:34:16 +0000 Subject: [PATCH 022/686] [clangd] Fix a "-Wdangling-else" compiler warning in the test. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339416 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/CodeCompleteTests.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 5fd17d0a2..350d47d9b 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1393,8 +1393,9 @@ TEST(CompletionTest, FixItForArrowToDot) { ReplacementEdit.newText = "."; for (const auto &C : Results.Completions) { EXPECT_TRUE(C.FixIts.size() == 1u || C.Name == "AuxFunction"); - if (!C.FixIts.empty()) + if (!C.FixIts.empty()) { EXPECT_THAT(C.FixIts, ElementsAre(ReplacementEdit)); + } } } From 274dfdca1ac5cf189cd3695aa3d61c480dbec623 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Fri, 10 Aug 2018 11:50:44 +0000 Subject: [PATCH 023/686] [clangd] Allow consuming limited number of items This patch modifies `consume` function to allow retrieval of limited number of symbols. This is the "cheap" implementation of top-level limiting iterator. In the future we would like to have a complete limit iterator implementation to insert it into the query subtrees, but in the meantime this version would be enough for a fully-functional proof-of-concept Dex implementation. Reviewers: ioeric, ilya-biryukov Reviewed by: ioeric Differential Revision: https://reviews.llvm.org/D50500 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339426 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.cpp | 5 +++-- clangd/index/dex/Iterator.h | 7 ++++--- unittests/clangd/DexIndexTests.cpp | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index 25107f92c..84d442e9d 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -218,9 +218,10 @@ class OrIterator : public Iterator { } // end namespace -std::vector consume(Iterator &It) { +std::vector consume(Iterator &It, size_t Limit) { std::vector Result; - for (; !It.reachedEnd(); It.advance()) + for (size_t Retrieved = 0; !It.reachedEnd() && Retrieved < Limit; + It.advance(), ++Retrieved) Result.push_back(It.peek()); return Result; } diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index f6270f12f..5e13b17af 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -101,9 +101,10 @@ class Iterator { virtual llvm::raw_ostream &dump(llvm::raw_ostream &OS) const = 0; }; -/// Exhausts given iterator and returns all processed DocIDs. The result -/// contains sorted DocumentIDs. -std::vector consume(Iterator &It); +/// Advances the iterator until it is either exhausted or the number of +/// requested items is reached. The result contains sorted DocumentIDs. +std::vector consume(Iterator &It, + size_t Limit = std::numeric_limits::max()); /// Returns a document iterator over given PostingList. std::unique_ptr create(PostingListRef Documents); diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index 906d62abb..d5db97ce1 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -240,6 +240,27 @@ TEST(DexIndexIterators, StringRepresentation) { "(& (& [1, 3, 5, 8, 9] [1, 5, 7, 9]) (| [0, 5] [0, 1, 5] []))"); } +TEST(DexIndexIterators, Limit) { + const PostingList L0 = {4, 7, 8, 20, 42, 100}; + const PostingList L1 = {1, 3, 5, 8, 9}; + const PostingList L2 = {1, 5, 7, 9}; + const PostingList L3 = {0, 5}; + const PostingList L4 = {0, 1, 5}; + const PostingList L5; + + auto DocIterator = create(L0); + EXPECT_THAT(consume(*DocIterator, 42), ElementsAre(4, 7, 8, 20, 42, 100)); + + DocIterator = create(L0); + EXPECT_THAT(consume(*DocIterator), ElementsAre(4, 7, 8, 20, 42, 100)); + + DocIterator = create(L0); + EXPECT_THAT(consume(*DocIterator, 3), ElementsAre(4, 7, 8)); + + DocIterator = create(L0); + EXPECT_THAT(consume(*DocIterator, 0), ElementsAre()); +} + testing::Matcher> trigramsAre(std::initializer_list Trigrams) { std::vector Tokens; From 7654135f0cbd155c285fd2a37d87e27e4fff3071 Mon Sep 17 00:00:00 2001 From: Andi-Bogdan Postelnicu Date: Fri, 10 Aug 2018 11:50:47 +0000 Subject: [PATCH 024/686] [clang-tidy] run-clang-tidy.py - add synchronisation to the output Differential Revision: https://reviews.llvm.org/D49851 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339427 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/tool/run-clang-tidy.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/clang-tidy/tool/run-clang-tidy.py b/clang-tidy/tool/run-clang-tidy.py index ce46c0ed3..3852ba2c0 100755 --- a/clang-tidy/tool/run-clang-tidy.py +++ b/clang-tidy/tool/run-clang-tidy.py @@ -153,7 +153,7 @@ def apply_fixes(args, tmpdir): subprocess.call(invocation) -def run_tidy(args, tmpdir, build_path, queue, failed_files): +def run_tidy(args, tmpdir, build_path, queue, lock, failed_files): """Takes filenames out of queue and runs clang-tidy on them.""" while True: name = queue.get() @@ -161,10 +161,15 @@ def run_tidy(args, tmpdir, build_path, queue, failed_files): tmpdir, build_path, args.header_filter, args.extra_arg, args.extra_arg_before, args.quiet, args.config) - sys.stdout.write(' '.join(invocation) + '\n') - return_code = subprocess.call(invocation) - if return_code != 0: + + proc = subprocess.Popen(invocation, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output, err = proc.communicate() + if proc.returncode != 0: failed_files.append(name) + with lock: + sys.stdout.write(' '.join(invocation) + '\n' + output + '\n') + if err > 0: + sys.stderr.write(err + '\n') queue.task_done() @@ -263,9 +268,10 @@ def main(): task_queue = queue.Queue(max_task) # List of files with a non-zero return code. failed_files = [] + lock = threading.Lock() for _ in range(max_task): t = threading.Thread(target=run_tidy, - args=(args, tmpdir, build_path, task_queue, failed_files)) + args=(args, tmpdir, build_path, task_queue, lock, failed_files)) t.daemon = True t.start() From 166628a67c37fe0ee4f18478e15208404c365cd2 Mon Sep 17 00:00:00 2001 From: Alexander Kornienko Date: Fri, 10 Aug 2018 13:59:33 +0000 Subject: [PATCH 025/686] [clang-tidy: modernize] modernize-redundant-void-arg crashes when a function body is in a macro Fixes https://bugs.llvm.org/show_bug.cgi?id=28406 Patch by IdrissRio. Differential revision: https://reviews.llvm.org/D49800 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339433 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../modernize/RedundantVoidArgCheck.cpp | 11 +++-- .../modernize-redundant-void-arg.cpp | 43 +++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/clang-tidy/modernize/RedundantVoidArgCheck.cpp b/clang-tidy/modernize/RedundantVoidArgCheck.cpp index f0d1fdb86..51dc5a2cc 100644 --- a/clang-tidy/modernize/RedundantVoidArgCheck.cpp +++ b/clang-tidy/modernize/RedundantVoidArgCheck.cpp @@ -149,6 +149,8 @@ void RedundantVoidArgCheck::removeVoidArgumentTokens( ProtoToken.getRawIdentifier() == "void") { State = SawVoid; VoidToken = ProtoToken; + } else if (ProtoToken.is(tok::TokenKind::l_paren)) { + State = SawLeftParen; } else { State = NothingYet; } @@ -235,10 +237,11 @@ void RedundantVoidArgCheck::processLambdaExpr( const MatchFinder::MatchResult &Result, const LambdaExpr *Lambda) { if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 && Lambda->hasExplicitParameters()) { - SourceLocation Begin = - Lambda->getIntroducerRange().getEnd().getLocWithOffset(1); - SourceLocation End = Lambda->getBody()->getBeginLoc().getLocWithOffset(-1); - removeVoidArgumentTokens(Result, SourceRange(Begin, End), + SourceManager *SM = Result.SourceManager; + TypeLoc TL = Lambda->getLambdaClass()->getLambdaTypeInfo()->getTypeLoc(); + removeVoidArgumentTokens(Result, + {SM->getSpellingLoc(TL.getBeginLoc()), + SM->getSpellingLoc(TL.getEndLoc())}, "lambda expression"); } } diff --git a/test/clang-tidy/modernize-redundant-void-arg.cpp b/test/clang-tidy/modernize-redundant-void-arg.cpp index ea4b75f64..750e5c1b2 100644 --- a/test/clang-tidy/modernize-redundant-void-arg.cpp +++ b/test/clang-tidy/modernize-redundant-void-arg.cpp @@ -445,3 +445,46 @@ struct DefinitionWithNoBody { // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: {{.*}} in function definition // CHECK-FIXES: DefinitionWithNoBody() = delete; }; + + + +#define BODY {} +#define LAMBDA1 [](void){} +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] +// CHECK-FIXES: LAMBDA1 [](){} + +#define LAMBDA2 [](void)BODY +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] +// CHECK-FIXES: LAMBDA2 []()BODY + +#define LAMBDA3(captures, args, body) captures args body +#define WRAP(...) __VA_ARGS__ + +#define LAMBDA4 (void)LAMBDA3([],(void),BODY) +// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] +// CHECK-FIXES: LAMBDA4 (void)LAMBDA3([],(),BODY) + +#define LAMBDA5 []() -> void (*)(void) {return BODY;} +// CHECK-MESSAGES: :[[@LINE-1]]:34: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] +// CHECK-FIXES: LAMBDA5 []() -> void (*)() {return BODY;} +void lambda_expression_with_macro_test(){ + (void)LAMBDA1; + (void)LAMBDA2; + (void)LAMBDA3([], (void), BODY); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] + // CHECK-FIXES: (void)LAMBDA3([], (), BODY); + + LAMBDA4; + LAMBDA5; + WRAP((void)WRAP(WRAP(LAMBDA3(WRAP([]), WRAP((void)), WRAP(BODY))))); + // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] + // CHECK-FIXES: WRAP((void)WRAP(WRAP(LAMBDA3(WRAP([]), WRAP(()), WRAP(BODY))))); + + (void)WRAP([](void) {}); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] + // CHECK-FIXES: (void)WRAP([]() {}); + + [](void) BODY; + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] + // CHECK-FIXES: []() BODY; +} From 3a14fe2e0f0205514b734309b4832e9a8fcde332 Mon Sep 17 00:00:00 2001 From: Roman Lebedev Date: Fri, 10 Aug 2018 15:05:46 +0000 Subject: [PATCH 026/686] [clang-tidy] check_clang_tidy.py: support CHECK-NOTES prefix Summary: Currently, there is two configured prefixes: `CHECK-FIXES` and `CHECK-MESSAGES` `CHECK-MESSAGES` checks that there are no test output lines with `warning:|error:`, which are not explicitly handled in lit tests. However there does not seem to be a nice way to enforce for all the `note:` to be checked. This was useful for me when developing D36836. Reviewers: alexfh, klimek, aaron.ballman, hokein Reviewed By: alexfh, aaron.ballman Subscribers: JonasToth, JDevlieghere, xazax.hun, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D36892 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339437 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/clang-tidy/index.rst | 3 +- test/clang-tidy/check_clang_tidy.py | 20 ++++++- test/clang-tidy/hicpp-exception-baseclass.cpp | 53 ++++++++++--------- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/docs/clang-tidy/index.rst b/docs/clang-tidy/index.rst index ec6c24ff5..0ed350c7b 100644 --- a/docs/clang-tidy/index.rst +++ b/docs/clang-tidy/index.rst @@ -650,7 +650,8 @@ clang-tidy tests. An additional check enabled by ``check_clang_tidy.py`` ensures that if `CHECK-MESSAGES:` is used in a file then every warning or error -must have an associated CHECK in that file. +must have an associated CHECK in that file. Or, you can use ``CHECK-NOTES:`` +instead, if you want to **also** ensure that all the notes are checked. To use the ``check_clang_tidy.py`` script, put a .cpp file with the appropriate ``RUN`` line in the ``test/clang-tidy`` directory. Use diff --git a/test/clang-tidy/check_clang_tidy.py b/test/clang-tidy/check_clang_tidy.py index bb2c5a260..dadb84d57 100755 --- a/test/clang-tidy/check_clang_tidy.py +++ b/test/clang-tidy/check_clang_tidy.py @@ -78,6 +78,7 @@ def main(): file_check_suffix = ('-' + args.check_suffix) if args.check_suffix else '' check_fixes_prefix = 'CHECK-FIXES' + file_check_suffix check_messages_prefix = 'CHECK-MESSAGES' + file_check_suffix + check_notes_prefix = 'CHECK-NOTES' + file_check_suffix # Tests should not rely on STL being available, and instead provide mock # implementations of relevant APIs. @@ -91,9 +92,11 @@ def main(): has_check_fixes = check_fixes_prefix in input_text has_check_messages = check_messages_prefix in input_text + has_check_notes = check_notes_prefix in input_text - if not has_check_fixes and not has_check_messages: - sys.exit('Neither %s nor %s found in the input' % (check_fixes_prefix, check_messages_prefix) ) + if not has_check_fixes and not has_check_messages and not has_check_notes: + sys.exit('%s, %s or %s not found in the input' % (check_fixes_prefix, + check_messages_prefix, check_notes_prefix) ) # Remove the contents of the CHECK lines to avoid CHECKs matching on # themselves. We need to keep the comments to preserve line numbers while @@ -156,5 +159,18 @@ def main(): print('FileCheck failed:\n' + e.output.decode()) raise + if has_check_notes: + notes_file = temp_file_name + '.notes' + write_file(notes_file, clang_tidy_output) + try: + subprocess.check_output( + ['FileCheck', '-input-file=' + notes_file, input_file_name, + '-check-prefix=' + check_notes_prefix, + '-implicit-check-not={{note|warning|error}}:'], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print('FileCheck failed:\n' + e.output.decode()) + raise + if __name__ == '__main__': main() diff --git a/test/clang-tidy/hicpp-exception-baseclass.cpp b/test/clang-tidy/hicpp-exception-baseclass.cpp index 9a0638b3a..fdf8093b7 100644 --- a/test/clang-tidy/hicpp-exception-baseclass.cpp +++ b/test/clang-tidy/hicpp-exception-baseclass.cpp @@ -20,28 +20,28 @@ class really_creative : public non_derived_exception, private std::exception {}; void problematic() { try { throw int(42); - // CHECK-MESSAGES: [[@LINE-1]]:11: warning: throwing an exception whose type 'int' is not derived from 'std::exception' + // CHECK-NOTES: [[@LINE-1]]:11: warning: throwing an exception whose type 'int' is not derived from 'std::exception' } catch (int e) { } throw int(42); - // CHECK-MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' try { throw 12; - // CHECK-MESSAGES: [[@LINE-1]]:11: warning: throwing an exception whose type 'int' is not derived from 'std::exception' + // CHECK-NOTES: [[@LINE-1]]:11: warning: throwing an exception whose type 'int' is not derived from 'std::exception' } catch (...) { throw; // Ok, even if the type is not known, conforming code can never rethrow a non-std::exception object. } try { throw non_derived_exception(); - // CHECK-MESSAGES: [[@LINE-1]]:11: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' - // CHECK-MESSAGES: 9:1: note: type defined here + // CHECK-NOTES: [[@LINE-1]]:11: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' + // CHECK-NOTES: 9:1: note: type defined here } catch (non_derived_exception &e) { } throw non_derived_exception(); - // CHECK-MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' - // CHECK-MESSAGES: 9:1: note: type defined here + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' + // CHECK-NOTES: 9:1: note: type defined here // FIXME: More complicated kinds of inheritance should be checked later, but there is // currently no way use ASTMatchers for this kind of task. @@ -100,15 +100,15 @@ void allowed_throws() { // Templated function that throws exception based on template type template void ThrowException() { throw T(); } -// CHECK-MESSAGES: [[@LINE-1]]:31: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' -// CHECK-MESSAGES: 120:1: note: type defined here -// CHECK-MESSAGES: [[@LINE-3]]:31: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' -// CHECK-MESSAGES: 120:1: note: type defined here -// CHECK-MESSAGES: [[@LINE-5]]:31: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' -// CHECK-MESSAGES: 123:1: note: type defined here -// CHECK-MESSAGES: [[@LINE-7]]:31: warning: throwing an exception whose type 'int' is not derived from 'std::exception' -// CHECK-MESSAGES: [[@LINE-8]]:31: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' -// CHECK-MESSAGES: 9:1: note: type defined here +// CHECK-NOTES: [[@LINE-1]]:31: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' +// CHECK-NOTES: 120:1: note: type defined here +// CHECK-NOTES: [[@LINE-3]]:31: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' +// CHECK-NOTES: 120:1: note: type defined here +// CHECK-NOTES: [[@LINE-5]]:31: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' +// CHECK-NOTES: 123:1: note: type defined here +// CHECK-NOTES: [[@LINE-7]]:31: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +// CHECK-NOTES: [[@LINE-8]]:31: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' +// CHECK-NOTES: 9:1: note: type defined here #define THROW_EXCEPTION(CLASS) ThrowException() #define THROW_BAD_EXCEPTION throw int(42); #define THROW_GOOD_EXCEPTION throw std::exception(); @@ -134,8 +134,8 @@ void generic_exceptions() { THROW_EXCEPTION(deep_hierarchy); // Ok THROW_BAD_EXCEPTION; - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'int' is not derived from 'std::exception' - // CHECK-MESSAGES: [[@LINE-25]]:35: note: expanded from macro 'THROW_BAD_EXCEPTION' + // CHECK-NOTES: [[@LINE-1]]:3: warning: throwing an exception whose type 'int' is not derived from 'std::exception' + // CHECK-NOTES: [[@LINE-25]]:35: note: expanded from macro 'THROW_BAD_EXCEPTION' THROW_GOOD_EXCEPTION; THROW_DERIVED_EXCEPTION; @@ -143,16 +143,19 @@ void generic_exceptions() { THROW_EXCEPTION(generic_exception); // Ok throw bad_generic_exception(); - // CHECK-MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' + // CHECK-NOTES: 120:1: note: type defined here throw bad_generic_exception(); - // CHECK-MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' + // CHECK-NOTES: 120:1: note: type defined here THROW_EXCEPTION(bad_generic_exception); // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' THROW_EXCEPTION(bad_generic_exception); // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' throw exotic_exception(); - // CHECK-MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' + // CHECK-NOTES: 123:1: note: type defined here THROW_EXCEPTION(exotic_exception); // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' @@ -168,12 +171,12 @@ using UsingGood = deep_hierarchy; void typedefed() { throw TypedefedBad(); - // CHECK-MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'TypedefedBad' (aka 'int') is not derived from 'std::exception' - // CHECK-MESSAGES: 164:1: note: type defined here + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'TypedefedBad' (aka 'int') is not derived from 'std::exception' + // CHECK-NOTES: 167:1: note: type defined here throw TypedefedGood(); // Ok throw UsingBad(); - // CHECK-MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'UsingBad' (aka 'int') is not derived from 'std::exception' - // CHECK-MESSAGES: 166:1: note: type defined here + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'UsingBad' (aka 'int') is not derived from 'std::exception' + // CHECK-NOTES: 169:1: note: type defined here throw UsingGood(); // Ok } From db1904c58b91cfebea7d2cefc29290ade1581eb2 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Fri, 10 Aug 2018 17:25:07 +0000 Subject: [PATCH 027/686] [clangd] extend the publishDiagnostics response to send back fixits to the client if the client supports this extension This commit extends the 'textDocument/publishDiagnostics' notification sent from Clangd to the client. When it's enabled, Clangd sends out the fixits associated with the appropriate diagnostic in the body of the 'publishDiagnostics' notification. The client can enable this extension by setting 'clangdFixSupport' to true in the textDocument capabilities during initialization. Differential Revision: https://reviews.llvm.org/D50415 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339454 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 18 +++++++++++++++++- clangd/ClangdLSPServer.h | 2 ++ clangd/Diagnostics.h | 6 ++++++ clangd/Protocol.cpp | 10 ++++++++++ clangd/Protocol.h | 15 +++++++++++++++ 5 files changed, 50 insertions(+), 1 deletion(-) diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index 99353e6c5..0d3023be4 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -82,6 +82,8 @@ void ClangdLSPServer::onInitialize(InitializeParams &Params) { CCOpts.EnableSnippets = Params.capabilities.textDocument.completion.completionItem.snippetSupport; + DiagOpts.EmbedFixesInDiagnostics = + Params.capabilities.textDocument.publishDiagnostics.clangdFixSupport; if (Params.capabilities.workspace && Params.capabilities.workspace->symbol && Params.capabilities.workspace->symbol->symbolKind) { @@ -486,11 +488,25 @@ void ClangdLSPServer::onDiagnosticsReady(PathRef File, DiagnosticToReplacementMap LocalFixIts; // Temporary storage for (auto &Diag : Diagnostics) { toLSPDiags(Diag, [&](clangd::Diagnostic Diag, llvm::ArrayRef Fixes) { - DiagnosticsJSON.push_back(json::Object{ + json::Object LSPDiag({ {"range", Diag.range}, {"severity", Diag.severity}, {"message", Diag.message}, }); + // LSP extension: embed the fixes in the diagnostic. + if (DiagOpts.EmbedFixesInDiagnostics && !Fixes.empty()) { + json::Array ClangdFixes; + for (const auto &Fix : Fixes) { + WorkspaceEdit WE; + URIForFile URI{File}; + WE.changes = {{URI.uri(), std::vector(Fix.Edits.begin(), + Fix.Edits.end())}}; + ClangdFixes.push_back( + json::Object{{"edit", toJSON(WE)}, {"title", Fix.Message}}); + } + LSPDiag["clangd_fixes"] = std::move(ClangdFixes); + } + DiagnosticsJSON.push_back(std::move(LSPDiag)); auto &FixItsForDiagnostic = LocalFixIts[Diag]; std::copy(Fixes.begin(), Fixes.end(), diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h index 3850e4a97..e4cbe5001 100644 --- a/clangd/ClangdLSPServer.h +++ b/clangd/ClangdLSPServer.h @@ -155,6 +155,8 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { RealFileSystemProvider FSProvider; /// Options used for code completion clangd::CodeCompleteOptions CCOpts; + /// Options used for diagnostics. + ClangdDiagnosticOptions DiagOpts; /// The supported kinds of the client. SymbolKindBitset SupportedSymbolKinds; diff --git a/clangd/Diagnostics.h b/clangd/Diagnostics.h index b43f04ae5..a08b9fe04 100644 --- a/clangd/Diagnostics.h +++ b/clangd/Diagnostics.h @@ -23,6 +23,12 @@ namespace clang { namespace clangd { +struct ClangdDiagnosticOptions { + /// If true, Clangd uses an LSP extension to embed the fixes with the + /// diagnostics that are sent to the client. + bool EmbedFixesInDiagnostics = false; +}; + /// Contains basic information about a diagnostic. struct DiagBase { std::string Message; diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp index 202fbfb08..698bb0489 100644 --- a/clangd/Protocol.cpp +++ b/clangd/Protocol.cpp @@ -178,6 +178,15 @@ bool fromJSON(const json::Value &Params, CompletionClientCapabilities &R) { return true; } +bool fromJSON(const llvm::json::Value &Params, + PublishDiagnosticsClientCapabilities &R) { + json::ObjectMapper O(Params); + if (!O) + return false; + O.map("clangdFixSupport", R.clangdFixSupport); + return true; +} + bool fromJSON(const json::Value &E, SymbolKind &Out) { if (auto T = E.getAsInteger()) { if (*T < static_cast(SymbolKind::File) || @@ -240,6 +249,7 @@ bool fromJSON(const json::Value &Params, TextDocumentClientCapabilities &R) { if (!O) return false; O.map("completion", R.completion); + O.map("publishDiagnostics", R.publishDiagnostics); return true; } diff --git a/clangd/Protocol.h b/clangd/Protocol.h index b0d810e87..f533c97f4 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -247,6 +247,18 @@ struct CompletionClientCapabilities { }; bool fromJSON(const llvm::json::Value &, CompletionClientCapabilities &); +struct PublishDiagnosticsClientCapabilities { + // Whether the client accepts diagnostics with related information. + // NOTE: not used by clangd at the moment. + // bool relatedInformation; + + /// Whether the client accepts diagnostics with fixes attached using the + /// "clangd_fixes" extension. + bool clangdFixSupport = false; +}; +bool fromJSON(const llvm::json::Value &, + PublishDiagnosticsClientCapabilities &); + /// A symbol kind. enum class SymbolKind { File = 1, @@ -313,6 +325,9 @@ bool fromJSON(const llvm::json::Value &, WorkspaceClientCapabilities &); struct TextDocumentClientCapabilities { /// Capabilities specific to the `textDocument/completion` CompletionClientCapabilities completion; + + /// Capabilities specific to the 'textDocument/publishDiagnostics' + PublishDiagnosticsClientCapabilities publishDiagnostics; }; bool fromJSON(const llvm::json::Value &, TextDocumentClientCapabilities &); From c86f5b0c50fc8ae862da0894283633e86a65d308 Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Fri, 10 Aug 2018 22:27:53 +0000 Subject: [PATCH 028/686] [clangd] Avoid duplicates in findDefinitions response Summary: When compile_commands.json contains some source files expressed as relative paths, we can get duplicate responses to findDefinitions. The responses only differ by the URI, which are different versions of the same file: "result": [ { ... "uri": "file:///home/emaisin/src/ls-interact/cpp-test/build/../src/first.h" }, { ... "uri": "file:///home/emaisin/src/ls-interact/cpp-test/src/first.h" } ] In getAbsoluteFilePath, we try to obtain the realpath of the FileEntry by calling tryGetRealPathName. However, this can fail and return an empty string. It may be bug a bug in clang, but in any case we should fall back to computing it ourselves if it happens. I changed getAbsoluteFilePath so that if tryGetRealPathName succeeds, we return right away (a real path is always absolute). Otherwise, we try to build an absolute path, as we did before, but we also call VFS->getRealPath to make sure to get the canonical path (e.g. without any ".." in it). Reviewers: malaperle Subscribers: hokein, ilya-biryukov, ioeric, MaskRay, jkorous, cfe-commits Differential Revision: https://reviews.llvm.org/D48687 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339483 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/FindSymbols.cpp | 2 +- clangd/SourceCode.cpp | 34 ++++++++++++++++------ clangd/SourceCode.h | 15 +++++++--- clangd/XRefs.cpp | 4 +-- unittests/clangd/TestFS.cpp | 26 +++++++++++++---- unittests/clangd/TestFS.h | 15 +++++++--- unittests/clangd/XRefsTests.cpp | 50 +++++++++++++++++++++++++++++---- 7 files changed, 114 insertions(+), 32 deletions(-) diff --git a/clangd/FindSymbols.cpp b/clangd/FindSymbols.cpp index c1ee67897..cc7f084f2 100644 --- a/clangd/FindSymbols.cpp +++ b/clangd/FindSymbols.cpp @@ -193,7 +193,7 @@ class DocumentSymbolsConsumer : public index::IndexDataConsumer { const FileEntry *F = SM.getFileEntryForID(SM.getMainFileID()); if (!F) return; - auto FilePath = getAbsoluteFilePath(F, SM); + auto FilePath = getRealPath(F, SM); if (FilePath) MainFileUri = URIForFile(*FilePath); } diff --git a/clangd/SourceCode.cpp b/clangd/SourceCode.cpp index 88ec2c956..931ad3c0f 100644 --- a/clangd/SourceCode.cpp +++ b/clangd/SourceCode.cpp @@ -185,18 +185,34 @@ std::vector replacementsToEdits(StringRef Code, return Edits; } -llvm::Optional -getAbsoluteFilePath(const FileEntry *F, const SourceManager &SourceMgr) { - SmallString<64> FilePath = F->tryGetRealPathName(); - if (FilePath.empty()) - FilePath = F->getName(); - if (!llvm::sys::path::is_absolute(FilePath)) { - if (!SourceMgr.getFileManager().makeAbsolutePath(FilePath)) { - log("Could not turn relative path to absolute: {0}", FilePath); +llvm::Optional getRealPath(const FileEntry *F, + const SourceManager &SourceMgr) { + // Ideally, we get the real path from the FileEntry object. + SmallString<128> FilePath = F->tryGetRealPathName(); + if (!FilePath.empty()) { + return FilePath.str().str(); + } + + // Otherwise, we try to compute ourselves. + vlog("FileEntry for {0} did not contain the real path.", F->getName()); + + llvm::SmallString<128> Path = F->getName(); + + if (!llvm::sys::path::is_absolute(Path)) { + if (!SourceMgr.getFileManager().makeAbsolutePath(Path)) { + log("Could not turn relative path to absolute: {0}", Path); return llvm::None; } } - return FilePath.str().str(); + + llvm::SmallString<128> RealPath; + if (SourceMgr.getFileManager().getVirtualFileSystem()->getRealPath( + Path, RealPath)) { + log("Could not compute real path: {0}", Path); + return Path.str().str(); + } + + return RealPath.str().str(); } TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, diff --git a/clangd/SourceCode.h b/clangd/SourceCode.h index cb0c67287..e633a1570 100644 --- a/clangd/SourceCode.h +++ b/clangd/SourceCode.h @@ -62,13 +62,20 @@ TextEdit replacementToEdit(StringRef Code, const tooling::Replacement &R); std::vector replacementsToEdits(StringRef Code, const tooling::Replacements &Repls); -/// Get the absolute file path of a given file entry. -llvm::Optional getAbsoluteFilePath(const FileEntry *F, - const SourceManager &SourceMgr); - TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, const LangOptions &L); +/// Get the real/canonical path of \p F. This means: +/// +/// - Absolute path +/// - Symlinks resolved +/// - No "." or ".." component +/// - No duplicate or trailing directory separator +/// +/// This function should be used when sending paths to clients, so that paths +/// are normalized as much as possible. +llvm::Optional getRealPath(const FileEntry *F, + const SourceManager &SourceMgr); } // namespace clangd } // namespace clang #endif diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index e95730ba7..f5e2b1e12 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -192,7 +192,7 @@ makeLocation(ParsedAST &AST, const SourceRange &ValSourceRange) { Range R = {Begin, End}; Location L; - auto FilePath = getAbsoluteFilePath(F, SourceMgr); + auto FilePath = getRealPath(F, SourceMgr); if (!FilePath) { log("failed to get path!"); return llvm::None; @@ -286,7 +286,7 @@ std::vector findDefinitions(ParsedAST &AST, Position Pos, std::string HintPath; const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); - if (auto Path = getAbsoluteFilePath(FE, SourceMgr)) + if (auto Path = getRealPath(FE, SourceMgr)) HintPath = *Path; // Query the index and populate the empty slot. Index->lookup( diff --git a/unittests/clangd/TestFS.cpp b/unittests/clangd/TestFS.cpp index b3081d643..d3112b922 100644 --- a/unittests/clangd/TestFS.cpp +++ b/unittests/clangd/TestFS.cpp @@ -32,8 +32,10 @@ buildTestFS(llvm::StringMap const &Files, return MemFS; } -MockCompilationDatabase::MockCompilationDatabase(bool UseRelPaths) - : ExtraClangFlags({"-ffreestanding"}), UseRelPaths(UseRelPaths) { +MockCompilationDatabase::MockCompilationDatabase(StringRef Directory, + StringRef RelPathPrefix) + : ExtraClangFlags({"-ffreestanding"}), Directory(Directory), + RelPathPrefix(RelPathPrefix) { // -ffreestanding avoids implicit stdc-predef.h. } @@ -42,12 +44,24 @@ MockCompilationDatabase::getCompileCommand(PathRef File) const { if (ExtraClangFlags.empty()) return None; - auto CommandLine = ExtraClangFlags; auto FileName = sys::path::filename(File); + + // Build the compile command. + auto CommandLine = ExtraClangFlags; CommandLine.insert(CommandLine.begin(), "clang"); - CommandLine.insert(CommandLine.end(), UseRelPaths ? FileName : File); - return {tooling::CompileCommand(sys::path::parent_path(File), FileName, - std::move(CommandLine), "")}; + if (RelPathPrefix.empty()) { + // Use the absolute path in the compile command. + CommandLine.push_back(File); + } else { + // Build a relative path using RelPathPrefix. + SmallString<32> RelativeFilePath(RelPathPrefix); + llvm::sys::path::append(RelativeFilePath, FileName); + CommandLine.push_back(RelativeFilePath.str()); + } + + return {tooling::CompileCommand( + Directory != StringRef() ? Directory : sys::path::parent_path(File), + FileName, std::move(CommandLine), "")}; } const char *testRoot() { diff --git a/unittests/clangd/TestFS.h b/unittests/clangd/TestFS.h index 9c2a15e63..a0efb755d 100644 --- a/unittests/clangd/TestFS.h +++ b/unittests/clangd/TestFS.h @@ -40,15 +40,22 @@ class MockFSProvider : public FileSystemProvider { // A Compilation database that returns a fixed set of compile flags. class MockCompilationDatabase : public GlobalCompilationDatabase { public: - /// When \p UseRelPaths is true, uses relative paths in compile commands. - /// When \p UseRelPaths is false, uses absoulte paths. - MockCompilationDatabase(bool UseRelPaths = false); + /// If \p Directory is not empty, use that as the Directory field of the + /// CompileCommand. + /// + /// If \p RelPathPrefix is not empty, use that as a prefix in front of the + /// source file name, instead of using an absolute path. + MockCompilationDatabase(StringRef Directory = StringRef(), + StringRef RelPathPrefix = StringRef()); llvm::Optional getCompileCommand(PathRef File) const override; std::vector ExtraClangFlags; - const bool UseRelPaths; + +private: + StringRef Directory; + StringRef RelPathPrefix; }; // Returns an absolute (fake) test directory for this OS. diff --git a/unittests/clangd/XRefsTests.cpp b/unittests/clangd/XRefsTests.cpp index 3383dd543..b08af32df 100644 --- a/unittests/clangd/XRefsTests.cpp +++ b/unittests/clangd/XRefsTests.cpp @@ -312,27 +312,65 @@ TEST(GoToDefinition, All) { } TEST(GoToDefinition, RelPathsInCompileCommand) { + // The source is in "/clangd-test/src". + // We build in "/clangd-test/build". + Annotations SourceAnnotations(R"cpp( +#include "header_in_preamble.h" int [[foo]]; -int baz = f^oo; +#include "header_not_in_preamble.h" +int baz = f$p1^oo + bar_pre$p2^amble + bar_not_pre$p3^amble; +)cpp"); + + Annotations HeaderInPreambleAnnotations(R"cpp( +int [[bar_preamble]]; +)cpp"); + + Annotations HeaderNotInPreambleAnnotations(R"cpp( +int [[bar_not_preamble]]; )cpp"); + // Make the compilation paths appear as ../src/foo.cpp in the compile + // commands. + SmallString<32> RelPathPrefix(".."); + llvm::sys::path::append(RelPathPrefix, "src"); + std::string BuildDir = testPath("build"); + MockCompilationDatabase CDB(BuildDir, RelPathPrefix); + IgnoreDiagnostics DiagConsumer; - MockCompilationDatabase CDB(/*UseRelPaths=*/true); MockFSProvider FS; ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); - auto FooCpp = testPath("foo.cpp"); + // Fill the filesystem. + auto FooCpp = testPath("src/foo.cpp"); FS.Files[FooCpp] = ""; + auto HeaderInPreambleH = testPath("src/header_in_preamble.h"); + FS.Files[HeaderInPreambleH] = HeaderInPreambleAnnotations.code(); + auto HeaderNotInPreambleH = testPath("src/header_not_in_preamble.h"); + FS.Files[HeaderNotInPreambleH] = HeaderNotInPreambleAnnotations.code(); - Server.addDocument(FooCpp, SourceAnnotations.code()); runAddDocument(Server, FooCpp, SourceAnnotations.code()); + + // Go to a definition in main source file. auto Locations = - runFindDefinitions(Server, FooCpp, SourceAnnotations.point()); + runFindDefinitions(Server, FooCpp, SourceAnnotations.point("p1")); EXPECT_TRUE(bool(Locations)) << "findDefinitions returned an error"; - EXPECT_THAT(*Locations, ElementsAre(Location{URIForFile{FooCpp}, SourceAnnotations.range()})); + + // Go to a definition in header_in_preamble.h. + Locations = runFindDefinitions(Server, FooCpp, SourceAnnotations.point("p2")); + EXPECT_TRUE(bool(Locations)) << "findDefinitions returned an error"; + EXPECT_THAT(*Locations, + ElementsAre(Location{URIForFile{HeaderInPreambleH}, + HeaderInPreambleAnnotations.range()})); + + // Go to a definition in header_not_in_preamble.h. + Locations = runFindDefinitions(Server, FooCpp, SourceAnnotations.point("p3")); + EXPECT_TRUE(bool(Locations)) << "findDefinitions returned an error"; + EXPECT_THAT(*Locations, + ElementsAre(Location{URIForFile{HeaderNotInPreambleH}, + HeaderNotInPreambleAnnotations.range()})); } TEST(Hover, All) { From dd74695afb13128dcb51623bc9ccb5c1da0dd9e4 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Sun, 12 Aug 2018 14:35:13 +0000 Subject: [PATCH 029/686] Add a new check to the readability module that flags uses of "magic numbers" (both floating-point and integral). Patch by Florin Iucha git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339516 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../CppCoreGuidelinesTidyModule.cpp | 3 + clang-tidy/readability/CMakeLists.txt | 1 + clang-tidy/readability/MagicNumbersCheck.cpp | 171 +++++++++++++++ clang-tidy/readability/MagicNumbersCheck.h | 97 +++++++++ .../readability/ReadabilityTidyModule.cpp | 3 + docs/ReleaseNotes.rst | 6 + .../cppcoreguidelines-avoid-magic-numbers.rst | 10 + docs/clang-tidy/checks/list.rst | 2 + .../checks/readability-magic-numbers.rst | 113 ++++++++++ test/clang-tidy/readability-magic-numbers.cpp | 199 ++++++++++++++++++ 10 files changed, 605 insertions(+) create mode 100644 clang-tidy/readability/MagicNumbersCheck.cpp create mode 100644 clang-tidy/readability/MagicNumbersCheck.h create mode 100644 docs/clang-tidy/checks/cppcoreguidelines-avoid-magic-numbers.rst create mode 100644 docs/clang-tidy/checks/readability-magic-numbers.rst create mode 100644 test/clang-tidy/readability-magic-numbers.cpp diff --git a/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp index a03925946..69ecd0c46 100644 --- a/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ b/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "../misc/UnconventionalAssignOperatorCheck.h" +#include "../readability/MagicNumbersCheck.h" #include "AvoidGotoCheck.h" #include "InterfacesGlobalInitCheck.h" #include "NarrowingConversionsCheck.h" @@ -41,6 +42,8 @@ class CppCoreGuidelinesModule : public ClangTidyModule { "cppcoreguidelines-avoid-goto"); CheckFactories.registerCheck( "cppcoreguidelines-interfaces-global-init"); + CheckFactories.registerCheck( + "cppcoreguidelines-avoid-magic-numbers"); CheckFactories.registerCheck( "cppcoreguidelines-narrowing-conversions"); CheckFactories.registerCheck("cppcoreguidelines-no-malloc"); diff --git a/clang-tidy/readability/CMakeLists.txt b/clang-tidy/readability/CMakeLists.txt index 961ad88a3..e7f56c558 100644 --- a/clang-tidy/readability/CMakeLists.txt +++ b/clang-tidy/readability/CMakeLists.txt @@ -11,6 +11,7 @@ add_clang_library(clangTidyReadabilityModule IdentifierNamingCheck.cpp ImplicitBoolConversionCheck.cpp InconsistentDeclarationParameterNameCheck.cpp + MagicNumbersCheck.cpp MisleadingIndentationCheck.cpp MisplacedArrayIndexCheck.cpp NamedParameterCheck.cpp diff --git a/clang-tidy/readability/MagicNumbersCheck.cpp b/clang-tidy/readability/MagicNumbersCheck.cpp new file mode 100644 index 000000000..08021f325 --- /dev/null +++ b/clang-tidy/readability/MagicNumbersCheck.cpp @@ -0,0 +1,171 @@ +//===--- MagicNumbersCheck.cpp - clang-tidy-------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A checker for magic numbers: integer or floating point literals embedded +// in the code, outside the definition of a constant or an enumeration. +// +//===----------------------------------------------------------------------===// + +#include "MagicNumbersCheck.h" +#include "../utils/OptionsUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/STLExtras.h" +#include + +using namespace clang::ast_matchers; +using namespace clang::ast_type_traits; + +namespace { + +bool isUsedToInitializeAConstant(const MatchFinder::MatchResult &Result, + const DynTypedNode &Node) { + + const auto *AsDecl = Node.get(); + if (AsDecl) { + if (AsDecl->getType().isConstQualified()) + return true; + + return AsDecl->isImplicit(); + } + + if (Node.get() != nullptr) + return true; + + return llvm::any_of(Result.Context->getParents(Node), + [&Result](const DynTypedNode &Parent) { + return isUsedToInitializeAConstant(Result, Parent); + }); +} + +} // namespace + +namespace clang { +namespace tidy { +namespace readability { + +const char DefaultIgnoredIntegerValues[] = "1;2;3;4;"; +const char DefaultIgnoredFloatingPointValues[] = "1.0;100.0;"; + +MagicNumbersCheck::MagicNumbersCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IgnoreAllFloatingPointValues( + Options.get("IgnoreAllFloatingPointValues", false)), + IgnorePowersOf2IntegerValues( + Options.get("IgnorePowersOf2IntegerValues", false)) { + // Process the set of ignored integer values. + const std::vector IgnoredIntegerValuesInput = + utils::options::parseStringList( + Options.get("IgnoredIntegerValues", DefaultIgnoredIntegerValues)); + IgnoredIntegerValues.resize(IgnoredIntegerValuesInput.size()); + llvm::transform(IgnoredIntegerValuesInput, IgnoredIntegerValues.begin(), + [](const std::string &Value) { return std::stoll(Value); }); + llvm::sort(IgnoredIntegerValues.begin(), IgnoredIntegerValues.end()); + + if (!IgnoreAllFloatingPointValues) { + // Process the set of ignored floating point values. + const std::vector IgnoredFloatingPointValuesInput = + utils::options::parseStringList(Options.get( + "IgnoredFloatingPointValues", DefaultIgnoredFloatingPointValues)); + IgnoredFloatingPointValues.reserve(IgnoredFloatingPointValuesInput.size()); + IgnoredDoublePointValues.reserve(IgnoredFloatingPointValuesInput.size()); + for (const auto &InputValue : IgnoredFloatingPointValuesInput) { + llvm::APFloat FloatValue(llvm::APFloat::IEEEsingle()); + FloatValue.convertFromString(InputValue, DefaultRoundingMode); + IgnoredFloatingPointValues.push_back(FloatValue.convertToFloat()); + + llvm::APFloat DoubleValue(llvm::APFloat::IEEEdouble()); + DoubleValue.convertFromString(InputValue, DefaultRoundingMode); + IgnoredDoublePointValues.push_back(DoubleValue.convertToDouble()); + } + llvm::sort(IgnoredFloatingPointValues.begin(), + IgnoredFloatingPointValues.end()); + llvm::sort(IgnoredDoublePointValues.begin(), + IgnoredDoublePointValues.end()); + } +} + +void MagicNumbersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IgnoredIntegerValues", DefaultIgnoredIntegerValues); + Options.store(Opts, "IgnoredFloatingPointValues", + DefaultIgnoredFloatingPointValues); +} + +void MagicNumbersCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(integerLiteral().bind("integer"), this); + if (!IgnoreAllFloatingPointValues) + Finder->addMatcher(floatLiteral().bind("float"), this); +} + +void MagicNumbersCheck::check(const MatchFinder::MatchResult &Result) { + checkBoundMatch(Result, "integer"); + checkBoundMatch(Result, "float"); +} + +bool MagicNumbersCheck::isConstant(const MatchFinder::MatchResult &Result, + const Expr &ExprResult) const { + return llvm::any_of( + Result.Context->getParents(ExprResult), + [&Result](const DynTypedNode &Parent) { + return isUsedToInitializeAConstant(Result, Parent) || + // Ignore this instance, because this match reports the location + // where the template is defined, not where it is instantiated. + Parent.get(); + }); +} + +bool MagicNumbersCheck::isIgnoredValue(const IntegerLiteral *Literal) const { + const llvm::APInt IntValue = Literal->getValue(); + const int64_t Value = IntValue.getZExtValue(); + if (Value == 0) + return true; + + if (IgnorePowersOf2IntegerValues && IntValue.isPowerOf2()) + return true; + + return std::binary_search(IgnoredIntegerValues.begin(), + IgnoredIntegerValues.end(), Value); +} + +bool MagicNumbersCheck::isIgnoredValue(const FloatingLiteral *Literal) const { + const llvm::APFloat FloatValue = Literal->getValue(); + if (FloatValue.isZero()) + return true; + + if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEsingle()) { + const float Value = FloatValue.convertToFloat(); + return std::binary_search(IgnoredFloatingPointValues.begin(), + IgnoredFloatingPointValues.end(), Value); + } + + if (&FloatValue.getSemantics() == &llvm::APFloat::IEEEdouble()) { + const double Value = FloatValue.convertToDouble(); + return std::binary_search(IgnoredDoublePointValues.begin(), + IgnoredDoublePointValues.end(), Value); + } + + return false; +} + +bool MagicNumbersCheck::isSyntheticValue(const SourceManager *SourceManager, + const IntegerLiteral *Literal) const { + const std::pair FileOffset = + SourceManager->getDecomposedLoc(Literal->getLocation()); + if (FileOffset.first.isInvalid()) + return false; + + const StringRef BufferIdentifier = + SourceManager->getBuffer(FileOffset.first)->getBufferIdentifier(); + + return BufferIdentifier.empty(); +} + +} // namespace readability +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/readability/MagicNumbersCheck.h b/clang-tidy/readability/MagicNumbersCheck.h new file mode 100644 index 000000000..db4cc8865 --- /dev/null +++ b/clang-tidy/readability/MagicNumbersCheck.h @@ -0,0 +1,97 @@ +//===--- MagicNumbersCheck.h - clang-tidy-----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MAGICNUMBERSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MAGICNUMBERSCHECK_H + +#include "../ClangTidy.h" +#include +#include +#include + +namespace clang { +namespace tidy { +namespace readability { + +/// Detects magic numbers, integer and floating point literals embedded in code. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability-magic-numbers.html +class MagicNumbersCheck : public ClangTidyCheck { +public: + MagicNumbersCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + bool isConstant(const clang::ast_matchers::MatchFinder::MatchResult &Result, + const clang::Expr &ExprResult) const; + + bool isIgnoredValue(const IntegerLiteral *Literal) const; + bool isIgnoredValue(const FloatingLiteral *Literal) const; + + bool isSyntheticValue(const clang::SourceManager *, + const FloatingLiteral *) const { + return false; + } + + bool isSyntheticValue(const clang::SourceManager *SourceManager, + const IntegerLiteral *Literal) const; + + template + void checkBoundMatch(const ast_matchers::MatchFinder::MatchResult &Result, + const char *BoundName) { + const L *MatchedLiteral = Result.Nodes.getNodeAs(BoundName); + if (!MatchedLiteral) + return; + + if (Result.SourceManager->isMacroBodyExpansion( + MatchedLiteral->getLocation())) + return; + + if (isIgnoredValue(MatchedLiteral)) + return; + + if (isConstant(Result, *MatchedLiteral)) + return; + + if (isSyntheticValue(Result.SourceManager, MatchedLiteral)) + return; + + const StringRef LiteralSourceText = Lexer::getSourceText( + CharSourceRange::getTokenRange(MatchedLiteral->getSourceRange()), + *Result.SourceManager, getLangOpts()); + + diag(MatchedLiteral->getLocation(), + "%0 is a magic number; consider replacing it with a named constant") + << LiteralSourceText; + } + + const bool IgnoreAllFloatingPointValues; + const bool IgnorePowersOf2IntegerValues; + + constexpr static unsigned SensibleNumberOfMagicValueExceptions = 16; + + constexpr static llvm::APFloat::roundingMode DefaultRoundingMode = + llvm::APFloat::rmNearestTiesToEven; + + llvm::SmallVector + IgnoredIntegerValues; + llvm::SmallVector + IgnoredFloatingPointValues; + llvm::SmallVector + IgnoredDoublePointValues; +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MAGICNUMBERSCHECK_H diff --git a/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tidy/readability/ReadabilityTidyModule.cpp index be1ae4388..a5b91544d 100644 --- a/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -20,6 +20,7 @@ #include "IdentifierNamingCheck.h" #include "ImplicitBoolConversionCheck.h" #include "InconsistentDeclarationParameterNameCheck.h" +#include "MagicNumbersCheck.h" #include "MisleadingIndentationCheck.h" #include "MisplacedArrayIndexCheck.h" #include "NamedParameterCheck.h" @@ -65,6 +66,8 @@ class ReadabilityModule : public ClangTidyModule { "readability-implicit-bool-conversion"); CheckFactories.registerCheck( "readability-inconsistent-declaration-parameter-name"); + CheckFactories.registerCheck( + "readability-magic-numbers"); CheckFactories.registerCheck( "readability-misleading-indentation"); CheckFactories.registerCheck( diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index 26f47c1fa..a316466f3 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -59,6 +59,12 @@ Improvements to clang-tidy The improvements are... +- New :doc:`readability-magic-numbers + ` check. + + Detect usage of magic numbers, numbers that are used as literals instead of + introduced via constants or symbols. + Improvements to include-fixer ----------------------------- diff --git a/docs/clang-tidy/checks/cppcoreguidelines-avoid-magic-numbers.rst b/docs/clang-tidy/checks/cppcoreguidelines-avoid-magic-numbers.rst new file mode 100644 index 000000000..6a9f64e45 --- /dev/null +++ b/docs/clang-tidy/checks/cppcoreguidelines-avoid-magic-numbers.rst @@ -0,0 +1,10 @@ +.. title:: clang-tidy - cppcoreguidelines-avoid-magic-numbers +.. meta:: + :http-equiv=refresh: 5;URL=readability-magic-numbers.html + +cppcoreguidelines-avoid-magic-numbers +===================================== + +The cppcoreguidelines-avoid-magic-numbers check is an alias, please see +`readability-magic-numbers `_ +for more information. diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index 9bb68bcd6..25515670c 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -79,6 +79,7 @@ Clang-Tidy Checks cert-msc51-cpp cert-oop11-cpp (redirects to performance-move-constructor-init) cppcoreguidelines-avoid-goto + cppcoreguidelines-avoid-magic-numbers (redirects to readability-magic-numbers) cppcoreguidelines-c-copy-assignment-signature (redirects to misc-unconventional-assign-operator) cppcoreguidelines-interfaces-global-init cppcoreguidelines-narrowing-conversions @@ -218,6 +219,7 @@ Clang-Tidy Checks readability-identifier-naming readability-implicit-bool-conversion readability-inconsistent-declaration-parameter-name + readability-magic-numbers readability-misleading-indentation readability-misplaced-array-index readability-named-parameter diff --git a/docs/clang-tidy/checks/readability-magic-numbers.rst b/docs/clang-tidy/checks/readability-magic-numbers.rst new file mode 100644 index 000000000..946672ed7 --- /dev/null +++ b/docs/clang-tidy/checks/readability-magic-numbers.rst @@ -0,0 +1,113 @@ +.. title:: clang-tidy - readability-magic-numbers + +readability-magic-numbers +========================= + +Detects magic numbers, integer or floating point literals that are embedded in +code and not introduced via constants or symbols. + +Many coding guidelines advise replacing the magic values with symbolic +constants to improve readability. Here are a few references: + + * `Rule ES.45: Avoid “magic constants”; use symbolic constants in C++ Core Guidelines `_ + * `Rule 5.1.1 Use symbolic names instead of literal values in code in High Integrity C++ `_ + * Item 17 in "C++ Coding Standards: 101 Rules, Guidelines and Best + Practices" by Herb Sutter and Andrei Alexandrescu + * Chapter 17 in "Clean Code - A handbook of agile software craftsmanship." + by Robert C. Martin + * Rule 20701 in "TRAIN REAL TIME DATA PROTOCOL Coding Rules" by Armin-Hagen + Weiss, Bombardier + * http://wiki.c2.com/?MagicNumber + + +Examples of magic values: + +.. code-block:: c++ + + double circleArea = 3.1415926535 * radius * radius; + + double totalCharge = 1.08 * itemPrice; + + int getAnswer() { + return -3; // FILENOTFOUND + } + + for (int mm = 1; mm <= 12; ++mm) { + std::cout << month[mm] << '\n'; + } + +Example with magic values refactored: + +.. code-block:: c++ + + double circleArea = M_PI * radius * radius; + + const double TAX_RATE = 0.08; // or make it variable and read from a file + + double totalCharge = (1.0 + TAX_RATE) * itemPrice; + + int getAnswer() { + return E_FILE_NOT_FOUND; + } + + for (int mm = 1; mm <= MONTHS_IN_A_YEAR; ++mm) { + std::cout << month[mm] << '\n'; + } + +For integral literals by default only `0` and `1` (and `-1`) integer values +are accepted without a warning. This can be overridden with the +:option:`IgnoredIntegerValues` option. Negative values are accepted if their +absolute value is present in the :option:`IgnoredIntegerValues` list. + +As a special case for integral values, all powers of two can be accepted +without warning by enabling the :option:`IgnorePowersOf2IntegerValues` option. + +For floating point literals by default the `0.0` floating point value is +accepted without a warning. The set of ignored floating point literals can +be configured using the :option:`IgnoredFloatingPointValues` option. +For each value in that set, the given string value is converted to a +floating-point value representation used by the target architecture. If a +floating-point literal value compares equal to one of the converted values, +then that literal is not diagnosed by this check. Because floating-point +equality is used to determine whether to diagnose or not, the user needs to +be aware of the details of floating-point representations for any values that +cannot be precisely represented for their target architecture. + +For each value in the :option:`IgnoredFloatingPointValues` set, both the +single-precision form and double-precision form are accepted (for example, if +3.14 is in the set, neither 3.14f nor 3.14 will produce a warning). + +Scientific notation is supported for both source code input and option. +Alternatively, the check for the floating point numbers can be disabled for +all floating point values by enabling the +:option:`IgnoreAllFloatingPointValues` option. + +Since values `0` and `0.0` are so common as the base counter of loops, +or initialization values for sums, they are always accepted without warning, +even if not present in the respective ignored values list. + +Options +------- + +.. option:: IgnoredIntegerValues + + Semicolon-separated list of magic positive integers that will be accepted + without a warning. Default values are `{1, 2, 3, 4}`, and `0` is accepted + unconditionally. + +.. option:: IgnorePowersOf2IntegerValues + + Boolean value indicating whether to accept all powers-of-two integer values + without warning. Default value is `false`. + +.. option:: IgnoredFloatingPointValues + + Semicolon-separated list of magic positive floating point values that will + be accepted without a warning. Default values are `{1.0, 100.0}` and `0.0` + is accepted unconditionally. + +.. option:: IgnoreAllFloatingPointValues + + Boolean value indicating whether to accept all floating point values without + warning. Default value is `false`. + diff --git a/test/clang-tidy/readability-magic-numbers.cpp b/test/clang-tidy/readability-magic-numbers.cpp new file mode 100644 index 000000000..3cf9dc52e --- /dev/null +++ b/test/clang-tidy/readability-magic-numbers.cpp @@ -0,0 +1,199 @@ +// RUN: %check_clang_tidy %s readability-magic-numbers %t \ +// RUN: -config='{CheckOptions: \ +// RUN: [{key: readability-magic-numbers.IgnoredIntegerValues, value: "0;1;2;10;100;"}, \ +// RUN: {key: readability-magic-numbers.IgnoredFloatingPointValues, value: "3.14;2.71828;9.81;10000.0;101.0;0x1.2p3"}, \ +// RUN: {key: readability-magic-numbers.IgnorePowersOf2IntegerValues, value: 1}]}' \ +// RUN: -- + +template +struct ValueBucket { + T value[V]; +}; + +int BadGlobalInt = 5; +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 5 is a magic number; consider replacing it with a named constant [readability-magic-numbers] + +int IntSquarer(int param) { + return param * param; +} + +void BuggyFunction() { + int BadLocalInt = 6; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: 6 is a magic number; consider replacing it with a named constant [readability-magic-numbers] + + (void)IntSquarer(7); + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 7 is a magic number; consider replacing it with a named constant [readability-magic-numbers] + + int LocalArray[15]; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 15 is a magic number; consider replacing it with a named constant [readability-magic-numbers] + + for (int ii = 0; ii < 22; ++ii) + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 22 is a magic number; consider replacing it with a named constant [readability-magic-numbers] + { + LocalArray[ii] = 3 * ii; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 3 is a magic number; consider replacing it with a named constant [readability-magic-numbers] + } + + ValueBucket Bucket; + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 66 is a magic number; consider replacing it with a named constant [readability-magic-numbers] +} + +class TwoIntContainer { +public: + TwoIntContainer(int val) : anotherMember(val * val), yetAnotherMember(6), anotherConstant(val + val) {} + // CHECK-MESSAGES: :[[@LINE-1]]:73: warning: 6 is a magic number; consider replacing it with a named constant [readability-magic-numbers] + + int getValue() const; + +private: + int oneMember = 9; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 9 is a magic number; consider replacing it with a named constant [readability-magic-numbers] + + int anotherMember; + + int yetAnotherMember; + + const int oneConstant = 2; + + const int anotherConstant; +}; + +int ValueArray[] = {3, 5}; +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: 3 is a magic number; consider replacing it with a named constant [readability-magic-numbers] +// CHECK-MESSAGES: :[[@LINE-2]]:24: warning: 5 is a magic number; consider replacing it with a named constant [readability-magic-numbers] + +float FloatPiVariable = 3.1415926535f; +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 3.1415926535f is a magic number; consider replacing it with a named constant [readability-magic-numbers] +double DoublePiVariable = 6.283185307; +// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: 6.283185307 is a magic number; consider replacing it with a named constant [readability-magic-numbers] + +float SomeFloats[] = {0.5, 0x1.2p4}; +// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: 0.5 is a magic number; consider replacing it with a named constant [readability-magic-numbers] +// CHECK-MESSAGES: :[[@LINE-2]]:28: warning: 0x1.2p4 is a magic number; consider replacing it with a named constant [readability-magic-numbers] + +int getAnswer() { + if (ValueArray[0] < ValueArray[1]) + return ValueArray[1]; + + return -3; // FILENOTFOUND + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 3 is a magic number; consider replacing it with a named constant [readability-magic-numbers] +} + +/* + * Clean code + */ + +#define INT_MACRO 5 + +const int GoodGlobalIntConstant = 42; + +constexpr int AlsoGoodGlobalIntConstant = 42; + +int InitializedByMacro = INT_MACRO; + +void SolidFunction() { + const int GoodLocalIntConstant = 43; + + (void)IntSquarer(GoodLocalIntConstant); + + int LocalArray[INT_MACRO]; + + ValueBucket Bucket; +} + +const int ConstValueArray[] = {7, 9}; + +const int ConstValueArray2D[2][2] = {{7, 9}, {13, 15}}; + +/* + * no warnings for ignored values (specified in the configuration above) + */ +int GrandfatheredIntegerValues[] = {0, 1, 2, 10, 100, -1, -10, -100, 65536}; + +float GrandfatheredFloatValues[] = {3.14f, 3.14, 2.71828, 2.71828f, -1.01E2, 1E4, 0x1.2p3}; + +/* + * no warnings for enums + */ +enum Smorgasbord { + STARTER, + ALPHA = 3, + BETA = 1 << 5, +}; + +const float FloatPiConstant = 3.1415926535f; +const double DoublePiConstant = 6.283185307; + +const float Angles[] = {45.0f, 90.0f, 135.0f}; + +double DoubleZeroIsAccepted = 0.0; +float FloatZeroIsAccepted = 0.0f; + +namespace geometry { + +template +struct Point { + T x; + T y; + + explicit Point(T xval, T yval) noexcept : x{xval}, y{yval} { + } +}; + +template +struct Dimension { + T x; + T y; + + explicit Dimension(T xval, T yval) noexcept : x{xval}, y{yval} { + } +}; + +template +struct Rectangle { + Point origin; + Dimension size; + T rotation; // angle of rotation around origin + + Rectangle(Point origin_, Dimension size_, T rotation_ = 0) noexcept : origin{origin_}, size{size_}, rotation{rotation_} { + } + + bool contains(Point point) const; +}; + +} // namespace geometry + +const geometry::Rectangle mandelbrotCanvas{geometry::Point{-2.5, -1}, geometry::Dimension{3.5, 2}}; + +// Simulate the macro magic in Google Test internal headers +class AssertionHelper { +public: + AssertionHelper(const char *Message, int LineNumber) : Message(Message), LineNumber(LineNumber) {} + +private: + const char *Message; + int LineNumber; +}; + +#define ASSERTION_HELPER_AT(M, L) AssertionHelper(M, L) + +#define ASSERTION_HELPER(M) ASSERTION_HELPER_AT(M, __LINE__) + +void FunctionWithCompilerDefinedSymbol(void) { + ASSERTION_HELPER("here and now"); +} + +// prove that integer literals introduced by the compiler are accepted silently +extern int ConsumeString(const char *Input); + +const char *SomeStrings[] = {"alpha", "beta", "gamma"}; + +int TestCheckerOverreach() { + int Total = 0; + + for (const auto *Str : SomeStrings) { + Total += ConsumeString(Str); + } + + return Total; +} From 7e714d5a72cd316166e7e4f1af8995c2971b2d94 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Sun, 12 Aug 2018 14:47:16 +0000 Subject: [PATCH 030/686] Adding the readability module to the list of dependencies for the C++ Core Guidelines module. Amends r339516 for a failing bot. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339517 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/cppcoreguidelines/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tidy/cppcoreguidelines/CMakeLists.txt index 49fedd622..ceec7aac8 100644 --- a/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ b/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -27,6 +27,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule clangLex clangTidy clangTidyMiscModule + clangTidyReadabilityModule clangTidyUtils clangTooling ) From b756f893ddb78e9afae57e30425205235ce91218 Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Mon, 13 Aug 2018 08:23:01 +0000 Subject: [PATCH 031/686] [clangd] Support textEdit in addition to insertText. Summary: Completion replies contains textEdits as well. Note that this change relies on https://reviews.llvm.org/D50443. Reviewers: ilya-biryukov Reviewed By: ilya-biryukov Subscribers: mgrang, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50449 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339543 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 51 ++++++++++++++--- clangd/CodeComplete.h | 3 + clangd/SourceCode.cpp | 5 ++ clangd/SourceCode.h | 2 + unittests/clangd/CodeCompleteTests.cpp | 76 ++++++++++++++++++++++++++ unittests/clangd/SourceCodeTests.cpp | 15 +++++ 6 files changed, 145 insertions(+), 7 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 90ebccb14..11d41f0ea 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -34,6 +34,7 @@ #include "index/Index.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Format/Format.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" @@ -285,6 +286,11 @@ struct CodeCompletionBuilder { Completion.FixIts.push_back( toTextEdit(FixIt, ASTCtx.getSourceManager(), ASTCtx.getLangOpts())); } + std::sort(Completion.FixIts.begin(), Completion.FixIts.end(), + [](const TextEdit &X, const TextEdit &Y) { + return std::tie(X.range.start.line, X.range.start.character) < + std::tie(Y.range.start.line, Y.range.start.character); + }); } if (C.IndexResult) { Completion.Origin |= C.IndexResult->Origin; @@ -1044,6 +1050,23 @@ class CodeCompleteFlow { // This is called by run() once Sema code completion is done, but before the // Sema data structures are torn down. It does all the real work. CodeCompleteResult runWithSema() { + const auto &CodeCompletionRange = CharSourceRange::getCharRange( + Recorder->CCSema->getPreprocessor().getCodeCompletionTokenRange()); + Range TextEditRange; + // When we are getting completions with an empty identifier, for example + // std::vector asdf; + // asdf.^; + // Then the range will be invalid and we will be doing insertion, use + // current cursor position in such cases as range. + if (CodeCompletionRange.isValid()) { + TextEditRange = halfOpenToRange(Recorder->CCSema->getSourceManager(), + CodeCompletionRange); + } else { + const auto &Pos = sourceLocToPosition( + Recorder->CCSema->getSourceManager(), + Recorder->CCSema->getPreprocessor().getCodeCompletionLoc()); + TextEditRange.start = TextEditRange.end = Pos; + } Filter = FuzzyMatcher( Recorder->CCSema->getPreprocessor().getCodeCompletionFilter()); QueryScopes = getQueryScopes(Recorder->CCContext, @@ -1063,6 +1086,7 @@ class CodeCompleteFlow { for (auto &C : Top) { Output.Completions.push_back(toCodeCompletion(C.first)); Output.Completions.back().Score = C.second; + Output.Completions.back().CompletionTokenRange = TextEditRange; } Output.HasMore = Incomplete; Output.Context = Recorder->CCContext.getKind(); @@ -1278,16 +1302,29 @@ CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const { LSP.documentation = Documentation; LSP.sortText = sortText(Score.Total, Name); LSP.filterText = Name; - // FIXME(kadircet): Use LSP.textEdit instead of insertText, because it causes - // undesired behaviours. Like completing "this.^" into "this-push_back". - LSP.insertText = RequiredQualifier + Name; + LSP.textEdit = {CompletionTokenRange, RequiredQualifier + Name}; + // Merge continious additionalTextEdits into main edit. The main motivation + // behind this is to help LSP clients, it seems most of them are confused when + // they are provided with additionalTextEdits that are consecutive to main + // edit. + // Note that we store additional text edits from back to front in a line. That + // is mainly to help LSP clients again, so that changes do not effect each + // other. + for (const auto &FixIt : FixIts) { + if (IsRangeConsecutive(FixIt.range, LSP.textEdit->range)) { + LSP.textEdit->newText = FixIt.newText + LSP.textEdit->newText; + LSP.textEdit->range.start = FixIt.range.start; + } else { + LSP.additionalTextEdits.push_back(FixIt); + } + } if (Opts.EnableSnippets) - LSP.insertText += SnippetSuffix; + LSP.textEdit->newText += SnippetSuffix; + // FIXME(kadircet): Do not even fill insertText after making sure textEdit is + // compatible with most of the editors. + LSP.insertText = LSP.textEdit->newText; LSP.insertTextFormat = Opts.EnableSnippets ? InsertTextFormat::Snippet : InsertTextFormat::PlainText; - LSP.additionalTextEdits.reserve(FixIts.size() + (HeaderInsertion ? 1 : 0)); - for (const auto &FixIt : FixIts) - LSP.additionalTextEdits.push_back(FixIt); if (HeaderInsertion) LSP.additionalTextEdits.push_back(*HeaderInsertion); return LSP; diff --git a/clangd/CodeComplete.h b/clangd/CodeComplete.h index c623be34a..542d47854 100644 --- a/clangd/CodeComplete.h +++ b/clangd/CodeComplete.h @@ -123,6 +123,9 @@ struct CodeCompletion { /// converting '->' to '.' on member access. std::vector FixIts; + /// Holds the range of the token we are going to replace with this completion. + Range CompletionTokenRange; + // Scores are used to rank completion items. struct Scores { // The score that items are ranked by. diff --git a/clangd/SourceCode.cpp b/clangd/SourceCode.cpp index 931ad3c0f..f801278cc 100644 --- a/clangd/SourceCode.cpp +++ b/clangd/SourceCode.cpp @@ -224,5 +224,10 @@ TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, return Result; } +bool IsRangeConsecutive(const Range &Left, const Range &Right) { + return Left.end.line == Right.start.line && + Left.end.character == Right.start.character; +} + } // namespace clangd } // namespace clang diff --git a/clangd/SourceCode.h b/clangd/SourceCode.h index e633a1570..ed50974ec 100644 --- a/clangd/SourceCode.h +++ b/clangd/SourceCode.h @@ -76,6 +76,8 @@ TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M, /// are normalized as much as possible. llvm::Optional getRealPath(const FileEntry *F, const SourceManager &SourceMgr); + +bool IsRangeConsecutive(const Range &Left, const Range &Right); } // namespace clangd } // namespace clang #endif diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 350d47d9b..67f54ebfb 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1439,6 +1439,82 @@ TEST(CompletionTest, FixItForDotToArrow) { } } +TEST(CompletionTest, RenderWithFixItMerged) { + TextEdit FixIt; + FixIt.range.end.character = 5; + FixIt.newText = "->"; + + CodeCompletion C; + C.Name = "x"; + C.RequiredQualifier = "Foo::"; + C.FixIts = {FixIt}; + C.CompletionTokenRange.start.character = 5; + + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + + auto R = C.render(Opts); + EXPECT_TRUE(R.textEdit); + EXPECT_EQ(R.textEdit->newText, "->Foo::x"); + EXPECT_TRUE(R.additionalTextEdits.empty()); +} + +TEST(CompletionTest, RenderWithFixItNonMerged) { + TextEdit FixIt; + FixIt.range.end.character = 4; + FixIt.newText = "->"; + + CodeCompletion C; + C.Name = "x"; + C.RequiredQualifier = "Foo::"; + C.FixIts = {FixIt}; + C.CompletionTokenRange.start.character = 5; + + CodeCompleteOptions Opts; + Opts.IncludeFixIts = true; + + auto R = C.render(Opts); + EXPECT_TRUE(R.textEdit); + EXPECT_EQ(R.textEdit->newText, "Foo::x"); + EXPECT_THAT(R.additionalTextEdits, UnorderedElementsAre(FixIt)); +} + +TEST(CompletionTest, CompletionTokenRange) { + MockFSProvider FS; + MockCompilationDatabase CDB; + IgnoreDiagnostics DiagConsumer; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + constexpr const char *TestCodes[] = { + R"cpp( + class Auxilary { + public: + void AuxFunction(); + }; + void f() { + Auxilary x; + x.[[Aux]]^; + } + )cpp", + R"cpp( + class Auxilary { + public: + void AuxFunction(); + }; + void f() { + Auxilary x; + x.[[]]^; + } + )cpp"}; + for (const auto &Text : TestCodes) { + Annotations TestCode(Text); + auto Results = completions(Server, TestCode.code(), TestCode.point()); + + EXPECT_EQ(Results.Completions.size(), 1u); + EXPECT_THAT(Results.Completions.front().CompletionTokenRange, TestCode.range()); + } +} + } // namespace } // namespace clangd } // namespace clang diff --git a/unittests/clangd/SourceCodeTests.cpp b/unittests/clangd/SourceCodeTests.cpp index 57276affe..4a97fa00a 100644 --- a/unittests/clangd/SourceCodeTests.cpp +++ b/unittests/clangd/SourceCodeTests.cpp @@ -37,6 +37,13 @@ Position position(int line, int character) { return Pos; } +Range range(const std::pair p1, const std::pair p2) { + Range range; + range.start = position(p1.first, p1.second); + range.end = position(p2.first, p2.second); + return range; +} + TEST(SourceCodeTests, PositionToOffset) { // line out of bounds EXPECT_THAT_EXPECTED(positionToOffset(File, position(-1, 2)), Failed()); @@ -119,6 +126,14 @@ TEST(SourceCodeTests, OffsetToPosition) { EXPECT_THAT(offsetToPosition(File, 30), Pos(2, 9)) << "out of bounds"; } +TEST(SourceCodeTests, IsRangeConsecutive) { + EXPECT_TRUE(IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 3}, {2, 4}))); + EXPECT_FALSE( + IsRangeConsecutive(range({0, 2}, {0, 3}), range({2, 3}, {2, 4}))); + EXPECT_FALSE( + IsRangeConsecutive(range({2, 2}, {2, 3}), range({2, 4}, {2, 5}))); +} + } // namespace } // namespace clangd } // namespace clang From ef4388093be7c0013a05638d34b30a3ee54f2a94 Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Mon, 13 Aug 2018 08:40:05 +0000 Subject: [PATCH 032/686] [clangd] Introduce scoring mechanism for SignatureInformations. Reviewers: ilya-biryukov Reviewed By: ilya-biryukov Subscribers: mgrang, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50555 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339547 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 67 +++++++++++++++++++++----- clangd/Quality.cpp | 12 +++++ clangd/Quality.h | 10 ++++ unittests/clangd/CodeCompleteTests.cpp | 22 +++++++++ 4 files changed, 100 insertions(+), 11 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 11d41f0ea..12a3d5f48 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -129,16 +129,16 @@ toCompletionItemKind(CodeCompletionResult::ResultKind ResKind, /// Get the optional chunk as a string. This function is possibly recursive. /// /// The parameter info for each parameter is appended to the Parameters. -std::string -getOptionalParameters(const CodeCompletionString &CCS, - std::vector &Parameters) { +std::string getOptionalParameters(const CodeCompletionString &CCS, + std::vector &Parameters, + SignatureQualitySignals &Signal) { std::string Result; for (const auto &Chunk : CCS) { switch (Chunk.Kind) { case CodeCompletionString::CK_Optional: assert(Chunk.Optional && "Expected the optional code completion string to be non-null."); - Result += getOptionalParameters(*Chunk.Optional, Parameters); + Result += getOptionalParameters(*Chunk.Optional, Parameters, Signal); break; case CodeCompletionString::CK_VerticalSpace: break; @@ -154,6 +154,8 @@ getOptionalParameters(const CodeCompletionString &CCS, ParameterInformation Info; Info.label = Chunk.Text; Parameters.push_back(std::move(Info)); + Signal.ContainsActiveParameter = true; + Signal.NumberOfOptionalParameters++; break; } default: @@ -685,6 +687,9 @@ struct CompletionRecorder : public CodeCompleteConsumer { llvm::unique_function ResultsCallback; }; +using ScoredSignature = + std::pair; + class SignatureHelpCollector final : public CodeCompleteConsumer { public: @@ -698,7 +703,9 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, OverloadCandidate *Candidates, unsigned NumCandidates) override { + std::vector ScoredSignatures; SigHelp.signatures.reserve(NumCandidates); + ScoredSignatures.reserve(NumCandidates); // FIXME(rwols): How can we determine the "active overload candidate"? // Right now the overloaded candidates seem to be provided in a "best fit" // order, so I'm not too worried about this. @@ -712,11 +719,45 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { CurrentArg, S, *Allocator, CCTUInfo, true); assert(CCS && "Expected the CodeCompletionString to be non-null"); // FIXME: for headers, we need to get a comment from the index. - SigHelp.signatures.push_back(processOverloadCandidate( + ScoredSignatures.push_back(processOverloadCandidate( Candidate, *CCS, getParameterDocComment(S.getASTContext(), Candidate, CurrentArg, /*CommentsFromHeaders=*/false))); } + std::sort(ScoredSignatures.begin(), ScoredSignatures.end(), + [](const ScoredSignature &L, const ScoredSignature &R) { + // Ordering follows: + // - Less number of parameters is better. + // - Function is better than FunctionType which is better than + // Function Template. + // - High score is better. + // - Shorter signature is better. + // - Alphebatically smaller is better. + if (L.first.NumberOfParameters != R.first.NumberOfParameters) + return L.first.NumberOfParameters < + R.first.NumberOfParameters; + if (L.first.NumberOfOptionalParameters != + R.first.NumberOfOptionalParameters) + return L.first.NumberOfOptionalParameters < + R.first.NumberOfOptionalParameters; + if (L.first.Kind != R.first.Kind) { + using OC = CodeCompleteConsumer::OverloadCandidate; + switch (L.first.Kind) { + case OC::CK_Function: + return true; + case OC::CK_FunctionType: + return R.first.Kind != OC::CK_Function; + case OC::CK_FunctionTemplate: + return false; + } + llvm_unreachable("Unknown overload candidate type."); + } + if (L.second.label.size() != R.second.label.size()) + return L.second.label.size() < R.second.label.size(); + return L.second.label < R.second.label; + }); + for (const auto &SS : ScoredSignatures) + SigHelp.signatures.push_back(SS.second); } GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; } @@ -726,14 +767,15 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { private: // FIXME(ioeric): consider moving CodeCompletionString logic here to // CompletionString.h. - SignatureInformation - processOverloadCandidate(const OverloadCandidate &Candidate, - const CodeCompletionString &CCS, - llvm::StringRef DocComment) const { + ScoredSignature processOverloadCandidate(const OverloadCandidate &Candidate, + const CodeCompletionString &CCS, + llvm::StringRef DocComment) const { SignatureInformation Result; + SignatureQualitySignals Signal; const char *ReturnType = nullptr; Result.documentation = formatDocumentation(CCS, DocComment); + Signal.Kind = Candidate.getKind(); for (const auto &Chunk : CCS) { switch (Chunk.Kind) { @@ -755,6 +797,8 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { ParameterInformation Info; Info.label = Chunk.Text; Result.parameters.push_back(std::move(Info)); + Signal.NumberOfParameters++; + Signal.ContainsActiveParameter = true; break; } case CodeCompletionString::CK_Optional: { @@ -762,7 +806,7 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { assert(Chunk.Optional && "Expected the optional code completion string to be non-null."); Result.label += - getOptionalParameters(*Chunk.Optional, Result.parameters); + getOptionalParameters(*Chunk.Optional, Result.parameters, Signal); break; } case CodeCompletionString::CK_VerticalSpace: @@ -776,7 +820,8 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { Result.label += " -> "; Result.label += ReturnType; } - return Result; + dlog("Signal for {0}: {1}", Result, Signal); + return {Signal, Result}; } SignatureHelp &SigHelp; diff --git a/clangd/Quality.cpp b/clangd/Quality.cpp index 8cf794b0f..f96b50e27 100644 --- a/clangd/Quality.cpp +++ b/clangd/Quality.cpp @@ -401,5 +401,17 @@ std::string sortText(float Score, llvm::StringRef Name) { return S; } +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const SignatureQualitySignals &S) { + OS << formatv("=== Signature Quality:\n"); + OS << formatv("\tNumber of parameters: {0}\n", S.NumberOfParameters); + OS << formatv("\tNumber of optional parameters: {0}\n", + S.NumberOfOptionalParameters); + OS << formatv("\tContains active parameter: {0}\n", + S.ContainsActiveParameter); + OS << formatv("\tKind: {0}\n", S.Kind); + return OS; +} + } // namespace clangd } // namespace clang diff --git a/clangd/Quality.h b/clangd/Quality.h index 54778b9fa..86e21963d 100644 --- a/clangd/Quality.h +++ b/clangd/Quality.h @@ -163,6 +163,16 @@ template > class TopN { /// LSP. (The highest score compares smallest so it sorts at the top). std::string sortText(float Score, llvm::StringRef Tiebreak = ""); +struct SignatureQualitySignals { + uint32_t NumberOfParameters = 0; + uint32_t NumberOfOptionalParameters = 0; + bool ContainsActiveParameter = false; + CodeCompleteConsumer::OverloadCandidate::CandidateKind Kind = + CodeCompleteConsumer::OverloadCandidate::CandidateKind::CK_Function; +}; +llvm::raw_ostream &operator<<(llvm::raw_ostream &, + const SignatureQualitySignals &); + } // namespace clangd } // namespace clang diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 67f54ebfb..d59754a29 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1515,6 +1515,28 @@ TEST(CompletionTest, CompletionTokenRange) { } } +TEST(SignatureHelpTest, OverloadsOrdering) { + const auto Results = signatures(R"cpp( + void foo(int x); + void foo(int x, float y); + void foo(float x, int y); + void foo(float x, float y); + void foo(int x, int y = 0); + int main() { foo(^); } + )cpp"); + EXPECT_THAT( + Results.signatures, + ElementsAre( + Sig("foo(int x) -> void", {"int x"}), + Sig("foo(int x, int y = 0) -> void", {"int x", "int y = 0"}), + Sig("foo(float x, int y) -> void", {"float x", "int y"}), + Sig("foo(int x, float y) -> void", {"int x", "float y"}), + Sig("foo(float x, float y) -> void", {"float x", "float y"}))); + // We always prefer the first signature. + EXPECT_EQ(0, Results.activeSignature); + EXPECT_EQ(0, Results.activeParameter); +} + } // namespace } // namespace clangd } // namespace clang From 6f44c50f58e3583854f7f53ea79c73e923e29aa8 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 13 Aug 2018 08:57:06 +0000 Subject: [PATCH 033/686] [clangd] Generate incomplete trigrams for the Dex index This patch handles trigram generation "short" identifiers and queries. Trigram generator produces incomplete trigrams for short names so that the same query iterator API can be used to match symbols which don't have enough symbols to form a trigram and correctly handle queries which also are not sufficient for generating a full trigram. Reviewed by: ioeric Differential revision: https://reviews.llvm.org/D50517 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339548 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.h | 8 +++ clangd/index/dex/Trigram.cpp | 94 ++++++++++++++++++++---------- clangd/index/dex/Trigram.h | 14 ++++- unittests/clangd/DexIndexTests.cpp | 60 +++++++++++-------- 4 files changed, 118 insertions(+), 58 deletions(-) diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 5e13b17af..317b54964 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -47,6 +47,14 @@ using DocID = uint32_t; /// Contains sorted sequence of DocIDs all of which belong to symbols matching /// certain criteria, i.e. containing a Search Token. PostingLists are values /// for the inverted index. +// FIXME(kbobyrev): Posting lists for incomplete trigrams (one/two symbols) are +// likely to be very dense and hence require special attention so that the index +// doesn't use too much memory. Possible solution would be to construct +// compressed posting lists which consist of ranges of DocIDs instead of +// distinct DocIDs. A special case would be the empty query: for that case +// TrueIterator should be implemented - an iterator which doesn't actually store +// any PostingList within itself, but "contains" all DocIDs in range +// [0, IndexSize). using PostingList = std::vector; /// Immutable reference to PostingList object. using PostingListRef = llvm::ArrayRef; diff --git a/clangd/index/dex/Trigram.cpp b/clangd/index/dex/Trigram.cpp index 549093df7..370ca29ea 100644 --- a/clangd/index/dex/Trigram.cpp +++ b/clangd/index/dex/Trigram.cpp @@ -10,11 +10,9 @@ #include "Trigram.h" #include "../../FuzzyMatch.h" #include "Token.h" - #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringExtras.h" - #include #include #include @@ -25,12 +23,11 @@ namespace clang { namespace clangd { namespace dex { -// FIXME(kbobyrev): Deal with short symbol symbol names. A viable approach would -// be generating unigrams and bigrams here, too. This would prevent symbol index -// from applying fuzzy matching on a tremendous number of symbols and allow -// supplementary retrieval for short queries. -// -// Short names (total segment length <3 characters) are currently ignored. +/// This is used to mark unigrams and bigrams and distinct them from complete +/// trigrams. Since '$' is not present in valid identifier names, it is safe to +/// use it as the special symbol. +static const char END_MARKER = '$'; + std::vector generateIdentifierTrigrams(llvm::StringRef Identifier) { // Apply fuzzy matching text segmentation. std::vector Roles(Identifier.size()); @@ -50,17 +47,45 @@ std::vector generateIdentifierTrigrams(llvm::StringRef Identifier) { // not available then 0 is stored. std::vector> Next(LowercaseIdentifier.size()); unsigned NextTail = 0, NextHead = 0, NextNextHead = 0; + // Store two first HEAD characters in the identifier (if present). + std::deque TwoHeads; for (int I = LowercaseIdentifier.size() - 1; I >= 0; --I) { Next[I] = {{NextTail, NextHead, NextNextHead}}; NextTail = Roles[I] == Tail ? I : 0; if (Roles[I] == Head) { NextNextHead = NextHead; NextHead = I; + TwoHeads.push_front(LowercaseIdentifier[I]); + if (TwoHeads.size() > 2) + TwoHeads.pop_back(); } } DenseSet UniqueTrigrams; - std::array Chars; + + auto add = [&](std::string Chars) { + UniqueTrigrams.insert(Token(Token::Kind::Trigram, Chars)); + }; + + // FIXME(kbobyrev): Instead of producing empty trigram for each identifier, + // just use True Iterator on the query side when the query string is empty. + add({{END_MARKER, END_MARKER, END_MARKER}}); + + if (TwoHeads.size() == 2) + add({{TwoHeads.front(), TwoHeads.back(), END_MARKER}}); + + if (!LowercaseIdentifier.empty()) + add({{LowercaseIdentifier.front(), END_MARKER, END_MARKER}}); + + if (LowercaseIdentifier.size() >= 2) + add({{LowercaseIdentifier[0], LowercaseIdentifier[1], END_MARKER}}); + + if (LowercaseIdentifier.size() >= 3) + add({{LowercaseIdentifier[0], LowercaseIdentifier[1], + LowercaseIdentifier[2]}}); + + // Iterate through valid seqneces of three characters Fuzzy Matcher can + // process. for (size_t I = 0; I < LowercaseIdentifier.size(); ++I) { // Skip delimiters. if (Roles[I] != Head && Roles[I] != Tail) @@ -71,13 +96,8 @@ std::vector generateIdentifierTrigrams(llvm::StringRef Identifier) { for (const unsigned K : Next[J]) { if (!K) continue; - Chars = {{LowercaseIdentifier[I], LowercaseIdentifier[J], - LowercaseIdentifier[K], 0}}; - auto Trigram = Token(Token::Kind::Trigram, Chars.data()); - // Push unique trigrams to the result. - if (!UniqueTrigrams.count(Trigram)) { - UniqueTrigrams.insert(Trigram); - } + add({{LowercaseIdentifier[I], LowercaseIdentifier[J], + LowercaseIdentifier[K]}}); } } } @@ -89,33 +109,43 @@ std::vector generateIdentifierTrigrams(llvm::StringRef Identifier) { return Result; } -// FIXME(kbobyrev): Similarly, to generateIdentifierTrigrams, this ignores short -// inputs (total segment length <3 characters). std::vector generateQueryTrigrams(llvm::StringRef Query) { // Apply fuzzy matching text segmentation. std::vector Roles(Query.size()); calculateRoles(Query, llvm::makeMutableArrayRef(Roles.data(), Query.size())); + // Additional pass is necessary to count valid identifier characters. + // Depending on that, this function might return incomplete trigram. + unsigned ValidSymbolsCount = 0; + for (size_t I = 0; I < Roles.size(); ++I) + if (Roles[I] == Head || Roles[I] == Tail) + ++ValidSymbolsCount; + std::string LowercaseQuery = Query.lower(); DenseSet UniqueTrigrams; - std::deque Chars; - for (size_t I = 0; I < LowercaseQuery.size(); ++I) { - // If current symbol is delimiter, just skip it. - if (Roles[I] != Head && Roles[I] != Tail) - continue; + // If the number of symbols which can form fuzzy matching trigram is not + // sufficient, generate a single incomplete trigram for query. + if (ValidSymbolsCount < 3) { + std::string Chars = LowercaseQuery.substr(0, std::min(3UL, Query.size())); + Chars.append(3 - Chars.size(), END_MARKER); + UniqueTrigrams.insert(Token(Token::Kind::Trigram, Chars)); + } else { + std::deque Chars; + for (size_t I = 0; I < LowercaseQuery.size(); ++I) { + // If current symbol is delimiter, just skip it. + if (Roles[I] != Head && Roles[I] != Tail) + continue; + + Chars.push_back(LowercaseQuery[I]); - Chars.push_back(LowercaseQuery[I]); + if (Chars.size() > 3) + Chars.pop_front(); - if (Chars.size() > 3) - Chars.pop_front(); - if (Chars.size() == 3) { - auto Trigram = - Token(Token::Kind::Trigram, std::string(begin(Chars), end(Chars))); - // Push unique trigrams to the result. - if (!UniqueTrigrams.count(Trigram)) { - UniqueTrigrams.insert(Trigram); + if (Chars.size() == 3) { + UniqueTrigrams.insert( + Token(Token::Kind::Trigram, std::string(begin(Chars), end(Chars)))); } } } diff --git a/clangd/index/dex/Trigram.h b/clangd/index/dex/Trigram.h index 0e21c7d13..00614edf5 100644 --- a/clangd/index/dex/Trigram.h +++ b/clangd/index/dex/Trigram.h @@ -36,14 +36,20 @@ namespace dex { /// First, given Identifier (unqualified symbol name) is segmented using /// FuzzyMatch API and lowercased. After segmentation, the following technique /// is applied for generating trigrams: for each letter or digit in the input -/// string the algorithms looks for the possible next and skip-1-next symbols +/// string the algorithms looks for the possible next and skip-1-next characters /// which can be jumped to during fuzzy matching. Each combination of such three -/// symbols is inserted into the result. +/// characters is inserted into the result. /// /// Trigrams can start at any character in the input. Then we can choose to move /// to the next character, move to the start of the next segment, or skip over a /// segment. /// +/// This also generates incomplete trigrams for short query scenarios: +/// * Empty trigram: "$$$". +/// * Unigram: the first character of the identifier. +/// * Bigrams: a 2-char prefix of the identifier and a bigram of the first two +/// HEAD characters (if they exist). +// /// Note: the returned list of trigrams does not have duplicates, if any trigram /// belongs to more than one class it is only inserted once. std::vector generateIdentifierTrigrams(llvm::StringRef Identifier); @@ -53,6 +59,10 @@ std::vector generateIdentifierTrigrams(llvm::StringRef Identifier); /// Query is segmented using FuzzyMatch API and downcasted to lowercase. Then, /// the simplest trigrams - sequences of three consecutive letters and digits /// are extracted and returned after deduplication. +/// +/// For short queries (less than 3 characters with Head or Tail roles in Fuzzy +/// Matching segmentation) this returns a single trigram with the first +/// characters (up to 3) to perfrom prefix match. std::vector generateQueryTrigrams(llvm::StringRef Query); } // namespace dex diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index d5db97ce1..f23192fbe 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -271,45 +271,57 @@ trigramsAre(std::initializer_list Trigrams) { } TEST(DexIndexTrigrams, IdentifierTrigrams) { - EXPECT_THAT(generateIdentifierTrigrams("X86"), trigramsAre({"x86"})); + EXPECT_THAT(generateIdentifierTrigrams("X86"), + trigramsAre({"x86", "x$$", "x8$", "$$$"})); - EXPECT_THAT(generateIdentifierTrigrams("nl"), trigramsAre({})); + EXPECT_THAT(generateIdentifierTrigrams("nl"), + trigramsAre({"nl$", "n$$", "$$$"})); + + EXPECT_THAT(generateIdentifierTrigrams("n"), trigramsAre({"n$$", "$$$"})); EXPECT_THAT(generateIdentifierTrigrams("clangd"), - trigramsAre({"cla", "lan", "ang", "ngd"})); + trigramsAre({"c$$", "cl$", "cla", "lan", "ang", "ngd", "$$$"})); EXPECT_THAT(generateIdentifierTrigrams("abc_def"), - trigramsAre({"abc", "abd", "ade", "bcd", "bde", "cde", "def"})); + trigramsAre({"a$$", "abc", "abd", "ade", "bcd", "bde", "cde", + "def", "ab$", "ad$", "$$$"})); - EXPECT_THAT( - generateIdentifierTrigrams("a_b_c_d_e_"), - trigramsAre({"abc", "abd", "acd", "ace", "bcd", "bce", "bde", "cde"})); + EXPECT_THAT(generateIdentifierTrigrams("a_b_c_d_e_"), + trigramsAre({"a$$", "a_$", "a_b", "abc", "abd", "acd", "ace", + "bcd", "bce", "bde", "cde", "ab$", "$$$"})); - EXPECT_THAT( - generateIdentifierTrigrams("unique_ptr"), - trigramsAre({"uni", "unp", "upt", "niq", "nip", "npt", "iqu", "iqp", - "ipt", "que", "qup", "qpt", "uep", "ept", "ptr"})); + EXPECT_THAT(generateIdentifierTrigrams("unique_ptr"), + trigramsAre({"u$$", "uni", "unp", "upt", "niq", "nip", "npt", + "iqu", "iqp", "ipt", "que", "qup", "qpt", "uep", + "ept", "ptr", "un$", "up$", "$$$"})); EXPECT_THAT(generateIdentifierTrigrams("TUDecl"), - trigramsAre({"tud", "tde", "ude", "dec", "ecl"})); + trigramsAre({"t$$", "tud", "tde", "ude", "dec", "ecl", "tu$", + "td$", "$$$"})); EXPECT_THAT(generateIdentifierTrigrams("IsOK"), - trigramsAre({"iso", "iok", "sok"})); - - EXPECT_THAT(generateIdentifierTrigrams("abc_defGhij__klm"), - trigramsAre({ - "abc", "abd", "abg", "ade", "adg", "adk", "agh", "agk", "bcd", - "bcg", "bde", "bdg", "bdk", "bgh", "bgk", "cde", "cdg", "cdk", - "cgh", "cgk", "def", "deg", "dek", "dgh", "dgk", "dkl", "efg", - "efk", "egh", "egk", "ekl", "fgh", "fgk", "fkl", "ghi", "ghk", - "gkl", "hij", "hik", "hkl", "ijk", "ikl", "jkl", "klm", - })); + trigramsAre({"i$$", "iso", "iok", "sok", "is$", "io$", "$$$"})); + + EXPECT_THAT( + generateIdentifierTrigrams("abc_defGhij__klm"), + trigramsAre({"a$$", "abc", "abd", "abg", "ade", "adg", "adk", "agh", + "agk", "bcd", "bcg", "bde", "bdg", "bdk", "bgh", "bgk", + "cde", "cdg", "cdk", "cgh", "cgk", "def", "deg", "dek", + "dgh", "dgk", "dkl", "efg", "efk", "egh", "egk", "ekl", + "fgh", "fgk", "fkl", "ghi", "ghk", "gkl", "hij", "hik", + "hkl", "ijk", "ikl", "jkl", "klm", "ab$", "ad$", "$$$"})); } TEST(DexIndexTrigrams, QueryTrigrams) { - EXPECT_THAT(generateQueryTrigrams("X86"), trigramsAre({"x86"})); + EXPECT_THAT(generateQueryTrigrams("c"), trigramsAre({"c$$"})); + EXPECT_THAT(generateQueryTrigrams("cl"), trigramsAre({"cl$"})); + EXPECT_THAT(generateQueryTrigrams("cla"), trigramsAre({"cla"})); - EXPECT_THAT(generateQueryTrigrams("nl"), trigramsAre({})); + EXPECT_THAT(generateQueryTrigrams("_"), trigramsAre({"_$$"})); + EXPECT_THAT(generateQueryTrigrams("__"), trigramsAre({"__$"})); + EXPECT_THAT(generateQueryTrigrams("___"), trigramsAre({"___"})); + + EXPECT_THAT(generateQueryTrigrams("X86"), trigramsAre({"x86"})); EXPECT_THAT(generateQueryTrigrams("clangd"), trigramsAre({"cla", "lan", "ang", "ngd"})); From 4283deeb9c2518c45d78e7d5f39665d0890e9405 Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Mon, 13 Aug 2018 12:24:48 +0000 Subject: [PATCH 034/686] Fix MSVC 'std::min: no matching overloaded function found' error. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339557 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Trigram.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clangd/index/dex/Trigram.cpp b/clangd/index/dex/Trigram.cpp index 370ca29ea..af422eafe 100644 --- a/clangd/index/dex/Trigram.cpp +++ b/clangd/index/dex/Trigram.cpp @@ -128,7 +128,8 @@ std::vector generateQueryTrigrams(llvm::StringRef Query) { // If the number of symbols which can form fuzzy matching trigram is not // sufficient, generate a single incomplete trigram for query. if (ValidSymbolsCount < 3) { - std::string Chars = LowercaseQuery.substr(0, std::min(3UL, Query.size())); + std::string Chars = + LowercaseQuery.substr(0, std::min(3UL, Query.size())); Chars.append(3 - Chars.size(), END_MARKER); UniqueTrigrams.insert(Token(Token::Kind::Trigram, Chars)); } else { From 3adbe986acabc6538e65958f02baef40e07ccf95 Mon Sep 17 00:00:00 2001 From: Martin Bohme Date: Mon, 13 Aug 2018 14:24:52 +0000 Subject: [PATCH 035/686] [clang-tidy] Recognize [[clang::reinitializes]] attribute in bugprone-use-after-move Summary: This allows member functions to be marked as reinitializing the object. After a moved-from object has been reinitialized, the check will no longer consider it to be in an indeterminate state. The patch that adds the attribute itself is at https://reviews.llvm.org/D49911 Reviewers: ilya-biryukov, aaron.ballman, alexfh, hokein, rsmith Reviewed By: aaron.ballman Subscribers: dblaikie, xazax.hun, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D49910 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339571 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/bugprone/UseAfterMoveCheck.cpp | 4 +++ .../checks/bugprone-use-after-move.rst | 3 ++ test/clang-tidy/bugprone-use-after-move.cpp | 35 +++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tidy/bugprone/UseAfterMoveCheck.cpp index 6fa4cab3c..ea6454920 100644 --- a/clang-tidy/bugprone/UseAfterMoveCheck.cpp +++ b/clang-tidy/bugprone/UseAfterMoveCheck.cpp @@ -308,6 +308,10 @@ void UseAfterMoveFinder::getReinits( cxxMemberCallExpr( on(allOf(DeclRefMatcher, StandardSmartPointerTypeMatcher)), callee(cxxMethodDecl(hasName("reset")))), + // Methods that have the [[clang::reinitializes]] attribute. + cxxMemberCallExpr( + on(DeclRefMatcher), + callee(cxxMethodDecl(hasAttr(clang::attr::Reinitializes)))), // Passing variable to a function as a non-const pointer. callExpr(forEachArgumentWithParam( unaryOperator(hasOperatorName("&"), diff --git a/docs/clang-tidy/checks/bugprone-use-after-move.rst b/docs/clang-tidy/checks/bugprone-use-after-move.rst index 05b0e094d..ac16a0a21 100644 --- a/docs/clang-tidy/checks/bugprone-use-after-move.rst +++ b/docs/clang-tidy/checks/bugprone-use-after-move.rst @@ -178,6 +178,9 @@ The check considers a variable to be reinitialized in the following cases: - ``reset()`` is called on the variable and the variable is of type ``std::unique_ptr``, ``std::shared_ptr`` or ``std::weak_ptr``. + - A member function marked with the ``[[clang::reinitializes]]`` attribute is + called on the variable. + If the variable in question is a struct and an individual member variable of that struct is written to, the check does not consider this to be a reinitialization -- even if, eventually, all member variables of the struct are diff --git a/test/clang-tidy/bugprone-use-after-move.cpp b/test/clang-tidy/bugprone-use-after-move.cpp index 9307f17f4..2d747b47c 100644 --- a/test/clang-tidy/bugprone-use-after-move.cpp +++ b/test/clang-tidy/bugprone-use-after-move.cpp @@ -107,6 +107,15 @@ class A { int i; }; +template +class AnnotatedContainer { +public: + AnnotatedContainer(); + + void foo() const; + [[clang::reinitializes]] void clear(); +}; + //////////////////////////////////////////////////////////////////////////////// // General tests. @@ -898,6 +907,32 @@ void standardSmartPointerResetIsReinit() { } } +void reinitAnnotation() { + { + AnnotatedContainer obj; + std::move(obj); + obj.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'obj' used after it was + // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + } + { + AnnotatedContainer obj; + std::move(obj); + obj.clear(); + obj.foo(); + } + { + // Calling clear() on a different object to the one that was moved is not + // considered a reinitialization. + AnnotatedContainer obj1, obj2; + std::move(obj1); + obj2.clear(); + obj1.foo(); + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'obj1' used after it was + // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here + } +} + //////////////////////////////////////////////////////////////////////////////// // Tests related to order of evaluation within expressions From 43afd58496558a361d360325743ee86c4f70569f Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Mon, 13 Aug 2018 14:32:19 +0000 Subject: [PATCH 036/686] Fix lint tests for D50449 Reviewers: ilya-biryukov, hokein Reviewed By: hokein Subscribers: hokein, ioeric, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50635 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339572 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clangd/completion-snippets.test | 13 +++++++++++++ test/clangd/completion.test | 26 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/test/clangd/completion-snippets.test b/test/clangd/completion-snippets.test index 06896319e..965ae587f 100644 --- a/test/clangd/completion-snippets.test +++ b/test/clangd/completion-snippets.test @@ -34,6 +34,19 @@ # CHECK-NEXT: "kind": 3, # CHECK-NEXT: "label": " func_with_args(int a, int b)", # CHECK-NEXT: "sortText": "{{.*}}func_with_args" +# CHECK-NEXT: "textEdit": { +# CHECK-NEXT: "newText": "func_with_args(${1:int a}, ${2:int b})", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 7, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 0, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } # CHECK-NEXT: } # CHECK-NEXT: ] # CHECK-NEXT: } diff --git a/test/clangd/completion.test b/test/clangd/completion.test index c679e3283..2283059e4 100644 --- a/test/clangd/completion.test +++ b/test/clangd/completion.test @@ -18,6 +18,19 @@ # CHECK-NEXT: "kind": 5, # CHECK-NEXT: "label": " a", # CHECK-NEXT: "sortText": "{{.*}}a" +# CHECK-NEXT: "textEdit": { +# CHECK-NEXT: "newText": "a", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 4, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 4, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } # CHECK-NEXT: } # CHECK-NEXT: ] --- @@ -38,6 +51,19 @@ # CHECK-NEXT: "kind": 5, # CHECK-NEXT: "label": " b", # CHECK-NEXT: "sortText": "{{.*}}b" +# CHECK-NEXT: "textEdit": { +# CHECK-NEXT: "newText": "b", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 4, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 4, +# CHECK-NEXT: "line": 2 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } # CHECK-NEXT: } # CHECK-NEXT: ] --- From 1a719634e0e3fd74fd723b94a6a122e8e1afffcb Mon Sep 17 00:00:00 2001 From: Eugene Zelenko Date: Mon, 13 Aug 2018 17:55:48 +0000 Subject: [PATCH 037/686] [Documentation] Remove unnecessary placeholder, grammar fix in Release Notes. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339590 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/ReleaseNotes.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index a316466f3..83d5e017c 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -57,12 +57,10 @@ The improvements are... Improvements to clang-tidy -------------------------- -The improvements are... - - New :doc:`readability-magic-numbers ` check. - Detect usage of magic numbers, numbers that are used as literals instead of + Detects usage of magic numbers, numbers that are used as literals instead of introduced via constants or symbols. Improvements to include-fixer From 3ddff633fa8c16cb1a02ccf0301012ffaeda48e5 Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Mon, 13 Aug 2018 18:05:50 +0000 Subject: [PATCH 038/686] [clang-doc] Pass over function-internal declarations git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339592 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/Mapper.cpp | 4 + test/clang-doc/bc-linkage.cpp | 912 +++++++++++++----------------- test/clang-doc/bc-record.cpp | 216 ++++--- test/clang-doc/mapper-linkage.cpp | 267 +++------ test/clang-doc/mapper-record.cpp | 178 +++--- test/clang-doc/yaml-linkage.cpp | 237 +++----- test/clang-doc/yaml-record.cpp | 24 +- 7 files changed, 713 insertions(+), 1125 deletions(-) diff --git a/clang-doc/Mapper.cpp b/clang-doc/Mapper.cpp index 71e940476..6131a353e 100644 --- a/clang-doc/Mapper.cpp +++ b/clang-doc/Mapper.cpp @@ -29,6 +29,10 @@ template bool MapASTVisitor::mapDecl(const T *D) { if (D->getASTContext().getSourceManager().isInSystemHeader(D->getLocation())) return true; + // Skip function-internal decls. + if (const DeclContext *F = D->getParentFunctionOrMethod()) + return true; + llvm::SmallString<128> USR; // If there is an error generating a USR for the decl, skip this decl. if (index::generateUSRForDecl(D, USR)) diff --git a/test/clang-doc/bc-linkage.cpp b/test/clang-doc/bc-linkage.cpp index 8fec0d351..9440798c7 100644 --- a/test/clang-doc/bc-linkage.cpp +++ b/test/clang-doc/bc-linkage.cpp @@ -97,72 +97,118 @@ inline void anonInlineFunction(); // RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/C9B3B71ACDD84C5BB320D34E97677715CDB3EA32.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/8960B5C9247D6F5C532756E53A1AD1240FA2146F.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'InnerClass' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'named' +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'inlinedFunctionWithInnerClass' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'innerPublicMethod' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'namedFunction' +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'InnerClass' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'named' +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'namedStaticFunction' +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'inlinedFunctionWithInnerClass' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'named' +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'void' +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'namedInlineFunction' +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'InnerClass' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'named' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/8960B5C9247D6F5C532756E53A1AD1240FA2146F.bc | FileCheck %s --check-prefix CHECK-1 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/7CDD73DCD6CD72F7E5CE25502810A182C66C4B45.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'named' -// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'Class' +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'int' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'publicField' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'int' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'protectedField' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'int' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'privateField' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'namedFunction' +// CHECK-1-NEXT: blob data = 'publicMethod' // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'named' -// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'Class' +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'Class' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'void' @@ -170,16 +216,23 @@ inline void anonInlineFunction(); // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'namedStaticFunction' +// CHECK-1-NEXT: blob data = 'protectedMethod' // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'named' -// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'Class' +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'Class' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'void' @@ -187,16 +240,23 @@ inline void anonInlineFunction(); // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'namedInlineFunction' +// CHECK-1-NEXT: blob data = 'privateMethod' // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'named' -// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'Class' +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'Class' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'void' @@ -204,104 +264,184 @@ inline void anonInlineFunction(); // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT:
-// RUN: llvm-bcanalyzer --dump %t/docs/bc/74A031CBE68C101F3E83F60ED17F20C11EC19D48.bc | FileCheck %s --check-prefix CHECK-2 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'InnerClass' -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'function' +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'void' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'x' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'inlinedFunction' +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'x' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'functionWithInnerClass' +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'x' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'inlinedFunctionWithInnerClass' +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'x' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'innerPublicMethod' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'InnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'InnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'staticFunction' +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'void' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'x' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' +// CHECK-2-NEXT: blob data = '{{.*}}' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'x' +// CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/7CDD73DCD6CD72F7E5CE25502810A182C66C4B45.bc | FileCheck %s --check-prefix CHECK-3 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/4712C5FA37B298A25501D1033C619B65B0ECC449.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' -// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: blob data = 'NamedClass' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'named' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'int' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'publicField' +// CHECK-3-NEXT: blob data = 'namedPublicField' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'int' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'protectedField' +// CHECK-3-NEXT: blob data = 'namedProtectedField' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'int' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'privateField' +// CHECK-3-NEXT: blob data = 'namedPrivateField' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'publicMethod' -// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'namedPublicMethod' +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: blob data = 'named' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: @@ -312,20 +452,26 @@ inline void anonInlineFunction(); // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'protectedMethod' -// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'namedProtectedMethod' +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: blob data = 'named' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: @@ -336,20 +482,26 @@ inline void anonInlineFunction(); // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'privateMethod' -// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'namedPrivateMethod' +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: blob data = 'named' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: @@ -362,483 +514,187 @@ inline void anonInlineFunction(); // CHECK-3-NEXT: // CHECK-3-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/85427901413EC77C961019EBB3ADEF7B0BAAFE78.bc | FileCheck %s --check-prefix CHECK-4 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/6E8FB72A89761E77020BFCEE9A9A6E64B15CC2A9.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'InnerClass' -// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'AnonClass' +// CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'functionWithInnerClass' -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: blob data = '{{.*}}' // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'int' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'anonPublicField' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'int' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'anonProtectedField' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'int' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'anonPrivateField' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'anonPublicMethod' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'AnonClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'AnonClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'void' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'anonProtectedMethod' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'AnonClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'AnonClass' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'void' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'innerPublicMethod' +// CHECK-4-NEXT: blob data = 'anonPrivateMethod' // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: blob data = 'AnonClass' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'functionWithInnerClass' -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: blob data = '{{.*}}' // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: blob data = 'AnonClass' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'int' +// CHECK-4-NEXT: blob data = 'void' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/83CC52D32583E0771710A7742DE81C839E953AC8.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'function' -// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: blob data = 'anonFunction' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = '{{.*}}' // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'void' // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'inlinedFunction' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'functionWithInnerClass' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'inlinedFunctionWithInnerClass' -// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: blob data = 'anonStaticFunction' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = '{{.*}}' // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: blob data = 'void' // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'staticFunction' -// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: blob data = 'anonInlineFunction' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = '{{.*}}' // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'void' // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'staticFunctionWithInnerClass' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/4712C5FA37B298A25501D1033C619B65B0ECC449.bc | FileCheck %s --check-prefix CHECK-6 -// CHECK-6: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'named' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'int' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedPublicField' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'int' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedProtectedField' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'int' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedPrivateField' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedPublicMethod' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'named' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'void' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedProtectedMethod' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'named' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'void' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedPrivateMethod' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'named' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'void' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/6E8FB72A89761E77020BFCEE9A9A6E64B15CC2A9.bc | FileCheck %s --check-prefix CHECK-7 -// CHECK-7: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'int' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonPublicField' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'int' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonProtectedField' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'int' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonPrivateField' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonPublicMethod' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'void' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonProtectedMethod' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'void' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonPrivateMethod' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'void' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/83CC52D32583E0771710A7742DE81C839E953AC8.bc | FileCheck %s --check-prefix CHECK-8 -// CHECK-8: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'anonFunction' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'void' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'anonStaticFunction' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'void' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'anonInlineFunction' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'void' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: diff --git a/test/clang-doc/bc-record.cpp b/test/clang-doc/bc-record.cpp index a0e224485..053954ad3 100644 --- a/test/clang-doc/bc-record.cpp +++ b/test/clang-doc/bc-record.cpp @@ -127,167 +127,149 @@ class X { // CHECK-0-NEXT: // CHECK-0-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/3FB542274573CAEAD54CEBFFCAEE3D77FB9713D8.bc | FileCheck %s --check-prefix CHECK-1 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'I' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'H' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: blob data = 'X' +// CHECK-1-NEXT: blob data = '{{.*}}' // CHECK-1-NEXT: // CHECK-1-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-2 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'X' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'C' +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'i' +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-3 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'C' -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'int' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'i' -// CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Y' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'X' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: // CHECK-3-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-4 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'Y' -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'X' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'H' +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'void' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'B' +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: blob data = 'X' +// CHECK-4-NEXT: blob data = 'Y' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'Bc' +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'A' +// CHECK-4-NEXT: blob data = 'B' +// CHECK-4-NEXT: +// CHECK-4-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'H' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'void' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'B' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: blob data = 'X' -// CHECK-5-NEXT: blob data = 'Y' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'Bc' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'A' -// CHECK-5-NEXT: blob data = 'B' -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'D' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-6 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-6 // CHECK-6: // CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: +// CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'D' -// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: blob data = 'F' +// CHECK-6-NEXT: blob data = '{{.*}}' // CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'E' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'D' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: // CHECK-6-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-7 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-7 // CHECK-7: // CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'F' -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'E' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'D' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'A' +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'int' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'X' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'int' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'Y' +// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-8 -// CHECK-8: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'A' -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'int' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'X' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'int' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'Y' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: diff --git a/test/clang-doc/mapper-linkage.cpp b/test/clang-doc/mapper-linkage.cpp index 5b4fe7df3..8d6b238b0 100644 --- a/test/clang-doc/mapper-linkage.cpp +++ b/test/clang-doc/mapper-linkage.cpp @@ -97,62 +97,56 @@ inline void anonInlineFunction(); // RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs -// RUN: llvm-bcanalyzer --dump %t/docs/bc/C9B3B71ACDD84C5BB320D34E97677715CDB3EA32.bc | FileCheck %s --check-prefix CHECK-0 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/8960B5C9247D6F5C532756E53A1AD1240FA2146F.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'innerPublicMethod' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'namedInlineFunction' +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'InnerClass' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = 'named' +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'inlinedFunctionWithInnerClass' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'InnerClass' -// CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = 'int' +// CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/8960B5C9247D6F5C532756E53A1AD1240FA2146F.bc | FileCheck %s --check-prefix CHECK-1 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/7CDD73DCD6CD72F7E5CE25502810A182C66C4B45.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'namedInlineFunction' +// CHECK-1-NEXT: blob data = 'privateMethod' // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'named' -// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'Class' +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = 'Class' +// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'void' @@ -160,68 +154,61 @@ inline void anonInlineFunction(); // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/74A031CBE68C101F3E83F60ED17F20C11EC19D48.bc | FileCheck %s --check-prefix CHECK-2 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'innerPublicMethod' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'InnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'InnerClass' -// CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' +// CHECK-2-NEXT: blob data = '{{.*}}' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'x' +// CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/7CDD73DCD6CD72F7E5CE25502810A182C66C4B45.bc | FileCheck %s --check-prefix CHECK-3 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/4712C5FA37B298A25501D1033C619B65B0ECC449.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'privateMethod' -// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'namedPrivateMethod' +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Class' +// CHECK-3-NEXT: blob data = 'named' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: @@ -234,169 +221,65 @@ inline void anonInlineFunction(); // CHECK-3-NEXT: // CHECK-3-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/85427901413EC77C961019EBB3ADEF7B0BAAFE78.bc | FileCheck %s --check-prefix CHECK-4 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/6E8FB72A89761E77020BFCEE9A9A6E64B15CC2A9.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'innerPublicMethod' +// CHECK-4-NEXT: blob data = 'anonPrivateMethod' // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: blob data = 'AnonClass' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'functionWithInnerClass' -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: blob data = '{{.*}}' // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'InnerClass' +// CHECK-4-NEXT: blob data = 'AnonClass' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'int' +// CHECK-4-NEXT: blob data = 'void' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/83CC52D32583E0771710A7742DE81C839E953AC8.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'staticFunctionWithInnerClass' -// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: blob data = 'anonInlineFunction' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = '{{.*}}' // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' +// CHECK-5-NEXT: blob data = 'void' // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'int' -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'x' -// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/4712C5FA37B298A25501D1033C619B65B0ECC449.bc | FileCheck %s --check-prefix CHECK-6 -// CHECK-6: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'namedPrivateMethod' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'named' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = '{{.*}}' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'NamedClass' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'void' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/6E8FB72A89761E77020BFCEE9A9A6E64B15CC2A9.bc | FileCheck %s --check-prefix CHECK-7 -// CHECK-7: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'anonPrivateMethod' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'AnonClass' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'void' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/83CC52D32583E0771710A7742DE81C839E953AC8.bc | FileCheck %s --check-prefix CHECK-8 -// CHECK-8: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'anonInlineFunction' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'void' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: diff --git a/test/clang-doc/mapper-record.cpp b/test/clang-doc/mapper-record.cpp index dbabd8fdd..9f699219d 100644 --- a/test/clang-doc/mapper-record.cpp +++ b/test/clang-doc/mapper-record.cpp @@ -75,146 +75,128 @@ class X { // CHECK-0-NEXT: // CHECK-0-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/3FB542274573CAEAD54CEBFFCAEE3D77FB9713D8.bc | FileCheck %s --check-prefix CHECK-1 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'I' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = 'H' -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: blob data = 'X' +// CHECK-1-NEXT: blob data = '{{.*}}' // CHECK-1-NEXT: // CHECK-1-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-2 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: blob data = 'X' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'C' +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'int' +// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = 'i' +// CHECK-2-NEXT: // CHECK-2-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-3 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'C' -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'int' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'i' -// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'Y' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = 'X' +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: // CHECK-3-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-4 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'Y' -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'X' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 +// CHECK-4-NEXT: blob data = 'Bc' +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'A' +// CHECK-4-NEXT: blob data = 'B' +// CHECK-4-NEXT: +// CHECK-4-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'Bc' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'A' -// CHECK-5-NEXT: blob data = 'B' -// CHECK-5-NEXT: -// CHECK-5-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-6 +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'D' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-6 // CHECK-6: // CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: +// CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'D' -// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: blob data = 'F' +// CHECK-6-NEXT: blob data = '{{.*}}' // CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'E' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: blob data = 'D' +// CHECK-6-NEXT: +// CHECK-6-NEXT: +// CHECK-6-NEXT: // CHECK-6-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-7 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-7 // CHECK-7: // CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'F' -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'E' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'D' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'A' +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'int' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'X' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'int' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'Y' +// CHECK-7-NEXT: // CHECK-7-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-8 -// CHECK-8: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'A' -// CHECK-8-NEXT: blob data = '{{.*}}' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'int' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'X' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'int' -// CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: blob data = 'Y' -// CHECK-8-NEXT: -// CHECK-8-NEXT: diff --git a/test/clang-doc/yaml-linkage.cpp b/test/clang-doc/yaml-linkage.cpp index 3a0aa5bf9..fb7adcd78 100644 --- a/test/clang-doc/yaml-linkage.cpp +++ b/test/clang-doc/yaml-linkage.cpp @@ -336,194 +336,89 @@ inline void anonInlineFunction(); // CHECK-3-NEXT: Name: 'void' // CHECK-3-NEXT: ... -// RUN: cat %t/docs/staticFunctionWithInnerClass/InnerClass.yaml | FileCheck %s --check-prefix CHECK-4 +// RUN: cat %t/docs/named/NamedClass.yaml | FileCheck %s --check-prefix CHECK-4 // CHECK-4: --- // CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: Name: 'InnerClass' +// CHECK-4-NEXT: Name: 'NamedClass' // CHECK-4-NEXT: Namespace: -// CHECK-4-NEXT: - Type: Function -// CHECK-4-NEXT: Name: 'staticFunctionWithInnerClass' +// CHECK-4-NEXT: - Type: Namespace +// CHECK-4-NEXT: Name: 'named' // CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-4-NEXT: DefLocation: -// CHECK-4-NEXT: LineNumber: 69 +// CHECK-4-NEXT: LineNumber: 47 // CHECK-4-NEXT: Filename: 'test' // CHECK-4-NEXT: TagType: Class +// CHECK-4-NEXT: Members: +// CHECK-4-NEXT: - Type: +// CHECK-4-NEXT: Name: 'int' +// CHECK-4-NEXT: Name: 'namedPublicField' +// CHECK-4-NEXT: - Type: +// CHECK-4-NEXT: Name: 'int' +// CHECK-4-NEXT: Name: 'namedProtectedField' +// CHECK-4-NEXT: Access: Protected +// CHECK-4-NEXT: - Type: +// CHECK-4-NEXT: Name: 'int' +// CHECK-4-NEXT: Name: 'namedPrivateField' +// CHECK-4-NEXT: Access: Private // CHECK-4-NEXT: ChildFunctions: // CHECK-4-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: Name: 'innerPublicMethod' +// CHECK-4-NEXT: Name: 'namedPublicMethod' // CHECK-4-NEXT: Namespace: // CHECK-4-NEXT: - Type: Record -// CHECK-4-NEXT: Name: 'InnerClass' +// CHECK-4-NEXT: Name: 'NamedClass' // CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: - Type: Function -// CHECK-4-NEXT: Name: 'staticFunctionWithInnerClass' +// CHECK-4-NEXT: - Type: Namespace +// CHECK-4-NEXT: Name: 'named' // CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: DefLocation: -// CHECK-4-NEXT: LineNumber: 71 -// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: Location: +// CHECK-4-NEXT: - LineNumber: 49 +// CHECK-4-NEXT: Filename: 'test' // CHECK-4-NEXT: IsMethod: true // CHECK-4-NEXT: Parent: // CHECK-4-NEXT: Type: Record -// CHECK-4-NEXT: Name: 'InnerClass' +// CHECK-4-NEXT: Name: 'NamedClass' // CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-4-NEXT: ReturnType: // CHECK-4-NEXT: Type: -// CHECK-4-NEXT: Name: 'int' +// CHECK-4-NEXT: Name: 'void' +// CHECK-4-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: 'namedProtectedMethod' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Record +// CHECK-4-NEXT: Name: 'NamedClass' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: - Type: Namespace +// CHECK-4-NEXT: Name: 'named' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Location: +// CHECK-4-NEXT: - LineNumber: 53 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: IsMethod: true +// CHECK-4-NEXT: Parent: +// CHECK-4-NEXT: Type: Record +// CHECK-4-NEXT: Name: 'NamedClass' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: ReturnType: +// CHECK-4-NEXT: Type: +// CHECK-4-NEXT: Name: 'void' +// CHECK-4-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: 'namedPrivateMethod' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Record +// CHECK-4-NEXT: Name: 'NamedClass' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: - Type: Namespace +// CHECK-4-NEXT: Name: 'named' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Location: +// CHECK-4-NEXT: - LineNumber: 57 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: IsMethod: true +// CHECK-4-NEXT: Parent: +// CHECK-4-NEXT: Type: Record +// CHECK-4-NEXT: Name: 'NamedClass' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: ReturnType: +// CHECK-4-NEXT: Type: +// CHECK-4-NEXT: Name: 'void' // CHECK-4-NEXT: ... - -// RUN: cat %t/docs/named/NamedClass.yaml | FileCheck %s --check-prefix CHECK-5 -// CHECK-5: --- -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: Namespace: -// CHECK-5-NEXT: - Type: Namespace -// CHECK-5-NEXT: Name: 'named' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: DefLocation: -// CHECK-5-NEXT: LineNumber: 47 -// CHECK-5-NEXT: Filename: 'test' -// CHECK-5-NEXT: TagType: Class -// CHECK-5-NEXT: Members: -// CHECK-5-NEXT: - Type: -// CHECK-5-NEXT: Name: 'int' -// CHECK-5-NEXT: Name: 'namedPublicField' -// CHECK-5-NEXT: - Type: -// CHECK-5-NEXT: Name: 'int' -// CHECK-5-NEXT: Name: 'namedProtectedField' -// CHECK-5-NEXT: Access: Protected -// CHECK-5-NEXT: - Type: -// CHECK-5-NEXT: Name: 'int' -// CHECK-5-NEXT: Name: 'namedPrivateField' -// CHECK-5-NEXT: Access: Private -// CHECK-5-NEXT: ChildFunctions: -// CHECK-5-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Name: 'namedPublicMethod' -// CHECK-5-NEXT: Namespace: -// CHECK-5-NEXT: - Type: Record -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: - Type: Namespace -// CHECK-5-NEXT: Name: 'named' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Location: -// CHECK-5-NEXT: - LineNumber: 49 -// CHECK-5-NEXT: Filename: 'test' -// CHECK-5-NEXT: IsMethod: true -// CHECK-5-NEXT: Parent: -// CHECK-5-NEXT: Type: Record -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: ReturnType: -// CHECK-5-NEXT: Type: -// CHECK-5-NEXT: Name: 'void' -// CHECK-5-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Name: 'namedProtectedMethod' -// CHECK-5-NEXT: Namespace: -// CHECK-5-NEXT: - Type: Record -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: - Type: Namespace -// CHECK-5-NEXT: Name: 'named' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Location: -// CHECK-5-NEXT: - LineNumber: 53 -// CHECK-5-NEXT: Filename: 'test' -// CHECK-5-NEXT: IsMethod: true -// CHECK-5-NEXT: Parent: -// CHECK-5-NEXT: Type: Record -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: ReturnType: -// CHECK-5-NEXT: Type: -// CHECK-5-NEXT: Name: 'void' -// CHECK-5-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Name: 'namedPrivateMethod' -// CHECK-5-NEXT: Namespace: -// CHECK-5-NEXT: - Type: Record -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: - Type: Namespace -// CHECK-5-NEXT: Name: 'named' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Location: -// CHECK-5-NEXT: - LineNumber: 57 -// CHECK-5-NEXT: Filename: 'test' -// CHECK-5-NEXT: IsMethod: true -// CHECK-5-NEXT: Parent: -// CHECK-5-NEXT: Type: Record -// CHECK-5-NEXT: Name: 'NamedClass' -// CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: ReturnType: -// CHECK-5-NEXT: Type: -// CHECK-5-NEXT: Name: 'void' -// CHECK-5-NEXT: ... - -// RUN: cat %t/docs/functionWithInnerClass/InnerClass.yaml | FileCheck %s --check-prefix CHECK-6 -// CHECK-6: --- -// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'InnerClass' -// CHECK-6-NEXT: Namespace: -// CHECK-6-NEXT: - Type: Function -// CHECK-6-NEXT: Name: 'functionWithInnerClass' -// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 15 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: TagType: Class -// CHECK-6-NEXT: ChildFunctions: -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'innerPublicMethod' -// CHECK-6-NEXT: Namespace: -// CHECK-6-NEXT: - Type: Record -// CHECK-6-NEXT: Name: 'InnerClass' -// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: - Type: Function -// CHECK-6-NEXT: Name: 'functionWithInnerClass' -// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 17 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: IsMethod: true -// CHECK-6-NEXT: Parent: -// CHECK-6-NEXT: Type: Record -// CHECK-6-NEXT: Name: 'InnerClass' -// CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: ReturnType: -// CHECK-6-NEXT: Type: -// CHECK-6-NEXT: Name: 'int' -// CHECK-6-NEXT: ... - -// RUN: cat %t/docs/inlinedFunctionWithInnerClass/InnerClass.yaml | FileCheck %s --check-prefix CHECK-7 -// CHECK-7: --- -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: Name: 'InnerClass' -// CHECK-7-NEXT: Namespace: -// CHECK-7-NEXT: - Type: Function -// CHECK-7-NEXT: Name: 'inlinedFunctionWithInnerClass' -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: DefLocation: -// CHECK-7-NEXT: LineNumber: 24 -// CHECK-7-NEXT: Filename: 'test' -// CHECK-7-NEXT: TagType: Class -// CHECK-7-NEXT: ChildFunctions: -// CHECK-7-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: Name: 'innerPublicMethod' -// CHECK-7-NEXT: Namespace: -// CHECK-7-NEXT: - Type: Record -// CHECK-7-NEXT: Name: 'InnerClass' -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: - Type: Function -// CHECK-7-NEXT: Name: 'inlinedFunctionWithInnerClass' -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: DefLocation: -// CHECK-7-NEXT: LineNumber: 26 -// CHECK-7-NEXT: Filename: 'test' -// CHECK-7-NEXT: IsMethod: true -// CHECK-7-NEXT: Parent: -// CHECK-7-NEXT: Type: Record -// CHECK-7-NEXT: Name: 'InnerClass' -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: ReturnType: -// CHECK-7-NEXT: Type: -// CHECK-7-NEXT: Name: 'int' -// CHECK-7-NEXT: ... diff --git a/test/clang-doc/yaml-record.cpp b/test/clang-doc/yaml-record.cpp index 8fad22017..2006baa12 100644 --- a/test/clang-doc/yaml-record.cpp +++ b/test/clang-doc/yaml-record.cpp @@ -207,30 +207,16 @@ class X { // CHECK-6-NEXT: - 'B' // CHECK-6-NEXT: ... -// RUN: cat %t/docs/H/I.yaml | FileCheck %s --check-prefix CHECK-7 +// RUN: cat %t/docs/X/Y.yaml | FileCheck %s --check-prefix CHECK-7 // CHECK-7: --- // CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: Name: 'I' +// CHECK-7-NEXT: Name: 'Y' // CHECK-7-NEXT: Namespace: -// CHECK-7-NEXT: - Type: Function -// CHECK-7-NEXT: Name: 'H' +// CHECK-7-NEXT: - Type: Record +// CHECK-7-NEXT: Name: 'X' // CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-7-NEXT: DefLocation: -// CHECK-7-NEXT: LineNumber: 12 +// CHECK-7-NEXT: LineNumber: 39 // CHECK-7-NEXT: Filename: 'test' // CHECK-7-NEXT: TagType: Class // CHECK-7-NEXT: ... - -// RUN: cat %t/docs/X/Y.yaml | FileCheck %s --check-prefix CHECK-8 -// CHECK-8: --- -// CHECK-8-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-8-NEXT: Name: 'Y' -// CHECK-8-NEXT: Namespace: -// CHECK-8-NEXT: - Type: Record -// CHECK-8-NEXT: Name: 'X' -// CHECK-8-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-8-NEXT: DefLocation: -// CHECK-8-NEXT: LineNumber: 39 -// CHECK-8-NEXT: Filename: 'test' -// CHECK-8-NEXT: TagType: Class -// CHECK-8-NEXT: ... From e67dd0fe00f7ec0a36bb35cb8788973aa69a1b88 Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Mon, 13 Aug 2018 21:39:03 +0000 Subject: [PATCH 039/686] [clang-doc] Updating BitcodeReader to use llvm::Error git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339617 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/BitcodeReader.cpp | 447 ++++++++++++++++++++---------------- clang-doc/BitcodeReader.h | 18 +- 2 files changed, 257 insertions(+), 208 deletions(-) diff --git a/clang-doc/BitcodeReader.cpp b/clang-doc/BitcodeReader.cpp index 7acf107db..dee0930bc 100644 --- a/clang-doc/BitcodeReader.cpp +++ b/clang-doc/BitcodeReader.cpp @@ -10,6 +10,7 @@ #include "BitcodeReader.h" #include "llvm/ADT/IndexedMap.h" #include "llvm/ADT/Optional.h" +#include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" namespace clang { @@ -17,49 +18,53 @@ namespace doc { using Record = llvm::SmallVector; -bool decodeRecord(Record R, llvm::SmallVectorImpl &Field, - llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, llvm::SmallVectorImpl &Field, + llvm::StringRef Blob) { Field.assign(Blob.begin(), Blob.end()); - return true; + return llvm::Error::success(); } -bool decodeRecord(Record R, SymbolID &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, SymbolID &Field, llvm::StringRef Blob) { if (R[0] != BitCodeConstants::USRHashSize) - return false; + return llvm::make_error("Incorrect USR size.\n", + llvm::inconvertibleErrorCode()); // First position in the record is the length of the following array, so we // copy the following elements to the field. for (int I = 0, E = R[0]; I < E; ++I) Field[I] = R[I + 1]; - return true; + return llvm::Error::success(); } -bool decodeRecord(Record R, bool &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, bool &Field, llvm::StringRef Blob) { Field = R[0] != 0; - return true; + return llvm::Error::success(); } -bool decodeRecord(Record R, int &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, int &Field, llvm::StringRef Blob) { if (R[0] > INT_MAX) - return false; + return llvm::make_error("Integer too large to parse.\n", + llvm::inconvertibleErrorCode()); Field = (int)R[0]; - return true; + return llvm::Error::success(); } -bool decodeRecord(Record R, AccessSpecifier &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, AccessSpecifier &Field, + llvm::StringRef Blob) { switch (R[0]) { case AS_public: case AS_private: case AS_protected: case AS_none: Field = (AccessSpecifier)R[0]; - return true; + return llvm::Error::success(); default: - return false; + return llvm::make_error( + "Invalid value for AccessSpecifier.\n", llvm::inconvertibleErrorCode()); } } -bool decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) { switch (R[0]) { case TTK_Struct: case TTK_Interface: @@ -67,21 +72,23 @@ bool decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) { case TTK_Class: case TTK_Enum: Field = (TagTypeKind)R[0]; - return true; + return llvm::Error::success(); default: - return false; + return llvm::make_error( + "Invalid value for TagTypeKind.\n", llvm::inconvertibleErrorCode()); } } -bool decodeRecord(Record R, llvm::Optional &Field, - llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, llvm::Optional &Field, + llvm::StringRef Blob) { if (R[0] > INT_MAX) - return false; + return llvm::make_error("Integer too large to parse.\n", + llvm::inconvertibleErrorCode()); Field.emplace((int)R[0], Blob); - return true; + return llvm::Error::success(); } -bool decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) { switch (auto IT = static_cast(R[0])) { case InfoType::IT_namespace: case InfoType::IT_record: @@ -89,12 +96,13 @@ bool decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) { case InfoType::IT_default: case InfoType::IT_enum: Field = IT; - return true; + return llvm::Error::success(); } - return false; + return llvm::make_error("Invalid value for InfoType.\n", + llvm::inconvertibleErrorCode()); } -bool decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { switch (auto F = static_cast(R[0])) { case FieldId::F_namespace: case FieldId::F_parent: @@ -104,45 +112,51 @@ bool decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { case FieldId::F_child_record: case FieldId::F_default: Field = F; - return true; + return llvm::Error::success(); } - return false; + return llvm::make_error("Invalid value for FieldId.\n", + llvm::inconvertibleErrorCode()); } -bool decodeRecord(Record R, llvm::SmallVectorImpl> &Field, - llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, + llvm::SmallVectorImpl> &Field, + llvm::StringRef Blob) { Field.push_back(Blob); - return true; + return llvm::Error::success(); } -bool decodeRecord(Record R, llvm::SmallVectorImpl &Field, - llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, llvm::SmallVectorImpl &Field, + llvm::StringRef Blob) { if (R[0] > INT_MAX) - return false; + return llvm::make_error("Integer too large to parse.\n", + llvm::inconvertibleErrorCode()); Field.emplace_back((int)R[0], Blob); - return true; + return llvm::Error::success(); } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - const unsigned VersionNo) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + const unsigned VersionNo) { if (ID == VERSION && R[0] == VersionNo) - return true; - return false; + return llvm::Error::success(); + return llvm::make_error( + "Mismatched bitcode version number.\n", llvm::inconvertibleErrorCode()); } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - NamespaceInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + NamespaceInfo *I) { switch (ID) { case NAMESPACE_USR: return decodeRecord(R, I->USR, Blob); case NAMESPACE_NAME: return decodeRecord(R, I->Name, Blob); default: - return false; + return llvm::make_error( + "Invalid field for NamespaceInfo.\n", llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, RecordInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + RecordInfo *I) { switch (ID) { case RECORD_USR: return decodeRecord(R, I->USR, Blob); @@ -155,11 +169,13 @@ bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, RecordInfo *I) { case RECORD_TAG_TYPE: return decodeRecord(R, I->TagType, Blob); default: - return false; + return llvm::make_error( + "Invalid field for RecordInfo.\n", llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, EnumInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + EnumInfo *I) { switch (ID) { case ENUM_USR: return decodeRecord(R, I->USR, Blob); @@ -174,11 +190,13 @@ bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, EnumInfo *I) { case ENUM_SCOPED: return decodeRecord(R, I->Scoped, Blob); default: - return false; + return llvm::make_error("Invalid field for EnumInfo.\n", + llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, FunctionInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + FunctionInfo *I) { switch (ID) { case FUNCTION_USR: return decodeRecord(R, I->USR, Blob); @@ -193,37 +211,42 @@ bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, FunctionInfo *I) { case FUNCTION_IS_METHOD: return decodeRecord(R, I->IsMethod, Blob); default: - return false; + return llvm::make_error( + "Invalid field for FunctionInfo.\n", llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, TypeInfo *I) { - return true; +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + TypeInfo *I) { + return llvm::Error::success(); } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - FieldTypeInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + FieldTypeInfo *I) { switch (ID) { case FIELD_TYPE_NAME: return decodeRecord(R, I->Name, Blob); default: - return false; + return llvm::make_error("Invalid field for TypeInfo.\n", + llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - MemberTypeInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + MemberTypeInfo *I) { switch (ID) { case MEMBER_TYPE_NAME: return decodeRecord(R, I->Name, Blob); case MEMBER_TYPE_ACCESS: return decodeRecord(R, I->Access, Blob); default: - return false; + return llvm::make_error( + "Invalid field for MemberTypeInfo.\n", llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, CommentInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + CommentInfo *I) { switch (ID) { case COMMENT_KIND: return decodeRecord(R, I->Kind, Blob); @@ -248,12 +271,13 @@ bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, CommentInfo *I) { case COMMENT_EXPLICIT: return decodeRecord(R, I->Explicit, Blob); default: - return false; + return llvm::make_error( + "Invalid field for CommentInfo.\n", llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, Reference *I, - FieldId &F) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + Reference *I, FieldId &F) { switch (ID) { case REFERENCE_USR: return decodeRecord(R, I->USR, Blob); @@ -264,159 +288,178 @@ bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, Reference *I, case REFERENCE_FIELD: return decodeRecord(R, F, Blob); default: - return false; + return llvm::make_error("Invalid field for Reference.\n", + llvm::inconvertibleErrorCode()); } } -template CommentInfo *getCommentInfo(T I) { - llvm::errs() << "Cannot have comment subblock.\n"; - exit(1); +template llvm::Expected getCommentInfo(T I) { + return llvm::make_error( + "Invalid type cannot contain CommentInfo.\n", + llvm::inconvertibleErrorCode()); } -template <> CommentInfo *getCommentInfo(FunctionInfo *I) { +template <> llvm::Expected getCommentInfo(FunctionInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } -template <> CommentInfo *getCommentInfo(NamespaceInfo *I) { +template <> llvm::Expected getCommentInfo(NamespaceInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } -template <> CommentInfo *getCommentInfo(RecordInfo *I) { +template <> llvm::Expected getCommentInfo(RecordInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } -template <> CommentInfo *getCommentInfo(EnumInfo *I) { +template <> llvm::Expected getCommentInfo(EnumInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } -template <> CommentInfo *getCommentInfo(CommentInfo *I) { +template <> llvm::Expected getCommentInfo(CommentInfo *I) { I->Children.emplace_back(llvm::make_unique()); return I->Children.back().get(); } -template <> CommentInfo *getCommentInfo(std::unique_ptr &I) { +template <> +llvm::Expected getCommentInfo(std::unique_ptr &I) { return getCommentInfo(I.get()); } template -void addTypeInfo(T I, TTypeInfo &&TI) { - llvm::errs() << "Invalid type for info.\n"; - exit(1); +llvm::Error addTypeInfo(T I, TTypeInfo &&TI) { + return llvm::make_error( + "Invalid type cannot contain TypeInfo.\n", + llvm::inconvertibleErrorCode()); } -template <> void addTypeInfo(RecordInfo *I, MemberTypeInfo &&T) { +template <> llvm::Error addTypeInfo(RecordInfo *I, MemberTypeInfo &&T) { I->Members.emplace_back(std::move(T)); + return llvm::Error::success(); } -template <> void addTypeInfo(FunctionInfo *I, TypeInfo &&T) { +template <> llvm::Error addTypeInfo(FunctionInfo *I, TypeInfo &&T) { I->ReturnType = std::move(T); + return llvm::Error::success(); } -template <> void addTypeInfo(FunctionInfo *I, FieldTypeInfo &&T) { +template <> llvm::Error addTypeInfo(FunctionInfo *I, FieldTypeInfo &&T) { I->Params.emplace_back(std::move(T)); + return llvm::Error::success(); } -template void addReference(T I, Reference &&R, FieldId F) { - llvm::errs() << "Invalid field type for info.\n"; - exit(1); +template llvm::Error addReference(T I, Reference &&R, FieldId F) { + return llvm::make_error( + "Invalid type cannot contain Reference\n", + llvm::inconvertibleErrorCode()); } -template <> void addReference(TypeInfo *I, Reference &&R, FieldId F) { +template <> llvm::Error addReference(TypeInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_type: I->Type = std::move(R); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } -template <> void addReference(FieldTypeInfo *I, Reference &&R, FieldId F) { +template <> +llvm::Error addReference(FieldTypeInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_type: I->Type = std::move(R); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } -template <> void addReference(MemberTypeInfo *I, Reference &&R, FieldId F) { +template <> +llvm::Error addReference(MemberTypeInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_type: I->Type = std::move(R); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } -template <> void addReference(EnumInfo *I, Reference &&R, FieldId F) { +template <> llvm::Error addReference(EnumInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } -template <> void addReference(NamespaceInfo *I, Reference &&R, FieldId F) { +template <> +llvm::Error addReference(NamespaceInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); - break; + return llvm::Error::success(); case FieldId::F_child_namespace: I->ChildNamespaces.emplace_back(std::move(R)); - break; + return llvm::Error::success(); case FieldId::F_child_record: I->ChildRecords.emplace_back(std::move(R)); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } -template <> void addReference(FunctionInfo *I, Reference &&R, FieldId F) { +template <> +llvm::Error addReference(FunctionInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); - break; + return llvm::Error::success(); case FieldId::F_parent: I->Parent = std::move(R); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } -template <> void addReference(RecordInfo *I, Reference &&R, FieldId F) { +template <> llvm::Error addReference(RecordInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); - break; + return llvm::Error::success(); case FieldId::F_parent: I->Parents.emplace_back(std::move(R)); - break; + return llvm::Error::success(); case FieldId::F_vparent: I->VirtualParents.emplace_back(std::move(R)); - break; + return llvm::Error::success(); case FieldId::F_child_record: I->ChildRecords.emplace_back(std::move(R)); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } @@ -443,14 +486,16 @@ template <> void addChild(RecordInfo *I, EnumInfo &&R) { } // Read records from bitcode into a given info. -template bool ClangDocBitcodeReader::readRecord(unsigned ID, T I) { +template +llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, T I) { Record R; llvm::StringRef Blob; unsigned RecID = Stream.readRecord(ID, R, &Blob); return parseRecord(R, RecID, Blob, I); } -template <> bool ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) { +template <> +llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) { Record R; llvm::StringRef Blob; unsigned RecID = Stream.readRecord(ID, R, &Blob); @@ -458,9 +503,11 @@ template <> bool ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) { } // Read a block of records into a single info. -template bool ClangDocBitcodeReader::readBlock(unsigned ID, T I) { +template +llvm::Error ClangDocBitcodeReader::readBlock(unsigned ID, T I) { if (Stream.EnterSubBlock(ID)) - return false; + return llvm::make_error("Unable to enter subblock.\n", + llvm::inconvertibleErrorCode()); while (true) { unsigned BlockOrCode = 0; @@ -468,83 +515,87 @@ template bool ClangDocBitcodeReader::readBlock(unsigned ID, T I) { switch (Res) { case Cursor::BadBlock: - return false; + return llvm::make_error( + "Bad block found.\n", llvm::inconvertibleErrorCode()); case Cursor::BlockEnd: - return true; + return llvm::Error::success(); case Cursor::BlockBegin: - if (readSubBlock(BlockOrCode, I)) - continue; - if (!Stream.SkipBlock()) - return false; + if (auto Err = readSubBlock(BlockOrCode, I)) { + if (!Stream.SkipBlock()) + continue; + return Err; + } continue; case Cursor::Record: break; } - if (!readRecord(BlockOrCode, I)) - return false; + if (auto Err = readRecord(BlockOrCode, I)) + return Err; } } template -bool ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) { +llvm::Error ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) { switch (ID) { // Blocks can only have Comment, Reference, TypeInfo, FunctionInfo, or // EnumInfo subblocks - case BI_COMMENT_BLOCK_ID: - if (readBlock(ID, getCommentInfo(I))) - return true; - return false; + case BI_COMMENT_BLOCK_ID: { + auto Comment = getCommentInfo(I); + if (!Comment) + return Comment.takeError(); + if (auto Err = readBlock(ID, Comment.get())) + return Err; + return llvm::Error::success(); + } case BI_TYPE_BLOCK_ID: { TypeInfo TI; - if (readBlock(ID, &TI)) { - addTypeInfo(I, std::move(TI)); - return true; - } - return false; + if (auto Err = readBlock(ID, &TI)) + return Err; + if (auto Err = addTypeInfo(I, std::move(TI))) + return Err; + return llvm::Error::success(); } case BI_FIELD_TYPE_BLOCK_ID: { FieldTypeInfo TI; - if (readBlock(ID, &TI)) { - addTypeInfo(I, std::move(TI)); - return true; - } - return false; + if (auto Err = readBlock(ID, &TI)) + return Err; + if (auto Err = addTypeInfo(I, std::move(TI))) + return Err; + return llvm::Error::success(); } case BI_MEMBER_TYPE_BLOCK_ID: { MemberTypeInfo TI; - if (readBlock(ID, &TI)) { - addTypeInfo(I, std::move(TI)); - return true; - } - return false; + if (auto Err = readBlock(ID, &TI)) + return Err; + if (auto Err = addTypeInfo(I, std::move(TI))) + return Err; + return llvm::Error::success(); } case BI_REFERENCE_BLOCK_ID: { Reference R; - if (readBlock(ID, &R)) { - addReference(I, std::move(R), CurrentReferenceField); - return true; - } - return false; + if (auto Err = readBlock(ID, &R)) + return Err; + if (auto Err = addReference(I, std::move(R), CurrentReferenceField)) + return Err; + return llvm::Error::success(); } case BI_FUNCTION_BLOCK_ID: { FunctionInfo F; - if (readBlock(ID, &F)) { - addChild(I, std::move(F)); - return true; - } - return false; + if (auto Err = readBlock(ID, &F)) + return Err; + addChild(I, std::move(F)); + return llvm::Error::success(); } case BI_ENUM_BLOCK_ID: { EnumInfo E; - if (readBlock(ID, &E)) { - addChild(I, std::move(E)); - return true; - } - return false; + if (auto Err = readBlock(ID, &E)) + return Err; + addChild(I, std::move(E)); + return llvm::Error::success(); } default: - llvm::errs() << "Invalid subblock type.\n"; - return false; + return llvm::make_error("Invalid subblock type.\n", + llvm::inconvertibleErrorCode()); } } @@ -576,37 +627,41 @@ ClangDocBitcodeReader::skipUntilRecordOrBlock(unsigned &BlockOrRecordID) { llvm_unreachable("Premature stream end."); } -bool ClangDocBitcodeReader::validateStream() { +llvm::Error ClangDocBitcodeReader::validateStream() { if (Stream.AtEndOfStream()) - return false; + return llvm::make_error("Premature end of stream.\n", + llvm::inconvertibleErrorCode()); // Sniff for the signature. if (Stream.Read(8) != BitCodeConstants::Signature[0] || Stream.Read(8) != BitCodeConstants::Signature[1] || Stream.Read(8) != BitCodeConstants::Signature[2] || Stream.Read(8) != BitCodeConstants::Signature[3]) - return false; - return true; + return llvm::make_error("Invalid bitcode signature.\n", + llvm::inconvertibleErrorCode()); + return llvm::Error::success(); } -bool ClangDocBitcodeReader::readBlockInfoBlock() { +llvm::Error ClangDocBitcodeReader::readBlockInfoBlock() { BlockInfo = Stream.ReadBlockInfoBlock(); if (!BlockInfo) - return false; + return llvm::make_error( + "Unable to parse BlockInfoBlock.\n", llvm::inconvertibleErrorCode()); Stream.setBlockInfo(&*BlockInfo); - return true; + return llvm::Error::success(); } template -std::unique_ptr ClangDocBitcodeReader::createInfo(unsigned ID) { +llvm::Expected> +ClangDocBitcodeReader::createInfo(unsigned ID) { std::unique_ptr I = llvm::make_unique(); - if (readBlock(ID, static_cast(I.get()))) - return I; - llvm::errs() << "Error reading from block.\n"; - return nullptr; + if (auto Err = readBlock(ID, static_cast(I.get()))) + return std::move(Err); + return I; } -std::unique_ptr ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { +llvm::Expected> +ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { switch (ID) { case BI_NAMESPACE_BLOCK_ID: return createInfo(ID); @@ -617,8 +672,8 @@ std::unique_ptr ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { case BI_FUNCTION_BLOCK_ID: return createInfo(ID); default: - llvm::errs() << "Error reading from block.\n"; - return nullptr; + return llvm::make_error("Cannot create info.\n", + llvm::inconvertibleErrorCode()); } } @@ -626,19 +681,15 @@ std::unique_ptr ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { llvm::Expected>> ClangDocBitcodeReader::readBitcode() { std::vector> Infos; - if (!validateStream()) - return llvm::make_error("Invalid bitcode stream.\n", - llvm::inconvertibleErrorCode()); - ; + if (auto Err = validateStream()) + return std::move(Err); // Read the top level blocks. while (!Stream.AtEndOfStream()) { unsigned Code = Stream.ReadCode(); if (Code != llvm::bitc::ENTER_SUBBLOCK) return llvm::make_error( - "Missing subblock in bitcode.\n", llvm::inconvertibleErrorCode()); - ; - + "No blocks in input.\n", llvm::inconvertibleErrorCode()); unsigned ID = Stream.ReadSubBlockID(); switch (ID) { // NamedType and Comment blocks should not appear at the top level @@ -648,29 +699,25 @@ ClangDocBitcodeReader::readBitcode() { case BI_COMMENT_BLOCK_ID: case BI_REFERENCE_BLOCK_ID: return llvm::make_error( - "Invalid top level block in bitcode.\n", - llvm::inconvertibleErrorCode()); - ; + "Invalid top level block.\n", llvm::inconvertibleErrorCode()); case BI_NAMESPACE_BLOCK_ID: case BI_RECORD_BLOCK_ID: case BI_ENUM_BLOCK_ID: - case BI_FUNCTION_BLOCK_ID: - if (std::unique_ptr I = readBlockToInfo(ID)) - Infos.emplace_back(std::move(I)); + case BI_FUNCTION_BLOCK_ID: { + auto InfoOrErr = readBlockToInfo(ID); + if (!InfoOrErr) + return InfoOrErr.takeError(); + Infos.emplace_back(std::move(InfoOrErr.get())); continue; + } case BI_VERSION_BLOCK_ID: - if (readBlock(ID, VersionNumber)) - continue; - return llvm::make_error( - "Invalid bitcode version in bitcode.\n", - llvm::inconvertibleErrorCode()); - ; + if (auto Err = readBlock(ID, VersionNumber)) + return std::move(Err); + continue; case llvm::bitc::BLOCKINFO_BLOCK_ID: - if (readBlockInfoBlock()) - continue; - return llvm::make_error( - "Invalid BlockInfo in bitcode.\n", llvm::inconvertibleErrorCode()); - ; + if (auto Err = readBlockInfoBlock()) + return std::move(Err); + continue; default: if (!Stream.SkipBlock()) continue; diff --git a/clang-doc/BitcodeReader.h b/clang-doc/BitcodeReader.h index aaf25257c..ec3f6b0fd 100644 --- a/clang-doc/BitcodeReader.h +++ b/clang-doc/BitcodeReader.h @@ -22,6 +22,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Support/Error.h" namespace clang { namespace doc { @@ -38,30 +39,31 @@ class ClangDocBitcodeReader { enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin }; // Top level parsing - bool validateStream(); - bool readVersion(); - bool readBlockInfoBlock(); + llvm::Error validateStream(); + llvm::Error readVersion(); + llvm::Error readBlockInfoBlock(); // Read a block of records into a single Info struct, calls readRecord on each // record found. - template bool readBlock(unsigned ID, T I); + template llvm::Error readBlock(unsigned ID, T I); // Step through a block of records to find the next data field. - template bool readSubBlock(unsigned ID, T I); + template llvm::Error readSubBlock(unsigned ID, T I); // Read record data into the given Info data field, calling the appropriate // parseRecord functions to parse and store the data. - template bool readRecord(unsigned ID, T I); + template llvm::Error readRecord(unsigned ID, T I); // Allocate the relevant type of info and add read data to the object. - template std::unique_ptr createInfo(unsigned ID); + template + llvm::Expected> createInfo(unsigned ID); // Helper function to step through blocks to find and dispatch the next record // or block to be read. Cursor skipUntilRecordOrBlock(unsigned &BlockOrRecordID); // Helper function to set up the approriate type of Info. - std::unique_ptr readBlockToInfo(unsigned ID); + llvm::Expected> readBlockToInfo(unsigned ID); llvm::BitstreamCursor &Stream; Optional BlockInfo; From bceeab87d6b3fd3311608a3362b25de0c608789b Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Mon, 13 Aug 2018 21:51:48 +0000 Subject: [PATCH 040/686] Revert "[clang-doc] Updating BitcodeReader to use llvm::Error" This reverts commit r339617 for breaking bots. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339620 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/BitcodeReader.cpp | 447 ++++++++++++++++-------------------- clang-doc/BitcodeReader.h | 18 +- 2 files changed, 208 insertions(+), 257 deletions(-) diff --git a/clang-doc/BitcodeReader.cpp b/clang-doc/BitcodeReader.cpp index dee0930bc..7acf107db 100644 --- a/clang-doc/BitcodeReader.cpp +++ b/clang-doc/BitcodeReader.cpp @@ -10,7 +10,6 @@ #include "BitcodeReader.h" #include "llvm/ADT/IndexedMap.h" #include "llvm/ADT/Optional.h" -#include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" namespace clang { @@ -18,53 +17,49 @@ namespace doc { using Record = llvm::SmallVector; -llvm::Error decodeRecord(Record R, llvm::SmallVectorImpl &Field, - llvm::StringRef Blob) { +bool decodeRecord(Record R, llvm::SmallVectorImpl &Field, + llvm::StringRef Blob) { Field.assign(Blob.begin(), Blob.end()); - return llvm::Error::success(); + return true; } -llvm::Error decodeRecord(Record R, SymbolID &Field, llvm::StringRef Blob) { +bool decodeRecord(Record R, SymbolID &Field, llvm::StringRef Blob) { if (R[0] != BitCodeConstants::USRHashSize) - return llvm::make_error("Incorrect USR size.\n", - llvm::inconvertibleErrorCode()); + return false; // First position in the record is the length of the following array, so we // copy the following elements to the field. for (int I = 0, E = R[0]; I < E; ++I) Field[I] = R[I + 1]; - return llvm::Error::success(); + return true; } -llvm::Error decodeRecord(Record R, bool &Field, llvm::StringRef Blob) { +bool decodeRecord(Record R, bool &Field, llvm::StringRef Blob) { Field = R[0] != 0; - return llvm::Error::success(); + return true; } -llvm::Error decodeRecord(Record R, int &Field, llvm::StringRef Blob) { +bool decodeRecord(Record R, int &Field, llvm::StringRef Blob) { if (R[0] > INT_MAX) - return llvm::make_error("Integer too large to parse.\n", - llvm::inconvertibleErrorCode()); + return false; Field = (int)R[0]; - return llvm::Error::success(); + return true; } -llvm::Error decodeRecord(Record R, AccessSpecifier &Field, - llvm::StringRef Blob) { +bool decodeRecord(Record R, AccessSpecifier &Field, llvm::StringRef Blob) { switch (R[0]) { case AS_public: case AS_private: case AS_protected: case AS_none: Field = (AccessSpecifier)R[0]; - return llvm::Error::success(); + return true; default: - return llvm::make_error( - "Invalid value for AccessSpecifier.\n", llvm::inconvertibleErrorCode()); + return false; } } -llvm::Error decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) { +bool decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) { switch (R[0]) { case TTK_Struct: case TTK_Interface: @@ -72,23 +67,21 @@ llvm::Error decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) { case TTK_Class: case TTK_Enum: Field = (TagTypeKind)R[0]; - return llvm::Error::success(); + return true; default: - return llvm::make_error( - "Invalid value for TagTypeKind.\n", llvm::inconvertibleErrorCode()); + return false; } } -llvm::Error decodeRecord(Record R, llvm::Optional &Field, - llvm::StringRef Blob) { +bool decodeRecord(Record R, llvm::Optional &Field, + llvm::StringRef Blob) { if (R[0] > INT_MAX) - return llvm::make_error("Integer too large to parse.\n", - llvm::inconvertibleErrorCode()); + return false; Field.emplace((int)R[0], Blob); - return llvm::Error::success(); + return true; } -llvm::Error decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) { +bool decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) { switch (auto IT = static_cast(R[0])) { case InfoType::IT_namespace: case InfoType::IT_record: @@ -96,13 +89,12 @@ llvm::Error decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) { case InfoType::IT_default: case InfoType::IT_enum: Field = IT; - return llvm::Error::success(); + return true; } - return llvm::make_error("Invalid value for InfoType.\n", - llvm::inconvertibleErrorCode()); + return false; } -llvm::Error decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { +bool decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { switch (auto F = static_cast(R[0])) { case FieldId::F_namespace: case FieldId::F_parent: @@ -112,51 +104,45 @@ llvm::Error decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { case FieldId::F_child_record: case FieldId::F_default: Field = F; - return llvm::Error::success(); + return true; } - return llvm::make_error("Invalid value for FieldId.\n", - llvm::inconvertibleErrorCode()); + return false; } -llvm::Error decodeRecord(Record R, - llvm::SmallVectorImpl> &Field, - llvm::StringRef Blob) { +bool decodeRecord(Record R, llvm::SmallVectorImpl> &Field, + llvm::StringRef Blob) { Field.push_back(Blob); - return llvm::Error::success(); + return true; } -llvm::Error decodeRecord(Record R, llvm::SmallVectorImpl &Field, - llvm::StringRef Blob) { +bool decodeRecord(Record R, llvm::SmallVectorImpl &Field, + llvm::StringRef Blob) { if (R[0] > INT_MAX) - return llvm::make_error("Integer too large to parse.\n", - llvm::inconvertibleErrorCode()); + return false; Field.emplace_back((int)R[0], Blob); - return llvm::Error::success(); + return true; } -llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - const unsigned VersionNo) { +bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + const unsigned VersionNo) { if (ID == VERSION && R[0] == VersionNo) - return llvm::Error::success(); - return llvm::make_error( - "Mismatched bitcode version number.\n", llvm::inconvertibleErrorCode()); + return true; + return false; } -llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - NamespaceInfo *I) { +bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + NamespaceInfo *I) { switch (ID) { case NAMESPACE_USR: return decodeRecord(R, I->USR, Blob); case NAMESPACE_NAME: return decodeRecord(R, I->Name, Blob); default: - return llvm::make_error( - "Invalid field for NamespaceInfo.\n", llvm::inconvertibleErrorCode()); + return false; } } -llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - RecordInfo *I) { +bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, RecordInfo *I) { switch (ID) { case RECORD_USR: return decodeRecord(R, I->USR, Blob); @@ -169,13 +155,11 @@ llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, case RECORD_TAG_TYPE: return decodeRecord(R, I->TagType, Blob); default: - return llvm::make_error( - "Invalid field for RecordInfo.\n", llvm::inconvertibleErrorCode()); + return false; } } -llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - EnumInfo *I) { +bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, EnumInfo *I) { switch (ID) { case ENUM_USR: return decodeRecord(R, I->USR, Blob); @@ -190,13 +174,11 @@ llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, case ENUM_SCOPED: return decodeRecord(R, I->Scoped, Blob); default: - return llvm::make_error("Invalid field for EnumInfo.\n", - llvm::inconvertibleErrorCode()); + return false; } } -llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - FunctionInfo *I) { +bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, FunctionInfo *I) { switch (ID) { case FUNCTION_USR: return decodeRecord(R, I->USR, Blob); @@ -211,42 +193,37 @@ llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, case FUNCTION_IS_METHOD: return decodeRecord(R, I->IsMethod, Blob); default: - return llvm::make_error( - "Invalid field for FunctionInfo.\n", llvm::inconvertibleErrorCode()); + return false; } } -llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - TypeInfo *I) { - return llvm::Error::success(); +bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, TypeInfo *I) { + return true; } -llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - FieldTypeInfo *I) { +bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + FieldTypeInfo *I) { switch (ID) { case FIELD_TYPE_NAME: return decodeRecord(R, I->Name, Blob); default: - return llvm::make_error("Invalid field for TypeInfo.\n", - llvm::inconvertibleErrorCode()); + return false; } } -llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - MemberTypeInfo *I) { +bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + MemberTypeInfo *I) { switch (ID) { case MEMBER_TYPE_NAME: return decodeRecord(R, I->Name, Blob); case MEMBER_TYPE_ACCESS: return decodeRecord(R, I->Access, Blob); default: - return llvm::make_error( - "Invalid field for MemberTypeInfo.\n", llvm::inconvertibleErrorCode()); + return false; } } -llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - CommentInfo *I) { +bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, CommentInfo *I) { switch (ID) { case COMMENT_KIND: return decodeRecord(R, I->Kind, Blob); @@ -271,13 +248,12 @@ llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, case COMMENT_EXPLICIT: return decodeRecord(R, I->Explicit, Blob); default: - return llvm::make_error( - "Invalid field for CommentInfo.\n", llvm::inconvertibleErrorCode()); + return false; } } -llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - Reference *I, FieldId &F) { +bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, Reference *I, + FieldId &F) { switch (ID) { case REFERENCE_USR: return decodeRecord(R, I->USR, Blob); @@ -288,178 +264,159 @@ llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, case REFERENCE_FIELD: return decodeRecord(R, F, Blob); default: - return llvm::make_error("Invalid field for Reference.\n", - llvm::inconvertibleErrorCode()); + return false; } } -template llvm::Expected getCommentInfo(T I) { - return llvm::make_error( - "Invalid type cannot contain CommentInfo.\n", - llvm::inconvertibleErrorCode()); +template CommentInfo *getCommentInfo(T I) { + llvm::errs() << "Cannot have comment subblock.\n"; + exit(1); } -template <> llvm::Expected getCommentInfo(FunctionInfo *I) { +template <> CommentInfo *getCommentInfo(FunctionInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } -template <> llvm::Expected getCommentInfo(NamespaceInfo *I) { +template <> CommentInfo *getCommentInfo(NamespaceInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } -template <> llvm::Expected getCommentInfo(RecordInfo *I) { +template <> CommentInfo *getCommentInfo(RecordInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } -template <> llvm::Expected getCommentInfo(EnumInfo *I) { +template <> CommentInfo *getCommentInfo(EnumInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } -template <> llvm::Expected getCommentInfo(CommentInfo *I) { +template <> CommentInfo *getCommentInfo(CommentInfo *I) { I->Children.emplace_back(llvm::make_unique()); return I->Children.back().get(); } -template <> -llvm::Expected getCommentInfo(std::unique_ptr &I) { +template <> CommentInfo *getCommentInfo(std::unique_ptr &I) { return getCommentInfo(I.get()); } template -llvm::Error addTypeInfo(T I, TTypeInfo &&TI) { - return llvm::make_error( - "Invalid type cannot contain TypeInfo.\n", - llvm::inconvertibleErrorCode()); +void addTypeInfo(T I, TTypeInfo &&TI) { + llvm::errs() << "Invalid type for info.\n"; + exit(1); } -template <> llvm::Error addTypeInfo(RecordInfo *I, MemberTypeInfo &&T) { +template <> void addTypeInfo(RecordInfo *I, MemberTypeInfo &&T) { I->Members.emplace_back(std::move(T)); - return llvm::Error::success(); } -template <> llvm::Error addTypeInfo(FunctionInfo *I, TypeInfo &&T) { +template <> void addTypeInfo(FunctionInfo *I, TypeInfo &&T) { I->ReturnType = std::move(T); - return llvm::Error::success(); } -template <> llvm::Error addTypeInfo(FunctionInfo *I, FieldTypeInfo &&T) { +template <> void addTypeInfo(FunctionInfo *I, FieldTypeInfo &&T) { I->Params.emplace_back(std::move(T)); - return llvm::Error::success(); } -template llvm::Error addReference(T I, Reference &&R, FieldId F) { - return llvm::make_error( - "Invalid type cannot contain Reference\n", - llvm::inconvertibleErrorCode()); +template void addReference(T I, Reference &&R, FieldId F) { + llvm::errs() << "Invalid field type for info.\n"; + exit(1); } -template <> llvm::Error addReference(TypeInfo *I, Reference &&R, FieldId F) { +template <> void addReference(TypeInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_type: I->Type = std::move(R); - return llvm::Error::success(); + break; default: - return llvm::make_error( - "Invalid type cannot contain Reference.\n", - llvm::inconvertibleErrorCode()); + llvm::errs() << "Invalid field type for info.\n"; + exit(1); } } -template <> -llvm::Error addReference(FieldTypeInfo *I, Reference &&R, FieldId F) { +template <> void addReference(FieldTypeInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_type: I->Type = std::move(R); - return llvm::Error::success(); + break; default: - return llvm::make_error( - "Invalid type cannot contain Reference.\n", - llvm::inconvertibleErrorCode()); + llvm::errs() << "Invalid field type for info.\n"; + exit(1); } } -template <> -llvm::Error addReference(MemberTypeInfo *I, Reference &&R, FieldId F) { +template <> void addReference(MemberTypeInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_type: I->Type = std::move(R); - return llvm::Error::success(); + break; default: - return llvm::make_error( - "Invalid type cannot contain Reference.\n", - llvm::inconvertibleErrorCode()); + llvm::errs() << "Invalid field type for info.\n"; + exit(1); } } -template <> llvm::Error addReference(EnumInfo *I, Reference &&R, FieldId F) { +template <> void addReference(EnumInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); - return llvm::Error::success(); + break; default: - return llvm::make_error( - "Invalid type cannot contain Reference.\n", - llvm::inconvertibleErrorCode()); + llvm::errs() << "Invalid field type for info.\n"; + exit(1); } } -template <> -llvm::Error addReference(NamespaceInfo *I, Reference &&R, FieldId F) { +template <> void addReference(NamespaceInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); - return llvm::Error::success(); + break; case FieldId::F_child_namespace: I->ChildNamespaces.emplace_back(std::move(R)); - return llvm::Error::success(); + break; case FieldId::F_child_record: I->ChildRecords.emplace_back(std::move(R)); - return llvm::Error::success(); + break; default: - return llvm::make_error( - "Invalid type cannot contain Reference.\n", - llvm::inconvertibleErrorCode()); + llvm::errs() << "Invalid field type for info.\n"; + exit(1); } } -template <> -llvm::Error addReference(FunctionInfo *I, Reference &&R, FieldId F) { +template <> void addReference(FunctionInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); - return llvm::Error::success(); + break; case FieldId::F_parent: I->Parent = std::move(R); - return llvm::Error::success(); + break; default: - return llvm::make_error( - "Invalid type cannot contain Reference.\n", - llvm::inconvertibleErrorCode()); + llvm::errs() << "Invalid field type for info.\n"; + exit(1); } } -template <> llvm::Error addReference(RecordInfo *I, Reference &&R, FieldId F) { +template <> void addReference(RecordInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); - return llvm::Error::success(); + break; case FieldId::F_parent: I->Parents.emplace_back(std::move(R)); - return llvm::Error::success(); + break; case FieldId::F_vparent: I->VirtualParents.emplace_back(std::move(R)); - return llvm::Error::success(); + break; case FieldId::F_child_record: I->ChildRecords.emplace_back(std::move(R)); - return llvm::Error::success(); + break; default: - return llvm::make_error( - "Invalid type cannot contain Reference.\n", - llvm::inconvertibleErrorCode()); + llvm::errs() << "Invalid field type for info.\n"; + exit(1); } } @@ -486,16 +443,14 @@ template <> void addChild(RecordInfo *I, EnumInfo &&R) { } // Read records from bitcode into a given info. -template -llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, T I) { +template bool ClangDocBitcodeReader::readRecord(unsigned ID, T I) { Record R; llvm::StringRef Blob; unsigned RecID = Stream.readRecord(ID, R, &Blob); return parseRecord(R, RecID, Blob, I); } -template <> -llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) { +template <> bool ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) { Record R; llvm::StringRef Blob; unsigned RecID = Stream.readRecord(ID, R, &Blob); @@ -503,11 +458,9 @@ llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) { } // Read a block of records into a single info. -template -llvm::Error ClangDocBitcodeReader::readBlock(unsigned ID, T I) { +template bool ClangDocBitcodeReader::readBlock(unsigned ID, T I) { if (Stream.EnterSubBlock(ID)) - return llvm::make_error("Unable to enter subblock.\n", - llvm::inconvertibleErrorCode()); + return false; while (true) { unsigned BlockOrCode = 0; @@ -515,87 +468,83 @@ llvm::Error ClangDocBitcodeReader::readBlock(unsigned ID, T I) { switch (Res) { case Cursor::BadBlock: - return llvm::make_error( - "Bad block found.\n", llvm::inconvertibleErrorCode()); + return false; case Cursor::BlockEnd: - return llvm::Error::success(); + return true; case Cursor::BlockBegin: - if (auto Err = readSubBlock(BlockOrCode, I)) { - if (!Stream.SkipBlock()) - continue; - return Err; - } + if (readSubBlock(BlockOrCode, I)) + continue; + if (!Stream.SkipBlock()) + return false; continue; case Cursor::Record: break; } - if (auto Err = readRecord(BlockOrCode, I)) - return Err; + if (!readRecord(BlockOrCode, I)) + return false; } } template -llvm::Error ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) { +bool ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) { switch (ID) { // Blocks can only have Comment, Reference, TypeInfo, FunctionInfo, or // EnumInfo subblocks - case BI_COMMENT_BLOCK_ID: { - auto Comment = getCommentInfo(I); - if (!Comment) - return Comment.takeError(); - if (auto Err = readBlock(ID, Comment.get())) - return Err; - return llvm::Error::success(); - } + case BI_COMMENT_BLOCK_ID: + if (readBlock(ID, getCommentInfo(I))) + return true; + return false; case BI_TYPE_BLOCK_ID: { TypeInfo TI; - if (auto Err = readBlock(ID, &TI)) - return Err; - if (auto Err = addTypeInfo(I, std::move(TI))) - return Err; - return llvm::Error::success(); + if (readBlock(ID, &TI)) { + addTypeInfo(I, std::move(TI)); + return true; + } + return false; } case BI_FIELD_TYPE_BLOCK_ID: { FieldTypeInfo TI; - if (auto Err = readBlock(ID, &TI)) - return Err; - if (auto Err = addTypeInfo(I, std::move(TI))) - return Err; - return llvm::Error::success(); + if (readBlock(ID, &TI)) { + addTypeInfo(I, std::move(TI)); + return true; + } + return false; } case BI_MEMBER_TYPE_BLOCK_ID: { MemberTypeInfo TI; - if (auto Err = readBlock(ID, &TI)) - return Err; - if (auto Err = addTypeInfo(I, std::move(TI))) - return Err; - return llvm::Error::success(); + if (readBlock(ID, &TI)) { + addTypeInfo(I, std::move(TI)); + return true; + } + return false; } case BI_REFERENCE_BLOCK_ID: { Reference R; - if (auto Err = readBlock(ID, &R)) - return Err; - if (auto Err = addReference(I, std::move(R), CurrentReferenceField)) - return Err; - return llvm::Error::success(); + if (readBlock(ID, &R)) { + addReference(I, std::move(R), CurrentReferenceField); + return true; + } + return false; } case BI_FUNCTION_BLOCK_ID: { FunctionInfo F; - if (auto Err = readBlock(ID, &F)) - return Err; - addChild(I, std::move(F)); - return llvm::Error::success(); + if (readBlock(ID, &F)) { + addChild(I, std::move(F)); + return true; + } + return false; } case BI_ENUM_BLOCK_ID: { EnumInfo E; - if (auto Err = readBlock(ID, &E)) - return Err; - addChild(I, std::move(E)); - return llvm::Error::success(); + if (readBlock(ID, &E)) { + addChild(I, std::move(E)); + return true; + } + return false; } default: - return llvm::make_error("Invalid subblock type.\n", - llvm::inconvertibleErrorCode()); + llvm::errs() << "Invalid subblock type.\n"; + return false; } } @@ -627,41 +576,37 @@ ClangDocBitcodeReader::skipUntilRecordOrBlock(unsigned &BlockOrRecordID) { llvm_unreachable("Premature stream end."); } -llvm::Error ClangDocBitcodeReader::validateStream() { +bool ClangDocBitcodeReader::validateStream() { if (Stream.AtEndOfStream()) - return llvm::make_error("Premature end of stream.\n", - llvm::inconvertibleErrorCode()); + return false; // Sniff for the signature. if (Stream.Read(8) != BitCodeConstants::Signature[0] || Stream.Read(8) != BitCodeConstants::Signature[1] || Stream.Read(8) != BitCodeConstants::Signature[2] || Stream.Read(8) != BitCodeConstants::Signature[3]) - return llvm::make_error("Invalid bitcode signature.\n", - llvm::inconvertibleErrorCode()); - return llvm::Error::success(); + return false; + return true; } -llvm::Error ClangDocBitcodeReader::readBlockInfoBlock() { +bool ClangDocBitcodeReader::readBlockInfoBlock() { BlockInfo = Stream.ReadBlockInfoBlock(); if (!BlockInfo) - return llvm::make_error( - "Unable to parse BlockInfoBlock.\n", llvm::inconvertibleErrorCode()); + return false; Stream.setBlockInfo(&*BlockInfo); - return llvm::Error::success(); + return true; } template -llvm::Expected> -ClangDocBitcodeReader::createInfo(unsigned ID) { +std::unique_ptr ClangDocBitcodeReader::createInfo(unsigned ID) { std::unique_ptr I = llvm::make_unique(); - if (auto Err = readBlock(ID, static_cast(I.get()))) - return std::move(Err); - return I; + if (readBlock(ID, static_cast(I.get()))) + return I; + llvm::errs() << "Error reading from block.\n"; + return nullptr; } -llvm::Expected> -ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { +std::unique_ptr ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { switch (ID) { case BI_NAMESPACE_BLOCK_ID: return createInfo(ID); @@ -672,8 +617,8 @@ ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { case BI_FUNCTION_BLOCK_ID: return createInfo(ID); default: - return llvm::make_error("Cannot create info.\n", - llvm::inconvertibleErrorCode()); + llvm::errs() << "Error reading from block.\n"; + return nullptr; } } @@ -681,15 +626,19 @@ ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { llvm::Expected>> ClangDocBitcodeReader::readBitcode() { std::vector> Infos; - if (auto Err = validateStream()) - return std::move(Err); + if (!validateStream()) + return llvm::make_error("Invalid bitcode stream.\n", + llvm::inconvertibleErrorCode()); + ; // Read the top level blocks. while (!Stream.AtEndOfStream()) { unsigned Code = Stream.ReadCode(); if (Code != llvm::bitc::ENTER_SUBBLOCK) return llvm::make_error( - "No blocks in input.\n", llvm::inconvertibleErrorCode()); + "Missing subblock in bitcode.\n", llvm::inconvertibleErrorCode()); + ; + unsigned ID = Stream.ReadSubBlockID(); switch (ID) { // NamedType and Comment blocks should not appear at the top level @@ -699,25 +648,29 @@ ClangDocBitcodeReader::readBitcode() { case BI_COMMENT_BLOCK_ID: case BI_REFERENCE_BLOCK_ID: return llvm::make_error( - "Invalid top level block.\n", llvm::inconvertibleErrorCode()); + "Invalid top level block in bitcode.\n", + llvm::inconvertibleErrorCode()); + ; case BI_NAMESPACE_BLOCK_ID: case BI_RECORD_BLOCK_ID: case BI_ENUM_BLOCK_ID: - case BI_FUNCTION_BLOCK_ID: { - auto InfoOrErr = readBlockToInfo(ID); - if (!InfoOrErr) - return InfoOrErr.takeError(); - Infos.emplace_back(std::move(InfoOrErr.get())); + case BI_FUNCTION_BLOCK_ID: + if (std::unique_ptr I = readBlockToInfo(ID)) + Infos.emplace_back(std::move(I)); continue; - } case BI_VERSION_BLOCK_ID: - if (auto Err = readBlock(ID, VersionNumber)) - return std::move(Err); - continue; + if (readBlock(ID, VersionNumber)) + continue; + return llvm::make_error( + "Invalid bitcode version in bitcode.\n", + llvm::inconvertibleErrorCode()); + ; case llvm::bitc::BLOCKINFO_BLOCK_ID: - if (auto Err = readBlockInfoBlock()) - return std::move(Err); - continue; + if (readBlockInfoBlock()) + continue; + return llvm::make_error( + "Invalid BlockInfo in bitcode.\n", llvm::inconvertibleErrorCode()); + ; default: if (!Stream.SkipBlock()) continue; diff --git a/clang-doc/BitcodeReader.h b/clang-doc/BitcodeReader.h index ec3f6b0fd..aaf25257c 100644 --- a/clang-doc/BitcodeReader.h +++ b/clang-doc/BitcodeReader.h @@ -22,7 +22,6 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Bitcode/BitstreamReader.h" -#include "llvm/Support/Error.h" namespace clang { namespace doc { @@ -39,31 +38,30 @@ class ClangDocBitcodeReader { enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin }; // Top level parsing - llvm::Error validateStream(); - llvm::Error readVersion(); - llvm::Error readBlockInfoBlock(); + bool validateStream(); + bool readVersion(); + bool readBlockInfoBlock(); // Read a block of records into a single Info struct, calls readRecord on each // record found. - template llvm::Error readBlock(unsigned ID, T I); + template bool readBlock(unsigned ID, T I); // Step through a block of records to find the next data field. - template llvm::Error readSubBlock(unsigned ID, T I); + template bool readSubBlock(unsigned ID, T I); // Read record data into the given Info data field, calling the appropriate // parseRecord functions to parse and store the data. - template llvm::Error readRecord(unsigned ID, T I); + template bool readRecord(unsigned ID, T I); // Allocate the relevant type of info and add read data to the object. - template - llvm::Expected> createInfo(unsigned ID); + template std::unique_ptr createInfo(unsigned ID); // Helper function to step through blocks to find and dispatch the next record // or block to be read. Cursor skipUntilRecordOrBlock(unsigned &BlockOrRecordID); // Helper function to set up the approriate type of Info. - llvm::Expected> readBlockToInfo(unsigned ID); + std::unique_ptr readBlockToInfo(unsigned ID); llvm::BitstreamCursor &Stream; Optional BlockInfo; From 1909d66a4328441428868635fcc3218b1ae546a1 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Tue, 14 Aug 2018 09:36:32 +0000 Subject: [PATCH 041/686] [clangd] Show non-instantiated decls in signatureHelp Summary: To avoid producing very verbose output in substitutions involving typedefs, e.g. T -> std::vector::iterator gets turned into an unreadable mess when printed out for libstdc++, result contains internal types (std::__Vector_iterator<...>) and expanded well-defined typedefs (std::basic_string). Until we improve the presentation code in clang, going with non-instantiated decls looks like a better UX trade-off. Reviewers: hokein, ioeric, kadircet Reviewed By: hokein Subscribers: MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50645 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339665 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 10 ++++- unittests/clangd/CodeCompleteTests.cpp | 53 ++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 12a3d5f48..72e395f1e 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -714,7 +714,15 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { "too many arguments"); SigHelp.activeParameter = static_cast(CurrentArg); for (unsigned I = 0; I < NumCandidates; ++I) { - const auto &Candidate = Candidates[I]; + OverloadCandidate Candidate = Candidates[I]; + // We want to avoid showing instantiated signatures, because they may be + // long in some cases (e.g. when 'T' is substituted with 'std::string', we + // would get 'std::basic_string'). + if (auto *Func = Candidate.getFunction()) { + if (auto *Pattern = Func->getTemplateInstantiationPattern()) + Candidate = OverloadCandidate(Pattern); + } + const auto *CCS = Candidate.CreateSignatureString( CurrentArg, S, *Allocator, CCTUInfo, true); assert(CCS && "Expected the CodeCompletionString to be non-null"); diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index d59754a29..6e1045ae6 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1537,6 +1537,59 @@ TEST(SignatureHelpTest, OverloadsOrdering) { EXPECT_EQ(0, Results.activeParameter); } +TEST(SignatureHelpTest, InstantiatedSignatures) { + EXPECT_THAT(signatures(R"cpp( + template + void foo(T, T, T); + + int main() { + foo(^); + } + )cpp") + .signatures, + ElementsAre(Sig("foo(T, T, T) -> void", {"T", "T", "T"}))); + + EXPECT_THAT(signatures(R"cpp( + template + void foo(T, T, T); + + int main() { + foo(10, ^); + })cpp") + .signatures, + ElementsAre(Sig("foo(T, T, T) -> void", {"T", "T", "T"}))); + + EXPECT_THAT(signatures(R"cpp( + template + void foo(T...); + + int main() { + foo(^); + } + )cpp") + .signatures, + ElementsAre(Sig("foo(T...) -> void", {"T..."}))); + + // It is debatable whether we should substitute the outer template parameter + // ('T') in that case. Currently we don't substitute it in signature help, but + // do substitute in code complete. + // FIXME: make code complete and signature help consistent, figure out which + // way is better. + EXPECT_THAT(signatures(R"cpp( + template + struct X { + template + void foo(T, U); + }; + + int main() { + X().foo(^) + } + )cpp") + .signatures, + ElementsAre(Sig("foo(T, U) -> void", {"T", "U"}))); +} + } // namespace } // namespace clangd } // namespace clang From 3084e0febdd6d62e8692757d495085d14a47937d Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 14 Aug 2018 12:00:39 +0000 Subject: [PATCH 042/686] [clangd] NFC: Cleanup clangd help message Add missed space, fix a typo. Reviewed by: ioeric Differential Revision: https://reviews.llvm.org/D50702 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339673 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/tool/ClangdMain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 9e161f5e8..04a7358aa 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -147,7 +147,7 @@ static llvm::cl::opt InputMirrorFile( static llvm::cl::opt EnableIndex( "index", llvm::cl::desc("Enable index-based features such as global code completion " - "and searching for symbols." + "and searching for symbols. " "Clang uses an index built from symbols in opened files"), llvm::cl::init(true)); @@ -160,7 +160,7 @@ static llvm::cl::opt static llvm::cl::opt HeaderInsertionDecorators( "header-insertion-decorators", llvm::cl::desc("Prepend a circular dot or space before the completion " - "label, depending on wether " + "label, depending on whether " "an include line will be inserted or not."), llvm::cl::init(true)); From 90f23df853441efb674152b30fded3afb166bff1 Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Tue, 14 Aug 2018 15:38:59 +0000 Subject: [PATCH 043/686] [clang-doc] Fix unused variable Differential Revision: https://reviews.llvm.org/D50709 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339685 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/Mapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-doc/Mapper.cpp b/clang-doc/Mapper.cpp index 6131a353e..0b00bd1f7 100644 --- a/clang-doc/Mapper.cpp +++ b/clang-doc/Mapper.cpp @@ -30,7 +30,7 @@ template bool MapASTVisitor::mapDecl(const T *D) { return true; // Skip function-internal decls. - if (const DeclContext *F = D->getParentFunctionOrMethod()) + if (D->getParentFunctionOrMethod()) return true; llvm::SmallString<128> USR; From 60f35303d05ac327a1f23602245ba2e57846586c Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 14 Aug 2018 16:03:32 +0000 Subject: [PATCH 044/686] NFC: Enforce good formatting across multiple clang-tools-extra files This patch improves readability of multiple files in clang-tools-extra and enforces LLVM Coding Guidelines. Reviewed by: ioeric Differential Revision: https://reviews.llvm.org/D50707 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339687 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 2 +- clangd/ClangdLSPServer.h | 4 ++-- clangd/ClangdUnit.cpp | 4 ++-- clangd/ClangdUnit.h | 7 ++++--- clangd/CodeComplete.cpp | 6 +++--- clangd/CodeComplete.h | 9 +++++---- clangd/CodeCompletionStrings.cpp | 2 +- clangd/CodeCompletionStrings.h | 7 ++++--- clangd/Compiler.cpp | 4 ++-- clangd/Compiler.h | 9 +++++---- clangd/Context.cpp | 4 ++-- clangd/Diagnostics.cpp | 4 ++-- clangd/Diagnostics.h | 6 +++--- clangd/GlobalCompilationDatabase.cpp | 4 ++-- clangd/GlobalCompilationDatabase.h | 6 +++--- clangd/Quality.cpp | 4 ++-- clangd/Quality.h | 14 ++++++++++---- clangd/XRefs.cpp | 4 ++-- clangd/XRefs.h | 10 ++++++---- .../GlobalSymbolBuilderMain.cpp | 2 +- clangd/index/CanonicalIncludes.h | 2 +- clangd/index/FileIndex.h | 2 +- clangd/index/Index.h | 5 +++-- clangd/index/Merge.cpp | 8 ++++++-- clangd/index/Merge.h | 10 +++++++--- clangd/index/SymbolYAML.h | 2 +- clangd/index/dex/Iterator.h | 2 +- clangd/index/dex/Token.h | 2 +- clangd/index/dex/Trigram.h | 2 +- modularize/ModuleAssistant.cpp | 6 +++--- unittests/clangd/Annotations.cpp | 6 ++++-- unittests/clangd/Annotations.h | 9 ++++++--- unittests/clangd/SyncAPI.cpp | 3 ++- unittests/clangd/SyncAPI.h | 8 ++++++-- unittests/clangd/TestTU.cpp | 3 ++- unittests/clangd/TestTU.h | 9 ++++++--- 36 files changed, 113 insertions(+), 78 deletions(-) diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index 0d3023be4..f71e36183 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -5,7 +5,7 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #include "ClangdLSPServer.h" #include "Diagnostics.h" diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h index e4cbe5001..1d6252dad 100644 --- a/clangd/ClangdLSPServer.h +++ b/clangd/ClangdLSPServer.h @@ -5,7 +5,7 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H @@ -172,4 +172,4 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { } // namespace clangd } // namespace clang -#endif +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H diff --git a/clangd/ClangdUnit.cpp b/clangd/ClangdUnit.cpp index 86e7497a3..ecb597ca7 100644 --- a/clangd/ClangdUnit.cpp +++ b/clangd/ClangdUnit.cpp @@ -1,11 +1,11 @@ -//===--- ClangdUnit.cpp -----------------------------------------*- C++-*-===// +//===--- ClangdUnit.cpp ------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #include "ClangdUnit.h" #include "Compiler.h" diff --git a/clangd/ClangdUnit.h b/clangd/ClangdUnit.h index c7aca17ea..0b12fb1c0 100644 --- a/clangd/ClangdUnit.h +++ b/clangd/ClangdUnit.h @@ -1,11 +1,11 @@ -//===--- ClangdUnit.h -------------------------------------------*- C++-*-===// +//===--- ClangdUnit.h --------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H @@ -168,4 +168,5 @@ void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS); } // namespace clangd } // namespace clang -#endif + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 72e395f1e..f603d1a81 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -1,11 +1,11 @@ -//===--- CodeComplete.cpp ---------------------------------------*- C++-*-===// +//===--- CodeComplete.cpp ----------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// // // Code completion has several moving parts: // - AST-based completions are provided using the completion hooks in Sema. @@ -16,7 +16,7 @@ // Signature help works in a similar way as code completion, but it is simpler: // it's purely AST-based, and there are few candidates. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #include "CodeComplete.h" #include "AST.h" diff --git a/clangd/CodeComplete.h b/clangd/CodeComplete.h index 542d47854..227b49f0d 100644 --- a/clangd/CodeComplete.h +++ b/clangd/CodeComplete.h @@ -1,17 +1,18 @@ -//===--- CodeComplete.h -----------------------------------------*- C++-*-===// +//===--- CodeComplete.h ------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// // // Code completion provides suggestions for what the user might type next. // After "std::string S; S." we might suggest members of std::string. // Signature help describes the parameters of a function as you type them. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H @@ -192,4 +193,4 @@ bool isIndexedForCodeCompletion(const NamedDecl &ND, ASTContext &ASTCtx); } // namespace clangd } // namespace clang -#endif +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H diff --git a/clangd/CodeCompletionStrings.cpp b/clangd/CodeCompletionStrings.cpp index e601e0359..e0277000c 100644 --- a/clangd/CodeCompletionStrings.cpp +++ b/clangd/CodeCompletionStrings.cpp @@ -5,7 +5,7 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #include "CodeCompletionStrings.h" #include "clang/AST/ASTContext.h" diff --git a/clangd/CodeCompletionStrings.h b/clangd/CodeCompletionStrings.h index dac4ba566..7b66c5c99 100644 --- a/clangd/CodeCompletionStrings.h +++ b/clangd/CodeCompletionStrings.h @@ -5,12 +5,13 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// // // Functions for retrieving code completion information from // `CodeCompletionString`. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETIONSTRINGS_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETIONSTRINGS_H @@ -70,4 +71,4 @@ std::string getReturnType(const CodeCompletionString &CCS); } // namespace clangd } // namespace clang -#endif +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETIONSTRINGS_H diff --git a/clangd/Compiler.cpp b/clangd/Compiler.cpp index 8fcc9a97c..9c7a59b49 100644 --- a/clangd/Compiler.cpp +++ b/clangd/Compiler.cpp @@ -1,11 +1,11 @@ -//===--- Compiler.cpp -------------------------------------------*- C++-*-===// +//===--- Compiler.cpp --------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #include "Compiler.h" #include "Logger.h" diff --git a/clangd/Compiler.h b/clangd/Compiler.h index ce058ad28..49c1c90c4 100644 --- a/clangd/Compiler.h +++ b/clangd/Compiler.h @@ -1,17 +1,18 @@ -//===--- Compiler.h ---------------------------------------------*- C++-*-===// +//===--- Compiler.h ----------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// // // Shared utilities for invoking the clang compiler. // ClangdUnit takes care of much of this, but some features like CodeComplete // run their own compile actions that share logic. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_COMPILER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_COMPILER_H @@ -50,4 +51,4 @@ std::unique_ptr prepareCompilerInstance( } // namespace clangd } // namespace clang -#endif +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_COMPILER_H diff --git a/clangd/Context.cpp b/clangd/Context.cpp index 1a9ef24ff..66654c47e 100644 --- a/clangd/Context.cpp +++ b/clangd/Context.cpp @@ -1,11 +1,11 @@ -//===--- Context.cpp -----------------------------------------*- C++-*-===// +//===--- Context.cpp ---------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #include "Context.h" #include diff --git a/clangd/Diagnostics.cpp b/clangd/Diagnostics.cpp index 3c293dde3..c053bd222 100644 --- a/clangd/Diagnostics.cpp +++ b/clangd/Diagnostics.cpp @@ -1,11 +1,11 @@ -//===--- Diagnostics.cpp ----------------------------------------*- C++-*-===// +//===--- Diagnostics.cpp -----------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #include "Diagnostics.h" #include "Compiler.h" diff --git a/clangd/Diagnostics.h b/clangd/Diagnostics.h index a08b9fe04..0370bd36d 100644 --- a/clangd/Diagnostics.h +++ b/clangd/Diagnostics.h @@ -1,11 +1,11 @@ -//===--- Diagnostics.h ------------------------------------------*- C++-*-===// +//===--- Diagnostics.h -------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_DIAGNOSTICS_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_DIAGNOSTICS_H @@ -101,4 +101,4 @@ class StoreDiags : public DiagnosticConsumer { } // namespace clangd } // namespace clang -#endif +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_DIAGNOSTICS_H diff --git a/clangd/GlobalCompilationDatabase.cpp b/clangd/GlobalCompilationDatabase.cpp index 3bfc4eca1..6ed236e93 100644 --- a/clangd/GlobalCompilationDatabase.cpp +++ b/clangd/GlobalCompilationDatabase.cpp @@ -1,11 +1,11 @@ -//===--- GlobalCompilationDatabase.cpp --------------------------*- C++-*-===// +//===--- GlobalCompilationDatabase.cpp ---------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #include "GlobalCompilationDatabase.h" #include "Logger.h" diff --git a/clangd/GlobalCompilationDatabase.h b/clangd/GlobalCompilationDatabase.h index b64028fc6..70e790416 100644 --- a/clangd/GlobalCompilationDatabase.h +++ b/clangd/GlobalCompilationDatabase.h @@ -1,11 +1,11 @@ -//===--- GlobalCompilationDatabase.h ----------------------------*- C++-*-===// +//===--- GlobalCompilationDatabase.h -----------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H @@ -139,4 +139,4 @@ class InMemoryCompilationDb : public GlobalCompilationDatabase { } // namespace clangd } // namespace clang -#endif +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H diff --git a/clangd/Quality.cpp b/clangd/Quality.cpp index f96b50e27..5a9fcd2d4 100644 --- a/clangd/Quality.cpp +++ b/clangd/Quality.cpp @@ -1,11 +1,11 @@ -//===--- Quality.cpp --------------------------------------------*- C++-*-===// +//===--- Quality.cpp ---------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #include "Quality.h" #include "FileDistance.h" #include "URI.h" diff --git a/clangd/Quality.h b/clangd/Quality.h index 86e21963d..fda90560c 100644 --- a/clangd/Quality.h +++ b/clangd/Quality.h @@ -1,11 +1,11 @@ -//===--- Quality.h - Ranking alternatives for ambiguous queries -*- C++-*-===// +//===--- Quality.h - Ranking alternatives for ambiguous queries --*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// /// /// Some operations such as code completion produce a set of candidates. /// Usually the user can choose between them, but we should put the best options @@ -23,21 +23,27 @@ /// - sorting utilities like the TopN container. /// These could be split up further to isolate dependencies if we care. /// -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_QUALITY_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_QUALITY_H + #include "clang/Sema/CodeCompleteConsumer.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include #include #include + namespace llvm { class raw_ostream; } + namespace clang { class CodeCompletionResult; + namespace clangd { + struct Symbol; class URIDistance; @@ -176,4 +182,4 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &, } // namespace clangd } // namespace clang -#endif +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_QUALITY_H diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index f5e2b1e12..a79a8d119 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -1,11 +1,11 @@ -//===--- XRefs.cpp ----------------------------------------------*- C++-*-===// +//===--- XRefs.cpp -----------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #include "XRefs.h" #include "AST.h" #include "Logger.h" diff --git a/clangd/XRefs.h b/clangd/XRefs.h index d698a61c9..30b531951 100644 --- a/clangd/XRefs.h +++ b/clangd/XRefs.h @@ -1,15 +1,16 @@ -//===--- XRefs.h ------------------------------------------------*- C++-*-===// +//===--- XRefs.h -------------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// // // Features that traverse references between symbols. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_XREFS_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_XREFS_H @@ -35,4 +36,5 @@ llvm::Optional getHover(ParsedAST &AST, Position Pos); } // namespace clangd } // namespace clang -#endif + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_XREFS_H diff --git a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp b/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp index 0cc048021..a9f1f98c7 100644 --- a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp +++ b/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp @@ -11,7 +11,7 @@ // whole project. This tools is for **experimental** only. Don't use it in // production code. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #include "index/CanonicalIncludes.h" #include "index/Index.h" diff --git a/clangd/index/CanonicalIncludes.h b/clangd/index/CanonicalIncludes.h index a2fdb04ec..2488f5653 100644 --- a/clangd/index/CanonicalIncludes.h +++ b/clangd/index/CanonicalIncludes.h @@ -15,7 +15,7 @@ // We have a lookup table for common standard library implementations. // libstdc++ puts char_traits in bits/char_traits.h, but we #include . // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_CANONICALINCLUDES_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_CANONICALINCLUDES_H diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index 35c9f7efc..59bf58c6f 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -11,7 +11,7 @@ // maintained at source-file granuality (e.g. with ASTs), and files can be // updated dynamically. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_FILEINDEX_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_FILEINDEX_H diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 6d63335d7..4ae5cd323 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -1,11 +1,11 @@ -//===--- Index.h ------------------------------------------------*- C++-*-===// +//===--- Index.h -------------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_INDEX_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_INDEX_H @@ -386,4 +386,5 @@ class SymbolIndex { } // namespace clangd } // namespace clang + #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_INDEX_H diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index 58d156481..4834bfba6 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -1,18 +1,21 @@ -//===--- Merge.h ------------------------------------------------*- C++-*-===// +//===--- Merge.cpp -----------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + #include "Merge.h" #include "../Logger.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/raw_ostream.h" + namespace clang { namespace clangd { namespace { + using namespace llvm; class MergedIndex : public SymbolIndex { @@ -131,5 +134,6 @@ std::unique_ptr mergeIndex(const SymbolIndex *Dynamic, const SymbolIndex *Static) { return llvm::make_unique(Dynamic, Static); } + } // namespace clangd } // namespace clang diff --git a/clangd/index/Merge.h b/clangd/index/Merge.h index b9b58fd9f..48c3bc6c7 100644 --- a/clangd/index/Merge.h +++ b/clangd/index/Merge.h @@ -1,14 +1,17 @@ -//===--- Merge.h ------------------------------------------------*- C++-*-===// +//===--- Merge.h -------------------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_MERGE_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_MERGE_H + #include "Index.h" + namespace clang { namespace clangd { @@ -26,4 +29,5 @@ std::unique_ptr mergeIndex(const SymbolIndex *Dynamic, } // namespace clangd } // namespace clang -#endif + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_MERGE_H diff --git a/clangd/index/SymbolYAML.h b/clangd/index/SymbolYAML.h index 726af6c66..a4b945c37 100644 --- a/clangd/index/SymbolYAML.h +++ b/clangd/index/SymbolYAML.h @@ -13,7 +13,7 @@ // // This is for **experimental** only. Don't use it in the production code. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOL_FROM_YAML_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOL_FROM_YAML_H diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 317b54964..1616e8687 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -158,4 +158,4 @@ void populateChildren(std::vector> &Children, } // namespace clangd } // namespace clang -#endif +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_ITERATOR_H diff --git a/clangd/index/dex/Token.h b/clangd/index/dex/Token.h index 33a16d36a..f31368cb4 100644 --- a/clangd/index/dex/Token.h +++ b/clangd/index/dex/Token.h @@ -109,4 +109,4 @@ template <> struct DenseMapInfo { } // namespace llvm -#endif +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_DEX_TOKEN_H diff --git a/clangd/index/dex/Trigram.h b/clangd/index/dex/Trigram.h index 00614edf5..a3fc6b32b 100644 --- a/clangd/index/dex/Trigram.h +++ b/clangd/index/dex/Trigram.h @@ -69,4 +69,4 @@ std::vector generateQueryTrigrams(llvm::StringRef Query); } // namespace clangd } // namespace clang -#endif +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_DEX_TRIGRAM_H diff --git a/modularize/ModuleAssistant.cpp b/modularize/ModuleAssistant.cpp index 7f1cb1562..33dca788d 100644 --- a/modularize/ModuleAssistant.cpp +++ b/modularize/ModuleAssistant.cpp @@ -1,11 +1,11 @@ -//===--- ModuleAssistant.cpp - Module map generation manager -*- C++ -*---===// +//===--- ModuleAssistant.cpp - Module map generation manager --*- C++ -*---===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// // // This file defines the module generation entry point function, // createModuleMap, a Module class for representing a module, @@ -27,7 +27,7 @@ // map file using a stream obtained and managed by an // llvm::ToolOutputFile object. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// #include "Modularize.h" #include "llvm/ADT/SmallString.h" diff --git a/unittests/clangd/Annotations.cpp b/unittests/clangd/Annotations.cpp index 6bd81c81a..bf71a96fb 100644 --- a/unittests/clangd/Annotations.cpp +++ b/unittests/clangd/Annotations.cpp @@ -1,16 +1,18 @@ -//===--- Annotations.cpp - Annotated source code for unit tests -*- C++-*-===// +//===--- Annotations.cpp - Annotated source code for unit tests --*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + #include "Annotations.h" #include "SourceCode.h" namespace clang { namespace clangd { + using namespace llvm; // Crash if the assertion fails, printing the message and testcase. diff --git a/unittests/clangd/Annotations.h b/unittests/clangd/Annotations.h index 549a21e7b..4d787c254 100644 --- a/unittests/clangd/Annotations.h +++ b/unittests/clangd/Annotations.h @@ -1,11 +1,11 @@ -//===--- Annotations.h - Annotated source code for tests --------*- C++-*-===// +//===--- Annotations.h - Annotated source code for tests ---------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// // // Annotations lets you mark points and ranges inside source code, for tests: // @@ -27,8 +27,10 @@ // to define general overlapping ranges. // //===---------------------------------------------------------------------===// + #ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_ANNOTATIONS_H #define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_ANNOTATIONS_H + #include "Protocol.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" @@ -66,4 +68,5 @@ class Annotations { } // namespace clangd } // namespace clang -#endif + +#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_ANNOTATIONS_H diff --git a/unittests/clangd/SyncAPI.cpp b/unittests/clangd/SyncAPI.cpp index ae9ece590..a033db930 100644 --- a/unittests/clangd/SyncAPI.cpp +++ b/unittests/clangd/SyncAPI.cpp @@ -5,7 +5,8 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + #include "SyncAPI.h" namespace clang { diff --git a/unittests/clangd/SyncAPI.h b/unittests/clangd/SyncAPI.h index ddf90d81d..4bb65c670 100644 --- a/unittests/clangd/SyncAPI.h +++ b/unittests/clangd/SyncAPI.h @@ -5,10 +5,14 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// +// // This file contains synchronous versions of ClangdServer's async API. We // deliberately don't expose the sync API outside tests to encourage using the // async versions in clangd code. +// +//===----------------------------------------------------------------------===// + #ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_SYNCAPI_H #define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_SYNCAPI_H @@ -49,4 +53,4 @@ runDocumentSymbols(ClangdServer &Server, PathRef File); } // namespace clangd } // namespace clang -#endif +#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_SYNCAPI_H diff --git a/unittests/clangd/TestTU.cpp b/unittests/clangd/TestTU.cpp index b47d94484..c2b97a9a0 100644 --- a/unittests/clangd/TestTU.cpp +++ b/unittests/clangd/TestTU.cpp @@ -5,7 +5,8 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + #include "TestTU.h" #include "TestFS.h" #include "index/FileIndex.h" diff --git a/unittests/clangd/TestTU.h b/unittests/clangd/TestTU.h index b66159e3a..3995219e1 100644 --- a/unittests/clangd/TestTU.h +++ b/unittests/clangd/TestTU.h @@ -1,11 +1,11 @@ -//===--- TestTU.h - Scratch source files for testing ------------*- C++-*-===// +//===--- TestTU.h - Scratch source files for testing -------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // -//===---------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// // // Many tests for indexing, code completion etc are most naturally expressed // using code examples. @@ -14,8 +14,10 @@ // AST, particular symbols, etc. // //===---------------------------------------------------------------------===// + #ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTTU_H #define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTTU_H + #include "ClangdUnit.h" #include "index/Index.h" #include "gtest/gtest.h" @@ -64,4 +66,5 @@ const NamedDecl &findAnyDecl(ParsedAST &AST, llvm::StringRef Name); } // namespace clangd } // namespace clang -#endif + +#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTTU_H From d86c3fe8f381532dd63d8f856aaee45b2d968395 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Tue, 14 Aug 2018 22:20:35 +0000 Subject: [PATCH 045/686] [clangd] add missing test from r339454 I forgot to checkin the test for the fixits into SVN. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339737 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clangd/fixits-embed-in-diagnostic.test | 66 +++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 test/clangd/fixits-embed-in-diagnostic.test diff --git a/test/clangd/fixits-embed-in-diagnostic.test b/test/clangd/fixits-embed-in-diagnostic.test new file mode 100644 index 000000000..cd68ea81d --- /dev/null +++ b/test/clangd/fixits-embed-in-diagnostic.test @@ -0,0 +1,66 @@ +# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"publishDiagnostics":{"clangdFixSupport":true}}},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"struct Point {}; union Point p;"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "clangd_fixes": [ +# CHECK-NEXT: { +# CHECK-NEXT: "edit": { +# CHECK-NEXT: "changes": { +# CHECK-NEXT: "file://{{.*}}/foo.c": [ +# CHECK-NEXT: { +# CHECK-NEXT: "newText": "struct", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 22, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 17, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "title": "change 'union' to 'struct'" +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "message": "Use of 'Point' with tag type that does not match previous declaration\n\nfoo.c:1:8: note: previous use is here", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 22, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 17, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 1 +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "message": "Previous use is here\n\nfoo.c:1:18: error: use of 'Point' with tag type that does not match previous declaration", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 12, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 7, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 3 +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c" +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","id":4,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} From 468e441bf6b91429c3f9b49a3336bf07651e8de0 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Tue, 14 Aug 2018 22:21:40 +0000 Subject: [PATCH 046/686] [clangd] add an extension field to LSP to transfer the diagnostic's category This patch adds a 'category' extension field to the LSP diagnostic that's sent by Clangd. This extension is always on by default. Differential Revision: https://reviews.llvm.org/D50571 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339738 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 2 ++ clangd/Diagnostics.cpp | 4 ++++ clangd/Diagnostics.h | 1 + clangd/Protocol.h | 6 ++++++ test/clangd/compile-commands-path-in-initialize.test | 2 ++ test/clangd/compile-commands-path.test | 3 +++ test/clangd/diagnostics.test | 1 + test/clangd/did-change-configuration-params.test | 1 + test/clangd/execute-command.test | 1 + test/clangd/extra-flags.test | 2 ++ test/clangd/fixits.test | 1 + 11 files changed, 24 insertions(+) diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index f71e36183..85e14e884 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -506,6 +506,8 @@ void ClangdLSPServer::onDiagnosticsReady(PathRef File, } LSPDiag["clangd_fixes"] = std::move(ClangdFixes); } + if (!Diag.category.empty()) + LSPDiag["category"] = Diag.category; DiagnosticsJSON.push_back(std::move(LSPDiag)); auto &FixItsForDiagnostic = LocalFixIts[Diag]; diff --git a/clangd/Diagnostics.cpp b/clangd/Diagnostics.cpp index c053bd222..893854d1d 100644 --- a/clangd/Diagnostics.cpp +++ b/clangd/Diagnostics.cpp @@ -226,6 +226,7 @@ void toLSPDiags( clangd::Diagnostic Res; Res.range = D.Range; Res.severity = getSeverity(D.Severity); + Res.category = D.Category; return Res; }; @@ -292,6 +293,9 @@ void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, D.InsideMainFile = InsideMainFile; D.File = Info.getSourceManager().getFilename(Info.getLocation()); D.Severity = DiagLevel; + D.Category = DiagnosticIDs::getCategoryNameFromID( + DiagnosticIDs::getCategoryNumberForDiag(Info.getID())) + .str(); return D; }; diff --git a/clangd/Diagnostics.h b/clangd/Diagnostics.h index 0370bd36d..89612c907 100644 --- a/clangd/Diagnostics.h +++ b/clangd/Diagnostics.h @@ -37,6 +37,7 @@ struct DiagBase { std::string File; clangd::Range Range; DiagnosticsEngine::Level Severity = DiagnosticsEngine::Note; + std::string Category; // Since File is only descriptive, we store a separate flag to distinguish // diags from the main file. bool InsideMainFile = false; diff --git a/clangd/Protocol.h b/clangd/Protocol.h index f533c97f4..1de5ced82 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -544,6 +544,12 @@ struct Diagnostic { /// The diagnostic's message. std::string message; + + /// The diagnostic's category. Can be omitted. + /// An LSP extension that's used to send the name of the category over to the + /// client. The category typically describes the compilation stage during + /// which the issue was produced, e.g. "Semantic Issue" or "Parse Issue". + std::string category; }; /// A LSP-specific comparator used to find diagnostic in a container like diff --git a/test/clangd/compile-commands-path-in-initialize.test b/test/clangd/compile-commands-path-in-initialize.test index b34c59525..17b4333ae 100644 --- a/test/clangd/compile-commands-path-in-initialize.test +++ b/test/clangd/compile-commands-path-in-initialize.test @@ -23,6 +23,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { +# CHECK-NEXT: "category": "#pragma message Directive", # CHECK-NEXT: "message": "MACRO is one", --- {"jsonrpc":"2.0","id":0,"method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabasePath":"INPUT_DIR/build-2"}}} @@ -30,6 +31,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { +# CHECK-NEXT: "category": "#pragma message Directive", # CHECK-NEXT: "message": "MACRO is two", --- {"jsonrpc":"2.0","id":10000,"method":"shutdown"} diff --git a/test/clangd/compile-commands-path.test b/test/clangd/compile-commands-path.test index f25d002f9..693101ccd 100644 --- a/test/clangd/compile-commands-path.test +++ b/test/clangd/compile-commands-path.test @@ -23,6 +23,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { +# CHECK-NEXT: "category": "#pragma message Directive", # CHECK-NEXT: "message": "MACRO is not defined", --- {"jsonrpc":"2.0","id":0,"method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabasePath":"INPUT_DIR/build-1"}}} @@ -30,6 +31,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { +# CHECK-NEXT: "category": "#pragma message Directive", # CHECK-NEXT: "message": "MACRO is one", --- {"jsonrpc":"2.0","id":0,"method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabasePath":"INPUT_DIR/build-2"}}} @@ -37,6 +39,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { +# CHECK-NEXT: "category": "#pragma message Directive", # CHECK-NEXT: "message": "MACRO is two", --- {"jsonrpc":"2.0","id":10000,"method":"shutdown"} diff --git a/test/clangd/diagnostics.test b/test/clangd/diagnostics.test index a191c0822..8a9feab3a 100644 --- a/test/clangd/diagnostics.test +++ b/test/clangd/diagnostics.test @@ -6,6 +6,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { +# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "message": "Return type of 'main' is not 'int'", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { diff --git a/test/clangd/did-change-configuration-params.test b/test/clangd/did-change-configuration-params.test index 51b4a8747..d1caf7148 100644 --- a/test/clangd/did-change-configuration-params.test +++ b/test/clangd/did-change-configuration-params.test @@ -24,6 +24,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { +# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "message": "Variable 'i' is uninitialized when used here", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { diff --git a/test/clangd/execute-command.test b/test/clangd/execute-command.test index 492006fdf..b907f0a9b 100644 --- a/test/clangd/execute-command.test +++ b/test/clangd/execute-command.test @@ -6,6 +6,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { +# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { diff --git a/test/clangd/extra-flags.test b/test/clangd/extra-flags.test index f2460eb12..07b64cd34 100644 --- a/test/clangd/extra-flags.test +++ b/test/clangd/extra-flags.test @@ -6,6 +6,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { +# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "message": "Variable 'i' is uninitialized when used here", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { @@ -28,6 +29,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { +# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "message": "Variable 'i' is uninitialized when used here", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { diff --git a/test/clangd/fixits.test b/test/clangd/fixits.test index ce74d1c8b..f99a5a9ba 100644 --- a/test/clangd/fixits.test +++ b/test/clangd/fixits.test @@ -6,6 +6,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { +# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { From e95bf3301611765f8b8ad08640f1029aeee77362 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Tue, 14 Aug 2018 22:27:03 +0000 Subject: [PATCH 047/686] [clangd] update the new test to check for diagnostic's category as well git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339739 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clangd/fixits-embed-in-diagnostic.test | 1 + 1 file changed, 1 insertion(+) diff --git a/test/clangd/fixits-embed-in-diagnostic.test b/test/clangd/fixits-embed-in-diagnostic.test index cd68ea81d..4d1039521 100644 --- a/test/clangd/fixits-embed-in-diagnostic.test +++ b/test/clangd/fixits-embed-in-diagnostic.test @@ -6,6 +6,7 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { +# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "clangd_fixes": [ # CHECK-NEXT: { # CHECK-NEXT: "edit": { From b625be35b7d82476e7dd821da23413dbe1b5eb9b Mon Sep 17 00:00:00 2001 From: Jan Korous Date: Wed, 15 Aug 2018 15:50:45 +0000 Subject: [PATCH 048/686] [clangd][tests] Fix typo in tests - invalid LSP exit message Syntactically invalid JSON payload was causing clangd to terminate because of unexpected EOF rather than exit as a response to LSP exit message. Differential Revision: https://reviews.llvm.org/D50641 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339781 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clangd/extra-flags.test | 2 +- test/clangd/formatting.test | 2 +- test/clangd/initialize-params.test | 2 +- test/clangd/shutdown-with-exit.test | 2 +- test/clangd/shutdown-without-exit.test | 2 +- test/clangd/unsupported-method.test | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/clangd/extra-flags.test b/test/clangd/extra-flags.test index 07b64cd34..8ba3c502e 100644 --- a/test/clangd/extra-flags.test +++ b/test/clangd/extra-flags.test @@ -49,6 +49,6 @@ --- {"jsonrpc":"2.0","id":5,"method":"shutdown"} --- -{"jsonrpc":"2.0":"method":"exit"} +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/formatting.test b/test/clangd/formatting.test index 8e0ae5bec..9f8f3db9a 100644 --- a/test/clangd/formatting.test +++ b/test/clangd/formatting.test @@ -184,4 +184,4 @@ --- {"jsonrpc":"2.0","id":6,"method":"shutdown"} --- -{"jsonrpc":"2.0":"method":"exit"} +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/initialize-params.test b/test/clangd/initialize-params.test index a04d70ad7..d22bf80ef 100644 --- a/test/clangd/initialize-params.test +++ b/test/clangd/initialize-params.test @@ -46,4 +46,4 @@ # CHECK-NEXT: "jsonrpc": "2.0", # CHECK-NEXT: "result": null --- -{"jsonrpc":"2.0":"method":"exit"} +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/shutdown-with-exit.test b/test/clangd/shutdown-with-exit.test index b2475299a..41c3b993b 100644 --- a/test/clangd/shutdown-with-exit.test +++ b/test/clangd/shutdown-with-exit.test @@ -1,4 +1,4 @@ # RUN: clangd -lit-test < %s {"jsonrpc":"2.0","id":3,"method":"shutdown"} --- -{"jsonrpc":"2.0":"method":"exit"} +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/shutdown-without-exit.test b/test/clangd/shutdown-without-exit.test index a3e86e9a2..106e578b6 100644 --- a/test/clangd/shutdown-without-exit.test +++ b/test/clangd/shutdown-without-exit.test @@ -1,2 +1,2 @@ # RUN: not clangd -lit-test < %s -{"jsonrpc":"2.0":"method":"exit"} +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/unsupported-method.test b/test/clangd/unsupported-method.test index 7737b4f5a..9cdb12c3d 100644 --- a/test/clangd/unsupported-method.test +++ b/test/clangd/unsupported-method.test @@ -13,4 +13,4 @@ --- {"jsonrpc":"2.0","id":2,"method":"shutdown"} --- -{"jsonrpc":"2.0":"method":"exit"} +{"jsonrpc":"2.0","method":"exit"} From 9b4ea89a56f3ca45c9b6f29d5e993cd09e7d3588 Mon Sep 17 00:00:00 2001 From: Jan Korous Date: Wed, 15 Aug 2018 15:58:05 +0000 Subject: [PATCH 049/686] [clangd][tests] Rename tests of clangd instance termination Just making testnames better reflect their testing scenarios. Differential Revision: https://reviews.llvm.org/D50641 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339782 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clangd/{shutdown-with-exit.test => exit-with-shutdown.test} | 0 .../{shutdown-without-exit.test => exit-without-shutdown.test} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename test/clangd/{shutdown-with-exit.test => exit-with-shutdown.test} (100%) rename test/clangd/{shutdown-without-exit.test => exit-without-shutdown.test} (100%) diff --git a/test/clangd/shutdown-with-exit.test b/test/clangd/exit-with-shutdown.test similarity index 100% rename from test/clangd/shutdown-with-exit.test rename to test/clangd/exit-with-shutdown.test diff --git a/test/clangd/shutdown-without-exit.test b/test/clangd/exit-without-shutdown.test similarity index 100% rename from test/clangd/shutdown-without-exit.test rename to test/clangd/exit-without-shutdown.test From a6fbca7eb5dc1b88a7f6d4cbada6d3d451d8bf25 Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Wed, 15 Aug 2018 16:02:28 +0000 Subject: [PATCH 050/686] Reland "[clang-doc] Updating BitcodeReader to use llvm::Error"" With explicit unique_ptr casts so that bots with older compilers don't break. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339783 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/BitcodeReader.cpp | 447 ++++++++++++++++++++---------------- clang-doc/BitcodeReader.h | 18 +- 2 files changed, 257 insertions(+), 208 deletions(-) diff --git a/clang-doc/BitcodeReader.cpp b/clang-doc/BitcodeReader.cpp index 7acf107db..dee0930bc 100644 --- a/clang-doc/BitcodeReader.cpp +++ b/clang-doc/BitcodeReader.cpp @@ -10,6 +10,7 @@ #include "BitcodeReader.h" #include "llvm/ADT/IndexedMap.h" #include "llvm/ADT/Optional.h" +#include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" namespace clang { @@ -17,49 +18,53 @@ namespace doc { using Record = llvm::SmallVector; -bool decodeRecord(Record R, llvm::SmallVectorImpl &Field, - llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, llvm::SmallVectorImpl &Field, + llvm::StringRef Blob) { Field.assign(Blob.begin(), Blob.end()); - return true; + return llvm::Error::success(); } -bool decodeRecord(Record R, SymbolID &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, SymbolID &Field, llvm::StringRef Blob) { if (R[0] != BitCodeConstants::USRHashSize) - return false; + return llvm::make_error("Incorrect USR size.\n", + llvm::inconvertibleErrorCode()); // First position in the record is the length of the following array, so we // copy the following elements to the field. for (int I = 0, E = R[0]; I < E; ++I) Field[I] = R[I + 1]; - return true; + return llvm::Error::success(); } -bool decodeRecord(Record R, bool &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, bool &Field, llvm::StringRef Blob) { Field = R[0] != 0; - return true; + return llvm::Error::success(); } -bool decodeRecord(Record R, int &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, int &Field, llvm::StringRef Blob) { if (R[0] > INT_MAX) - return false; + return llvm::make_error("Integer too large to parse.\n", + llvm::inconvertibleErrorCode()); Field = (int)R[0]; - return true; + return llvm::Error::success(); } -bool decodeRecord(Record R, AccessSpecifier &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, AccessSpecifier &Field, + llvm::StringRef Blob) { switch (R[0]) { case AS_public: case AS_private: case AS_protected: case AS_none: Field = (AccessSpecifier)R[0]; - return true; + return llvm::Error::success(); default: - return false; + return llvm::make_error( + "Invalid value for AccessSpecifier.\n", llvm::inconvertibleErrorCode()); } } -bool decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) { switch (R[0]) { case TTK_Struct: case TTK_Interface: @@ -67,21 +72,23 @@ bool decodeRecord(Record R, TagTypeKind &Field, llvm::StringRef Blob) { case TTK_Class: case TTK_Enum: Field = (TagTypeKind)R[0]; - return true; + return llvm::Error::success(); default: - return false; + return llvm::make_error( + "Invalid value for TagTypeKind.\n", llvm::inconvertibleErrorCode()); } } -bool decodeRecord(Record R, llvm::Optional &Field, - llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, llvm::Optional &Field, + llvm::StringRef Blob) { if (R[0] > INT_MAX) - return false; + return llvm::make_error("Integer too large to parse.\n", + llvm::inconvertibleErrorCode()); Field.emplace((int)R[0], Blob); - return true; + return llvm::Error::success(); } -bool decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) { switch (auto IT = static_cast(R[0])) { case InfoType::IT_namespace: case InfoType::IT_record: @@ -89,12 +96,13 @@ bool decodeRecord(Record R, InfoType &Field, llvm::StringRef Blob) { case InfoType::IT_default: case InfoType::IT_enum: Field = IT; - return true; + return llvm::Error::success(); } - return false; + return llvm::make_error("Invalid value for InfoType.\n", + llvm::inconvertibleErrorCode()); } -bool decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { switch (auto F = static_cast(R[0])) { case FieldId::F_namespace: case FieldId::F_parent: @@ -104,45 +112,51 @@ bool decodeRecord(Record R, FieldId &Field, llvm::StringRef Blob) { case FieldId::F_child_record: case FieldId::F_default: Field = F; - return true; + return llvm::Error::success(); } - return false; + return llvm::make_error("Invalid value for FieldId.\n", + llvm::inconvertibleErrorCode()); } -bool decodeRecord(Record R, llvm::SmallVectorImpl> &Field, - llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, + llvm::SmallVectorImpl> &Field, + llvm::StringRef Blob) { Field.push_back(Blob); - return true; + return llvm::Error::success(); } -bool decodeRecord(Record R, llvm::SmallVectorImpl &Field, - llvm::StringRef Blob) { +llvm::Error decodeRecord(Record R, llvm::SmallVectorImpl &Field, + llvm::StringRef Blob) { if (R[0] > INT_MAX) - return false; + return llvm::make_error("Integer too large to parse.\n", + llvm::inconvertibleErrorCode()); Field.emplace_back((int)R[0], Blob); - return true; + return llvm::Error::success(); } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - const unsigned VersionNo) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + const unsigned VersionNo) { if (ID == VERSION && R[0] == VersionNo) - return true; - return false; + return llvm::Error::success(); + return llvm::make_error( + "Mismatched bitcode version number.\n", llvm::inconvertibleErrorCode()); } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - NamespaceInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + NamespaceInfo *I) { switch (ID) { case NAMESPACE_USR: return decodeRecord(R, I->USR, Blob); case NAMESPACE_NAME: return decodeRecord(R, I->Name, Blob); default: - return false; + return llvm::make_error( + "Invalid field for NamespaceInfo.\n", llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, RecordInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + RecordInfo *I) { switch (ID) { case RECORD_USR: return decodeRecord(R, I->USR, Blob); @@ -155,11 +169,13 @@ bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, RecordInfo *I) { case RECORD_TAG_TYPE: return decodeRecord(R, I->TagType, Blob); default: - return false; + return llvm::make_error( + "Invalid field for RecordInfo.\n", llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, EnumInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + EnumInfo *I) { switch (ID) { case ENUM_USR: return decodeRecord(R, I->USR, Blob); @@ -174,11 +190,13 @@ bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, EnumInfo *I) { case ENUM_SCOPED: return decodeRecord(R, I->Scoped, Blob); default: - return false; + return llvm::make_error("Invalid field for EnumInfo.\n", + llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, FunctionInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + FunctionInfo *I) { switch (ID) { case FUNCTION_USR: return decodeRecord(R, I->USR, Blob); @@ -193,37 +211,42 @@ bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, FunctionInfo *I) { case FUNCTION_IS_METHOD: return decodeRecord(R, I->IsMethod, Blob); default: - return false; + return llvm::make_error( + "Invalid field for FunctionInfo.\n", llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, TypeInfo *I) { - return true; +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + TypeInfo *I) { + return llvm::Error::success(); } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - FieldTypeInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + FieldTypeInfo *I) { switch (ID) { case FIELD_TYPE_NAME: return decodeRecord(R, I->Name, Blob); default: - return false; + return llvm::make_error("Invalid field for TypeInfo.\n", + llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, - MemberTypeInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + MemberTypeInfo *I) { switch (ID) { case MEMBER_TYPE_NAME: return decodeRecord(R, I->Name, Blob); case MEMBER_TYPE_ACCESS: return decodeRecord(R, I->Access, Blob); default: - return false; + return llvm::make_error( + "Invalid field for MemberTypeInfo.\n", llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, CommentInfo *I) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + CommentInfo *I) { switch (ID) { case COMMENT_KIND: return decodeRecord(R, I->Kind, Blob); @@ -248,12 +271,13 @@ bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, CommentInfo *I) { case COMMENT_EXPLICIT: return decodeRecord(R, I->Explicit, Blob); default: - return false; + return llvm::make_error( + "Invalid field for CommentInfo.\n", llvm::inconvertibleErrorCode()); } } -bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, Reference *I, - FieldId &F) { +llvm::Error parseRecord(Record R, unsigned ID, llvm::StringRef Blob, + Reference *I, FieldId &F) { switch (ID) { case REFERENCE_USR: return decodeRecord(R, I->USR, Blob); @@ -264,159 +288,178 @@ bool parseRecord(Record R, unsigned ID, llvm::StringRef Blob, Reference *I, case REFERENCE_FIELD: return decodeRecord(R, F, Blob); default: - return false; + return llvm::make_error("Invalid field for Reference.\n", + llvm::inconvertibleErrorCode()); } } -template CommentInfo *getCommentInfo(T I) { - llvm::errs() << "Cannot have comment subblock.\n"; - exit(1); +template llvm::Expected getCommentInfo(T I) { + return llvm::make_error( + "Invalid type cannot contain CommentInfo.\n", + llvm::inconvertibleErrorCode()); } -template <> CommentInfo *getCommentInfo(FunctionInfo *I) { +template <> llvm::Expected getCommentInfo(FunctionInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } -template <> CommentInfo *getCommentInfo(NamespaceInfo *I) { +template <> llvm::Expected getCommentInfo(NamespaceInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } -template <> CommentInfo *getCommentInfo(RecordInfo *I) { +template <> llvm::Expected getCommentInfo(RecordInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } -template <> CommentInfo *getCommentInfo(EnumInfo *I) { +template <> llvm::Expected getCommentInfo(EnumInfo *I) { I->Description.emplace_back(); return &I->Description.back(); } -template <> CommentInfo *getCommentInfo(CommentInfo *I) { +template <> llvm::Expected getCommentInfo(CommentInfo *I) { I->Children.emplace_back(llvm::make_unique()); return I->Children.back().get(); } -template <> CommentInfo *getCommentInfo(std::unique_ptr &I) { +template <> +llvm::Expected getCommentInfo(std::unique_ptr &I) { return getCommentInfo(I.get()); } template -void addTypeInfo(T I, TTypeInfo &&TI) { - llvm::errs() << "Invalid type for info.\n"; - exit(1); +llvm::Error addTypeInfo(T I, TTypeInfo &&TI) { + return llvm::make_error( + "Invalid type cannot contain TypeInfo.\n", + llvm::inconvertibleErrorCode()); } -template <> void addTypeInfo(RecordInfo *I, MemberTypeInfo &&T) { +template <> llvm::Error addTypeInfo(RecordInfo *I, MemberTypeInfo &&T) { I->Members.emplace_back(std::move(T)); + return llvm::Error::success(); } -template <> void addTypeInfo(FunctionInfo *I, TypeInfo &&T) { +template <> llvm::Error addTypeInfo(FunctionInfo *I, TypeInfo &&T) { I->ReturnType = std::move(T); + return llvm::Error::success(); } -template <> void addTypeInfo(FunctionInfo *I, FieldTypeInfo &&T) { +template <> llvm::Error addTypeInfo(FunctionInfo *I, FieldTypeInfo &&T) { I->Params.emplace_back(std::move(T)); + return llvm::Error::success(); } -template void addReference(T I, Reference &&R, FieldId F) { - llvm::errs() << "Invalid field type for info.\n"; - exit(1); +template llvm::Error addReference(T I, Reference &&R, FieldId F) { + return llvm::make_error( + "Invalid type cannot contain Reference\n", + llvm::inconvertibleErrorCode()); } -template <> void addReference(TypeInfo *I, Reference &&R, FieldId F) { +template <> llvm::Error addReference(TypeInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_type: I->Type = std::move(R); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } -template <> void addReference(FieldTypeInfo *I, Reference &&R, FieldId F) { +template <> +llvm::Error addReference(FieldTypeInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_type: I->Type = std::move(R); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } -template <> void addReference(MemberTypeInfo *I, Reference &&R, FieldId F) { +template <> +llvm::Error addReference(MemberTypeInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_type: I->Type = std::move(R); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } -template <> void addReference(EnumInfo *I, Reference &&R, FieldId F) { +template <> llvm::Error addReference(EnumInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } -template <> void addReference(NamespaceInfo *I, Reference &&R, FieldId F) { +template <> +llvm::Error addReference(NamespaceInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); - break; + return llvm::Error::success(); case FieldId::F_child_namespace: I->ChildNamespaces.emplace_back(std::move(R)); - break; + return llvm::Error::success(); case FieldId::F_child_record: I->ChildRecords.emplace_back(std::move(R)); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } -template <> void addReference(FunctionInfo *I, Reference &&R, FieldId F) { +template <> +llvm::Error addReference(FunctionInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); - break; + return llvm::Error::success(); case FieldId::F_parent: I->Parent = std::move(R); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } -template <> void addReference(RecordInfo *I, Reference &&R, FieldId F) { +template <> llvm::Error addReference(RecordInfo *I, Reference &&R, FieldId F) { switch (F) { case FieldId::F_namespace: I->Namespace.emplace_back(std::move(R)); - break; + return llvm::Error::success(); case FieldId::F_parent: I->Parents.emplace_back(std::move(R)); - break; + return llvm::Error::success(); case FieldId::F_vparent: I->VirtualParents.emplace_back(std::move(R)); - break; + return llvm::Error::success(); case FieldId::F_child_record: I->ChildRecords.emplace_back(std::move(R)); - break; + return llvm::Error::success(); default: - llvm::errs() << "Invalid field type for info.\n"; - exit(1); + return llvm::make_error( + "Invalid type cannot contain Reference.\n", + llvm::inconvertibleErrorCode()); } } @@ -443,14 +486,16 @@ template <> void addChild(RecordInfo *I, EnumInfo &&R) { } // Read records from bitcode into a given info. -template bool ClangDocBitcodeReader::readRecord(unsigned ID, T I) { +template +llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, T I) { Record R; llvm::StringRef Blob; unsigned RecID = Stream.readRecord(ID, R, &Blob); return parseRecord(R, RecID, Blob, I); } -template <> bool ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) { +template <> +llvm::Error ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) { Record R; llvm::StringRef Blob; unsigned RecID = Stream.readRecord(ID, R, &Blob); @@ -458,9 +503,11 @@ template <> bool ClangDocBitcodeReader::readRecord(unsigned ID, Reference *I) { } // Read a block of records into a single info. -template bool ClangDocBitcodeReader::readBlock(unsigned ID, T I) { +template +llvm::Error ClangDocBitcodeReader::readBlock(unsigned ID, T I) { if (Stream.EnterSubBlock(ID)) - return false; + return llvm::make_error("Unable to enter subblock.\n", + llvm::inconvertibleErrorCode()); while (true) { unsigned BlockOrCode = 0; @@ -468,83 +515,87 @@ template bool ClangDocBitcodeReader::readBlock(unsigned ID, T I) { switch (Res) { case Cursor::BadBlock: - return false; + return llvm::make_error( + "Bad block found.\n", llvm::inconvertibleErrorCode()); case Cursor::BlockEnd: - return true; + return llvm::Error::success(); case Cursor::BlockBegin: - if (readSubBlock(BlockOrCode, I)) - continue; - if (!Stream.SkipBlock()) - return false; + if (auto Err = readSubBlock(BlockOrCode, I)) { + if (!Stream.SkipBlock()) + continue; + return Err; + } continue; case Cursor::Record: break; } - if (!readRecord(BlockOrCode, I)) - return false; + if (auto Err = readRecord(BlockOrCode, I)) + return Err; } } template -bool ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) { +llvm::Error ClangDocBitcodeReader::readSubBlock(unsigned ID, T I) { switch (ID) { // Blocks can only have Comment, Reference, TypeInfo, FunctionInfo, or // EnumInfo subblocks - case BI_COMMENT_BLOCK_ID: - if (readBlock(ID, getCommentInfo(I))) - return true; - return false; + case BI_COMMENT_BLOCK_ID: { + auto Comment = getCommentInfo(I); + if (!Comment) + return Comment.takeError(); + if (auto Err = readBlock(ID, Comment.get())) + return Err; + return llvm::Error::success(); + } case BI_TYPE_BLOCK_ID: { TypeInfo TI; - if (readBlock(ID, &TI)) { - addTypeInfo(I, std::move(TI)); - return true; - } - return false; + if (auto Err = readBlock(ID, &TI)) + return Err; + if (auto Err = addTypeInfo(I, std::move(TI))) + return Err; + return llvm::Error::success(); } case BI_FIELD_TYPE_BLOCK_ID: { FieldTypeInfo TI; - if (readBlock(ID, &TI)) { - addTypeInfo(I, std::move(TI)); - return true; - } - return false; + if (auto Err = readBlock(ID, &TI)) + return Err; + if (auto Err = addTypeInfo(I, std::move(TI))) + return Err; + return llvm::Error::success(); } case BI_MEMBER_TYPE_BLOCK_ID: { MemberTypeInfo TI; - if (readBlock(ID, &TI)) { - addTypeInfo(I, std::move(TI)); - return true; - } - return false; + if (auto Err = readBlock(ID, &TI)) + return Err; + if (auto Err = addTypeInfo(I, std::move(TI))) + return Err; + return llvm::Error::success(); } case BI_REFERENCE_BLOCK_ID: { Reference R; - if (readBlock(ID, &R)) { - addReference(I, std::move(R), CurrentReferenceField); - return true; - } - return false; + if (auto Err = readBlock(ID, &R)) + return Err; + if (auto Err = addReference(I, std::move(R), CurrentReferenceField)) + return Err; + return llvm::Error::success(); } case BI_FUNCTION_BLOCK_ID: { FunctionInfo F; - if (readBlock(ID, &F)) { - addChild(I, std::move(F)); - return true; - } - return false; + if (auto Err = readBlock(ID, &F)) + return Err; + addChild(I, std::move(F)); + return llvm::Error::success(); } case BI_ENUM_BLOCK_ID: { EnumInfo E; - if (readBlock(ID, &E)) { - addChild(I, std::move(E)); - return true; - } - return false; + if (auto Err = readBlock(ID, &E)) + return Err; + addChild(I, std::move(E)); + return llvm::Error::success(); } default: - llvm::errs() << "Invalid subblock type.\n"; - return false; + return llvm::make_error("Invalid subblock type.\n", + llvm::inconvertibleErrorCode()); } } @@ -576,37 +627,41 @@ ClangDocBitcodeReader::skipUntilRecordOrBlock(unsigned &BlockOrRecordID) { llvm_unreachable("Premature stream end."); } -bool ClangDocBitcodeReader::validateStream() { +llvm::Error ClangDocBitcodeReader::validateStream() { if (Stream.AtEndOfStream()) - return false; + return llvm::make_error("Premature end of stream.\n", + llvm::inconvertibleErrorCode()); // Sniff for the signature. if (Stream.Read(8) != BitCodeConstants::Signature[0] || Stream.Read(8) != BitCodeConstants::Signature[1] || Stream.Read(8) != BitCodeConstants::Signature[2] || Stream.Read(8) != BitCodeConstants::Signature[3]) - return false; - return true; + return llvm::make_error("Invalid bitcode signature.\n", + llvm::inconvertibleErrorCode()); + return llvm::Error::success(); } -bool ClangDocBitcodeReader::readBlockInfoBlock() { +llvm::Error ClangDocBitcodeReader::readBlockInfoBlock() { BlockInfo = Stream.ReadBlockInfoBlock(); if (!BlockInfo) - return false; + return llvm::make_error( + "Unable to parse BlockInfoBlock.\n", llvm::inconvertibleErrorCode()); Stream.setBlockInfo(&*BlockInfo); - return true; + return llvm::Error::success(); } template -std::unique_ptr ClangDocBitcodeReader::createInfo(unsigned ID) { +llvm::Expected> +ClangDocBitcodeReader::createInfo(unsigned ID) { std::unique_ptr I = llvm::make_unique(); - if (readBlock(ID, static_cast(I.get()))) - return I; - llvm::errs() << "Error reading from block.\n"; - return nullptr; + if (auto Err = readBlock(ID, static_cast(I.get()))) + return std::move(Err); + return I; } -std::unique_ptr ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { +llvm::Expected> +ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { switch (ID) { case BI_NAMESPACE_BLOCK_ID: return createInfo(ID); @@ -617,8 +672,8 @@ std::unique_ptr ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { case BI_FUNCTION_BLOCK_ID: return createInfo(ID); default: - llvm::errs() << "Error reading from block.\n"; - return nullptr; + return llvm::make_error("Cannot create info.\n", + llvm::inconvertibleErrorCode()); } } @@ -626,19 +681,15 @@ std::unique_ptr ClangDocBitcodeReader::readBlockToInfo(unsigned ID) { llvm::Expected>> ClangDocBitcodeReader::readBitcode() { std::vector> Infos; - if (!validateStream()) - return llvm::make_error("Invalid bitcode stream.\n", - llvm::inconvertibleErrorCode()); - ; + if (auto Err = validateStream()) + return std::move(Err); // Read the top level blocks. while (!Stream.AtEndOfStream()) { unsigned Code = Stream.ReadCode(); if (Code != llvm::bitc::ENTER_SUBBLOCK) return llvm::make_error( - "Missing subblock in bitcode.\n", llvm::inconvertibleErrorCode()); - ; - + "No blocks in input.\n", llvm::inconvertibleErrorCode()); unsigned ID = Stream.ReadSubBlockID(); switch (ID) { // NamedType and Comment blocks should not appear at the top level @@ -648,29 +699,25 @@ ClangDocBitcodeReader::readBitcode() { case BI_COMMENT_BLOCK_ID: case BI_REFERENCE_BLOCK_ID: return llvm::make_error( - "Invalid top level block in bitcode.\n", - llvm::inconvertibleErrorCode()); - ; + "Invalid top level block.\n", llvm::inconvertibleErrorCode()); case BI_NAMESPACE_BLOCK_ID: case BI_RECORD_BLOCK_ID: case BI_ENUM_BLOCK_ID: - case BI_FUNCTION_BLOCK_ID: - if (std::unique_ptr I = readBlockToInfo(ID)) - Infos.emplace_back(std::move(I)); + case BI_FUNCTION_BLOCK_ID: { + auto InfoOrErr = readBlockToInfo(ID); + if (!InfoOrErr) + return InfoOrErr.takeError(); + Infos.emplace_back(std::move(InfoOrErr.get())); continue; + } case BI_VERSION_BLOCK_ID: - if (readBlock(ID, VersionNumber)) - continue; - return llvm::make_error( - "Invalid bitcode version in bitcode.\n", - llvm::inconvertibleErrorCode()); - ; + if (auto Err = readBlock(ID, VersionNumber)) + return std::move(Err); + continue; case llvm::bitc::BLOCKINFO_BLOCK_ID: - if (readBlockInfoBlock()) - continue; - return llvm::make_error( - "Invalid BlockInfo in bitcode.\n", llvm::inconvertibleErrorCode()); - ; + if (auto Err = readBlockInfoBlock()) + return std::move(Err); + continue; default: if (!Stream.SkipBlock()) continue; diff --git a/clang-doc/BitcodeReader.h b/clang-doc/BitcodeReader.h index aaf25257c..ec3f6b0fd 100644 --- a/clang-doc/BitcodeReader.h +++ b/clang-doc/BitcodeReader.h @@ -22,6 +22,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Support/Error.h" namespace clang { namespace doc { @@ -38,30 +39,31 @@ class ClangDocBitcodeReader { enum class Cursor { BadBlock = 1, Record, BlockEnd, BlockBegin }; // Top level parsing - bool validateStream(); - bool readVersion(); - bool readBlockInfoBlock(); + llvm::Error validateStream(); + llvm::Error readVersion(); + llvm::Error readBlockInfoBlock(); // Read a block of records into a single Info struct, calls readRecord on each // record found. - template bool readBlock(unsigned ID, T I); + template llvm::Error readBlock(unsigned ID, T I); // Step through a block of records to find the next data field. - template bool readSubBlock(unsigned ID, T I); + template llvm::Error readSubBlock(unsigned ID, T I); // Read record data into the given Info data field, calling the appropriate // parseRecord functions to parse and store the data. - template bool readRecord(unsigned ID, T I); + template llvm::Error readRecord(unsigned ID, T I); // Allocate the relevant type of info and add read data to the object. - template std::unique_ptr createInfo(unsigned ID); + template + llvm::Expected> createInfo(unsigned ID); // Helper function to step through blocks to find and dispatch the next record // or block to be read. Cursor skipUntilRecordOrBlock(unsigned &BlockOrRecordID); // Helper function to set up the approriate type of Info. - std::unique_ptr readBlockToInfo(unsigned ID); + llvm::Expected> readBlockToInfo(unsigned ID); llvm::BitstreamCursor &Stream; Optional BlockInfo; From f9db20dade0924ca2bc689f846c5016f178b7003 Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Wed, 15 Aug 2018 16:18:46 +0000 Subject: [PATCH 051/686] [clang-doc] Explicitly cast to unique_ptr Older compilers don't like the implicit cast & move when returning a unique_ptr to an llvm::Expected type. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339785 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/BitcodeReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-doc/BitcodeReader.cpp b/clang-doc/BitcodeReader.cpp index dee0930bc..20cdc5085 100644 --- a/clang-doc/BitcodeReader.cpp +++ b/clang-doc/BitcodeReader.cpp @@ -657,7 +657,7 @@ ClangDocBitcodeReader::createInfo(unsigned ID) { std::unique_ptr I = llvm::make_unique(); if (auto Err = readBlock(ID, static_cast(I.get()))) return std::move(Err); - return I; + return std::unique_ptr{std::move(I)};; } llvm::Expected> From d9ee0dd0c23f489dcb79e926102bc7f236ea11e6 Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Thu, 16 Aug 2018 11:41:19 +0000 Subject: [PATCH 052/686] Attempt to fix clangd tests on older compilers Old gcc versions of gcc struggle with raw string literals inside macros. Inspired by rL339759 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339866 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/CodeCompleteTests.cpp | 28 +++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 6e1045ae6..a18175065 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1538,36 +1538,39 @@ TEST(SignatureHelpTest, OverloadsOrdering) { } TEST(SignatureHelpTest, InstantiatedSignatures) { - EXPECT_THAT(signatures(R"cpp( + StringRef Sig0 = R"cpp( template void foo(T, T, T); int main() { foo(^); } - )cpp") - .signatures, + )cpp"; + + EXPECT_THAT(signatures(Sig0).signatures, ElementsAre(Sig("foo(T, T, T) -> void", {"T", "T", "T"}))); - EXPECT_THAT(signatures(R"cpp( + StringRef Sig1 = R"cpp( template void foo(T, T, T); int main() { foo(10, ^); - })cpp") - .signatures, + })cpp"; + + EXPECT_THAT(signatures(Sig1).signatures, ElementsAre(Sig("foo(T, T, T) -> void", {"T", "T", "T"}))); - EXPECT_THAT(signatures(R"cpp( + StringRef Sig2 = R"cpp( template void foo(T...); int main() { foo(^); } - )cpp") - .signatures, + )cpp"; + + EXPECT_THAT(signatures(Sig2).signatures, ElementsAre(Sig("foo(T...) -> void", {"T..."}))); // It is debatable whether we should substitute the outer template parameter @@ -1575,7 +1578,7 @@ TEST(SignatureHelpTest, InstantiatedSignatures) { // do substitute in code complete. // FIXME: make code complete and signature help consistent, figure out which // way is better. - EXPECT_THAT(signatures(R"cpp( + StringRef Sig3 = R"cpp( template struct X { template @@ -1585,8 +1588,9 @@ TEST(SignatureHelpTest, InstantiatedSignatures) { int main() { X().foo(^) } - )cpp") - .signatures, + )cpp"; + + EXPECT_THAT(signatures(Sig3).signatures, ElementsAre(Sig("foo(T, U) -> void", {"T", "U"}))); } From 93b7e4404a0ff767e9e61c44ca959b0ed3f904dc Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 16 Aug 2018 13:19:43 +0000 Subject: [PATCH 053/686] [clangd] NFC: Improve Dex Iterators debugging traits This patch improves `dex::Iterator` string representation by incorporating the information about the element which is currently being pointed to by the `DocumentIterator`. Reviewed by: ioeric Differential Revision: https://reviews.llvm.org/D50689 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339877 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.cpp | 13 +++++++++++-- clangd/index/dex/Iterator.h | 4 +++- unittests/clangd/DexIndexTests.cpp | 5 +++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index 84d442e9d..a554c6627 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -49,10 +49,19 @@ class DocumentIterator : public Iterator { llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << '['; auto Separator = ""; - for (const auto &ID : Documents) { - OS << Separator << ID; + for (auto It = std::begin(Documents); It != std::end(Documents); ++It) { + OS << Separator; + if (It == Index) + OS << '{' << *It << '}'; + else + OS << *It; Separator = ", "; } + OS << Separator; + if (Index == std::end(Documents)) + OS << "{END}"; + else + OS << "END"; OS << ']'; return OS; } diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 1616e8687..06c7fbca1 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -99,7 +99,9 @@ class Iterator { /// /// Where Type is the iterator type representation: "&" for And, "|" for Or, /// ChildN is N-th iterator child. Raw iterators over PostingList are - /// represented as "[ID1, ID2, ...]" where IDN is N-th PostingList entry. + /// represented as "[ID1, ID2, ..., {IDN}, ... END]" where IDN is N-th + /// PostingList entry and the element which is pointed to by the PostingList + /// iterator is enclosed in {} braces. friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Iterator &Iterator) { return Iterator.dump(OS); diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index f23192fbe..8be6fc7e5 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -231,13 +231,14 @@ TEST(DexIndexIterators, StringRepresentation) { const PostingList L4 = {0, 1, 5}; const PostingList L5; - EXPECT_EQ(llvm::to_string(*(create(L0))), "[4, 7, 8, 20, 42, 100]"); + EXPECT_EQ(llvm::to_string(*(create(L0))), "[{4}, 7, 8, 20, 42, 100, END]"); auto Nested = createAnd(createAnd(create(L1), create(L2)), createOr(create(L3), create(L4), create(L5))); EXPECT_EQ(llvm::to_string(*Nested), - "(& (& [1, 3, 5, 8, 9] [1, 5, 7, 9]) (| [0, 5] [0, 1, 5] []))"); + "(& (& [{1}, 3, 5, 8, 9, END] [{1}, 5, 7, 9, END]) (| [0, {5}, " + "END] [0, {1}, 5, END] [{END}]))"); } TEST(DexIndexIterators, Limit) { From 1ca2daf65939fdd44d4bac3f2ff6c317b627c950 Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Thu, 16 Aug 2018 13:55:10 +0000 Subject: [PATCH 054/686] Fixed unused variable warning. NFCI. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339879 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/FileDistanceTests.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unittests/clangd/FileDistanceTests.cpp b/unittests/clangd/FileDistanceTests.cpp index 0d5afc9a9..acce245bf 100644 --- a/unittests/clangd/FileDistanceTests.cpp +++ b/unittests/clangd/FileDistanceTests.cpp @@ -58,7 +58,8 @@ TEST(FileDistanceTests, BadSource) { EXPECT_EQ(D.distance("b/b/b/c"), 17u); // a+up+down+down+down+down, not b+down } -auto UseUnittestScheme = UnittestSchemeAnchorSource; +// Force the unittest URI scheme to be linked, +static int LLVM_ATTRIBUTE_UNUSED UseUnittestScheme = UnittestSchemeAnchorSource; TEST(FileDistanceTests, URI) { FileDistanceOptions Opts; From a1b075d39e717e2a8656c935e23f1faa59295630 Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Thu, 16 Aug 2018 21:54:34 +0000 Subject: [PATCH 055/686] Implement a (simple) Markdown generator Implementing a simple Markdown generator from the emitted bitcode summary of declarations. Very primitive at this point, but will be expanded. Currently emits an .md file for each class and namespace, listing its contents. For a more detailed overview of the tool, see the design document on the mailing list: http://lists.llvm.org/pipermail/cfe-dev/2017-December/056203.html Differential Revision: https://reviews.llvm.org/D43424 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339948 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/CMakeLists.txt | 1 + clang-doc/Generators.cpp | 3 + clang-doc/Generators.h | 2 +- clang-doc/MDGenerator.cpp | 314 ++++++++++++++++++++++++++++++++ clang-doc/Representation.h | 15 +- clang-doc/YAMLGenerator.cpp | 10 +- clang-doc/gen_tests.py | 33 +++- clang-doc/tool/ClangDocMain.cpp | 43 +++-- test/clang-doc/md-comment.cpp | 48 +++++ test/clang-doc/md-linkage.cpp | 134 ++++++++++++++ test/clang-doc/md-module.cpp | 24 +++ test/clang-doc/md-namespace.cpp | 46 +++++ test/clang-doc/md-record.cpp | 97 ++++++++++ 13 files changed, 732 insertions(+), 38 deletions(-) create mode 100644 clang-doc/MDGenerator.cpp create mode 100644 test/clang-doc/md-comment.cpp create mode 100644 test/clang-doc/md-linkage.cpp create mode 100644 test/clang-doc/md-module.cpp create mode 100644 test/clang-doc/md-namespace.cpp create mode 100644 test/clang-doc/md-record.cpp diff --git a/clang-doc/CMakeLists.txt b/clang-doc/CMakeLists.txt index eaebf616f..1d70bb08f 100644 --- a/clang-doc/CMakeLists.txt +++ b/clang-doc/CMakeLists.txt @@ -10,6 +10,7 @@ add_clang_library(clangDoc ClangDoc.cpp Generators.cpp Mapper.cpp + MDGenerator.cpp Representation.cpp Serialize.cpp YAMLGenerator.cpp diff --git a/clang-doc/Generators.cpp b/clang-doc/Generators.cpp index fe01d6109..5a0d0c5c1 100644 --- a/clang-doc/Generators.cpp +++ b/clang-doc/Generators.cpp @@ -29,8 +29,11 @@ findGeneratorByName(llvm::StringRef Format) { // This anchor is used to force the linker to link in the generated object file // and thus register the generators. extern volatile int YAMLGeneratorAnchorSource; +extern volatile int MDGeneratorAnchorSource; static int LLVM_ATTRIBUTE_UNUSED YAMLGeneratorAnchorDest = YAMLGeneratorAnchorSource; +static int LLVM_ATTRIBUTE_UNUSED MDGeneratorAnchorDest = + MDGeneratorAnchorSource; } // namespace doc } // namespace clang diff --git a/clang-doc/Generators.h b/clang-doc/Generators.h index 9106d2cff..90a81e82d 100644 --- a/clang-doc/Generators.h +++ b/clang-doc/Generators.h @@ -27,7 +27,7 @@ class Generator { virtual ~Generator() = default; // Write out the decl info in the specified format. - virtual bool generateDocForInfo(Info *I, llvm::raw_ostream &OS) = 0; + virtual llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) = 0; }; typedef llvm::Registry GeneratorRegistry; diff --git a/clang-doc/MDGenerator.cpp b/clang-doc/MDGenerator.cpp new file mode 100644 index 000000000..f989f3fa6 --- /dev/null +++ b/clang-doc/MDGenerator.cpp @@ -0,0 +1,314 @@ +//===-- MDGenerator.cpp - Markdown Generator --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Generators.h" +#include "Representation.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include + +using namespace llvm; + +namespace clang { +namespace doc { + +// Enum conversion + +std::string getAccess(AccessSpecifier AS) { + switch (AS) { + case AccessSpecifier::AS_public: + return "public"; + case AccessSpecifier::AS_protected: + return "protected"; + case AccessSpecifier::AS_private: + return "private"; + case AccessSpecifier::AS_none: + return {}; + } +} + +std::string getTagType(TagTypeKind AS) { + switch (AS) { + case TagTypeKind::TTK_Class: + return "class"; + case TagTypeKind::TTK_Union: + return "union"; + case TagTypeKind::TTK_Interface: + return "interface"; + case TagTypeKind::TTK_Struct: + return "struct"; + case TagTypeKind::TTK_Enum: + return "enum"; + } +} + +// Markdown generation + +std::string genItalic(const Twine &Text) { return "*" + Text.str() + "*"; } + +std::string genEmphasis(const Twine &Text) { return "**" + Text.str() + "**"; } + +std::string genLink(const Twine &Text, const Twine &Link) { + return "[" + Text.str() + "](" + Link.str() + ")"; +} + +std::string genReferenceList(const llvm::SmallVectorImpl &Refs) { + std::string Buffer; + llvm::raw_string_ostream Stream(Buffer); + bool First = true; + for (const auto &R : Refs) { + if (!First) + Stream << ", "; + Stream << R.Name; + First = false; + } + return Stream.str(); +} + +void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n"; } + +void writeNewLine(raw_ostream &OS) { OS << "\n"; } + +void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) { + OS << std::string(Num, '#') + " " + Text << "\n"; +} + +void writeFileDefinition(const Location &L, raw_ostream &OS) { + OS << genItalic("Defined at line " + std::to_string(L.LineNumber) + " of " + + L.Filename) + << "\n"; +} + +void writeDescription(const CommentInfo &I, raw_ostream &OS) { + if (I.Kind == "FullComment") { + for (const auto &Child : I.Children) + writeDescription(*Child, OS); + } else if (I.Kind == "ParagraphComment") { + for (const auto &Child : I.Children) + writeDescription(*Child, OS); + writeNewLine(OS); + } else if (I.Kind == "BlockCommandComment") { + OS << genEmphasis(I.Name); + for (const auto &Child : I.Children) + writeDescription(*Child, OS); + } else if (I.Kind == "InlineCommandComment") { + OS << genEmphasis(I.Name) << " " << I.Text; + } else if (I.Kind == "ParamCommandComment") { + std::string Direction = I.Explicit ? (" " + I.Direction).str() : ""; + OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n"; + } else if (I.Kind == "TParamCommandComment") { + std::string Direction = I.Explicit ? (" " + I.Direction).str() : ""; + OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n"; + } else if (I.Kind == "VerbatimBlockComment") { + for (const auto &Child : I.Children) + writeDescription(*Child, OS); + } else if (I.Kind == "VerbatimBlockLineComment") { + OS << I.Text; + writeNewLine(OS); + } else if (I.Kind == "VerbatimLineComment") { + OS << I.Text; + writeNewLine(OS); + } else if (I.Kind == "HTMLStartTagComment") { + if (I.AttrKeys.size() != I.AttrValues.size()) + return; + std::string Buffer; + llvm::raw_string_ostream Attrs(Buffer); + for (unsigned Idx = 0; Idx < I.AttrKeys.size(); ++Idx) + Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\""; + + std::string CloseTag = I.SelfClosing ? "/>" : ">"; + writeLine("<" + I.Name + Attrs.str() + CloseTag, OS); + } else if (I.Kind == "HTMLEndTagComment") { + writeLine("", OS); + } else if (I.Kind == "TextComment") { + OS << I.Text; + } else { + OS << "Unknown comment kind: " << I.Kind << ".\n"; + } +} + +void genMarkdown(const EnumInfo &I, llvm::raw_ostream &OS) { + if (I.Scoped) + writeLine("| enum class " + I.Name + " |", OS); + else + writeLine("| enum " + I.Name + " |", OS); + writeLine("--", OS); + + std::string Buffer; + llvm::raw_string_ostream Members(Buffer); + if (!I.Members.empty()) + for (const auto &N : I.Members) + Members << "| " << N << " |\n"; + writeLine(Members.str(), OS); + if (I.DefLoc) + writeFileDefinition(I.DefLoc.getValue(), OS); + + for (const auto &C : I.Description) + writeDescription(C, OS); +} + +void genMarkdown(const FunctionInfo &I, llvm::raw_ostream &OS) { + std::string Buffer; + llvm::raw_string_ostream Stream(Buffer); + bool First = true; + for (const auto &N : I.Params) { + if (!First) + Stream << ", "; + Stream << N.Type.Name + " " + N.Name; + First = false; + } + Twine Signature = + I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")"; + std::string Access = getAccess(I.Access); + if (Access != "") + writeHeader(genItalic(Access) + " " + Signature, 3, OS); + else + writeHeader(Signature, 3, OS); + if (I.DefLoc) + writeFileDefinition(I.DefLoc.getValue(), OS); + + for (const auto &C : I.Description) + writeDescription(C, OS); +} + +void genMarkdown(const NamespaceInfo &I, llvm::raw_ostream &OS) { + if (I.Name == "") + writeHeader("Global Namespace", 1, OS); + else + writeHeader("namespace " + I.Name, 1, OS); + writeNewLine(OS); + + if (!I.Description.empty()) { + for (const auto &C : I.Description) + writeDescription(C, OS); + writeNewLine(OS); + } + + if (!I.ChildNamespaces.empty()) { + writeHeader("Namespaces", 2, OS); + for (const auto &R : I.ChildNamespaces) + writeLine(R.Name, OS); + writeNewLine(OS); + } + if (!I.ChildRecords.empty()) { + writeHeader("Records", 2, OS); + for (const auto &R : I.ChildRecords) + writeLine(R.Name, OS); + writeNewLine(OS); + } + if (!I.ChildFunctions.empty()) { + writeHeader("Functions", 2, OS); + for (const auto &F : I.ChildFunctions) + genMarkdown(F, OS); + writeNewLine(OS); + } + if (!I.ChildEnums.empty()) { + writeHeader("Enums", 2, OS); + for (const auto &E : I.ChildEnums) + genMarkdown(E, OS); + writeNewLine(OS); + } +} + +void genMarkdown(const RecordInfo &I, llvm::raw_ostream &OS) { + writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS); + if (I.DefLoc) + writeFileDefinition(I.DefLoc.getValue(), OS); + + if (!I.Description.empty()) { + for (const auto &C : I.Description) + writeDescription(C, OS); + writeNewLine(OS); + } + + std::string Parents = genReferenceList(I.Parents); + std::string VParents = genReferenceList(I.VirtualParents); + if (!Parents.empty() || !VParents.empty()) { + if (Parents.empty()) + writeLine("Inherits from " + VParents, OS); + else if (VParents.empty()) + writeLine("Inherits from " + Parents, OS); + else + writeLine("Inherits from " + Parents + ", " + VParents, OS); + writeNewLine(OS); + } + + if (!I.Members.empty()) { + writeHeader("Members", 2, OS); + for (const auto Member : I.Members) { + std::string Access = getAccess(Member.Access); + if (Access != "") + writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS); + else + writeLine(Member.Type.Name + " " + Member.Name, OS); + } + writeNewLine(OS); + } + + if (!I.ChildRecords.empty()) { + writeHeader("Records", 2, OS); + for (const auto &R : I.ChildRecords) + writeLine(R.Name, OS); + writeNewLine(OS); + } + if (!I.ChildFunctions.empty()) { + writeHeader("Functions", 2, OS); + for (const auto &F : I.ChildFunctions) + genMarkdown(F, OS); + writeNewLine(OS); + } + if (!I.ChildEnums.empty()) { + writeHeader("Enums", 2, OS); + for (const auto &E : I.ChildEnums) + genMarkdown(E, OS); + writeNewLine(OS); + } +} + +/// Generator for Markdown documentation. +class MDGenerator : public Generator { +public: + static const char *Format; + + llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; +}; + +const char *MDGenerator::Format = "md"; + +llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { + switch (I->IT) { + case InfoType::IT_namespace: + genMarkdown(*static_cast(I), OS); + break; + case InfoType::IT_record: + genMarkdown(*static_cast(I), OS); + break; + case InfoType::IT_enum: + genMarkdown(*static_cast(I), OS); + break; + case InfoType::IT_function: + genMarkdown(*static_cast(I), OS); + break; + case InfoType::IT_default: + return llvm::make_error("Unexpected info type.\n", + llvm::inconvertibleErrorCode()); + } + return llvm::Error::success(); +} + +static GeneratorRegistry::Add MD(MDGenerator::Format, + "Generator for MD output."); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the generator. +volatile int MDGeneratorAnchorSource = 0; + +} // namespace doc +} // namespace clang diff --git a/clang-doc/Representation.h b/clang-doc/Representation.h index 9e88bd9c8..57f510126 100644 --- a/clang-doc/Representation.h +++ b/clang-doc/Representation.h @@ -48,13 +48,14 @@ struct CommentInfo { CommentInfo(CommentInfo &Other) = delete; CommentInfo(CommentInfo &&Other) = default; - SmallString<16> Kind; // Kind of comment (TextComment, InlineCommandComment, - // HTMLStartTagComment, HTMLEndTagComment, - // BlockCommandComment, ParamCommandComment, - // TParamCommandComment, VerbatimBlockComment, - // VerbatimBlockLineComment, VerbatimLineComment). - SmallString<64> Text; // Text of the comment. - SmallString<16> Name; // Name of the comment (for Verbatim and HTML). + SmallString<16> + Kind; // Kind of comment (FullComment, ParagraphComment, TextComment, + // InlineCommandComment, HTMLStartTagComment, HTMLEndTagComment, + // BlockCommandComment, ParamCommandComment, + // TParamCommandComment, VerbatimBlockComment, + // VerbatimBlockLineComment, VerbatimLineComment). + SmallString<64> Text; // Text of the comment. + SmallString<16> Name; // Name of the comment (for Verbatim and HTML). SmallString<8> Direction; // Parameter direction (for (T)ParamCommand). SmallString<16> ParamName; // Parameter name (for (T)ParamCommand). SmallString<16> CloseName; // Closing tag name (for VerbatimBlock). diff --git a/clang-doc/YAMLGenerator.cpp b/clang-doc/YAMLGenerator.cpp index 58c1e1f36..e09390198 100644 --- a/clang-doc/YAMLGenerator.cpp +++ b/clang-doc/YAMLGenerator.cpp @@ -242,12 +242,12 @@ class YAMLGenerator : public Generator { public: static const char *Format; - bool generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; + llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; }; const char *YAMLGenerator::Format = "yaml"; -bool YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { +llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { llvm::yaml::Output InfoYAML(OS); switch (I->IT) { case InfoType::IT_namespace: @@ -263,10 +263,10 @@ bool YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { InfoYAML << *static_cast(I); break; case InfoType::IT_default: - llvm::errs() << "Unexpected info type in index.\n"; - return true; + return llvm::make_error("Unexpected info type.\n", + llvm::inconvertibleErrorCode()); } - return false; + return llvm::Error::success(); } static GeneratorRegistry::Add YAML(YAMLGenerator::Format, diff --git a/clang-doc/gen_tests.py b/clang-doc/gen_tests.py index ccdb069c4..9259684d5 100644 --- a/clang-doc/gen_tests.py +++ b/clang-doc/gen_tests.py @@ -18,16 +18,19 @@ To generate all current tests: - Generate mapper tests: - python gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix mapper + python gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix mapper -use-check-next - Generate reducer tests: - python gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix bc + python gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix bc -use-check-next - Generate yaml tests: - python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix yaml + python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix yaml -use-check-next - Generate public decl tests: - python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix public + python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix public -use-check-next + +- Generate Markdown tests: + python gen_tests.py -flag='--format=md' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix md This script was written on/for Linux, and has not been tested on any other platform and so it may not work. @@ -95,7 +98,8 @@ def get_test_case_code(test_case_path, flags): return code -def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer): +def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer, + check_next=True): output = '' run_cmd = '' if '--dump-mapper' in flags or '--dump-intermediate' in flags: @@ -119,8 +123,14 @@ def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer): output = re.sub(YAML_USR_REGEX, YAML_USR, output) output = re.sub(BITCODE_USR_REGEX, BITCODE_USR, output) output = CHECK.format(checkname) + output.rstrip() - output = run_cmd + output.replace('\n', - '\n' + CHECK_NEXT.format(checkname)) + + if check_next: + check_comment = CHECK_NEXT.format(checkname) + else: + check_comment = CHECK.format(checkname) + + output = output.replace('\n', '\n' + check_comment) + output = run_cmd + output.replace('%s\n' % check_comment, "") return output + '\n' @@ -151,6 +161,12 @@ def main(): metavar="PATH", default='llvm-bcanalyzer', help='path to llvm-bcanalyzer binary') + parser.add_argument( + '-use-check-next', + dest='check_next', + default=False, + action='store_true', + help='Whether or not to use CHECK-NEXT in the resulting tests.') args = parser.parse_args() flags = ' '.join(args.flags) @@ -188,7 +204,8 @@ def main(): if len(usr) < 2: continue all_output += get_output(root, out_file, out_dir, args.flags, - num_outputs, args.bcanalyzer) + num_outputs, args.bcanalyzer, + args.check_next) num_outputs += 1 # Add test case code to test diff --git a/clang-doc/tool/ClangDocMain.cpp b/clang-doc/tool/ClangDocMain.cpp index 6e4a92d7b..6b50f6ca8 100644 --- a/clang-doc/tool/ClangDocMain.cpp +++ b/clang-doc/tool/ClangDocMain.cpp @@ -34,6 +34,7 @@ #include "clang/Tooling/StandaloneExecution.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/APFloat.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -69,13 +70,18 @@ static llvm::cl::opt llvm::cl::init(false), llvm::cl::cat(ClangDocCategory)); enum OutputFormatTy { + md, yaml, }; -static llvm::cl::opt FormatEnum( - "format", llvm::cl::desc("Format for outputted docs."), - llvm::cl::values(clEnumVal(yaml, "Documentation in YAML format.")), - llvm::cl::init(yaml), llvm::cl::cat(ClangDocCategory)); +static llvm::cl::opt + FormatEnum("format", llvm::cl::desc("Format for outputted docs."), + llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml", + "Documentation in YAML format."), + clEnumValN(OutputFormatTy::md, "md", + "Documentation in MD format.")), + llvm::cl::init(OutputFormatTy::yaml), + llvm::cl::cat(ClangDocCategory)); static llvm::cl::opt DoxygenOnly( "doxygen", @@ -155,10 +161,12 @@ getInfoOutputFile(StringRef Root, return Path; } -std::string getFormatString(OutputFormatTy Ty) { - switch (Ty) { - case yaml: +std::string getFormatString() { + switch (FormatEnum) { + case OutputFormatTy::yaml: return "yaml"; + case OutputFormatTy::md: + return "md"; } llvm_unreachable("Unknown OutputFormatTy"); } @@ -191,14 +199,6 @@ int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); std::error_code OK; - // Fail early if an invalid format was provided. - std::string Format = getFormatString(FormatEnum); - auto G = doc::findGeneratorByName(Format); - if (!G) { - llvm::errs() << toString(G.takeError()) << "\n"; - return 1; - } - auto Exec = clang::tooling::createExecutorFromCommandLineArgs( argc, argv, ClangDocCategory); @@ -207,6 +207,15 @@ int main(int argc, const char **argv) { return 1; } + // Fail early if an invalid format was provided. + std::string Format = getFormatString(); + llvm::outs() << "Emiting docs in " << Format << " format.\n"; + auto G = doc::findGeneratorByName(Format); + if (!G) { + llvm::errs() << toString(G.takeError()) << "\n"; + return 1; + } + ArgumentsAdjuster ArgAdjuster; if (!DoxygenOnly) ArgAdjuster = combineAdjusters( @@ -277,8 +286,8 @@ int main(int argc, const char **argv) { continue; } - if (G->get()->generateDocForInfo(I, InfoOS)) - llvm::errs() << "Unable to generate docs for info.\n"; + if (auto Err = G->get()->generateDocForInfo(I, InfoOS)) + llvm::errs() << toString(std::move(Err)) << "\n"; } return 0; diff --git a/test/clang-doc/md-comment.cpp b/test/clang-doc/md-comment.cpp new file mode 100644 index 000000000..ae14eaac2 --- /dev/null +++ b/test/clang-doc/md-comment.cpp @@ -0,0 +1,48 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +/// \brief Brief description. +/// +/// Extended description that +/// continues onto the next line. +/// +///
    +///
  • Testing. +///
+/// +/// \verbatim +/// The description continues. +/// \endverbatim +/// -- +/// \param [out] I is a parameter. +/// \param J is a parameter. +/// \return void +void F(int I, int J); + +/// Bonus comment on definition +void F(int I, int J) {} + +// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: # Global Namespace +// CHECK-0: ## Functions +// CHECK-0: ### void F(int I, int J) +// CHECK-0: *Defined at line 28 of test* +// CHECK-0: **brief** Brief description. +// CHECK-0: Extended description that continues onto the next line. +// CHECK-0:
    +// CHECK-0:
  • +// CHECK-0: Testing.
+// CHECK-0: The description continues. +// CHECK-0: -- +// CHECK-0: **I** [out] +// CHECK-0: **J** +// CHECK-0: **return** void +// CHECK-0: Bonus comment on definition diff --git a/test/clang-doc/md-linkage.cpp b/test/clang-doc/md-linkage.cpp new file mode 100644 index 000000000..942ff3533 --- /dev/null +++ b/test/clang-doc/md-linkage.cpp @@ -0,0 +1,134 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void function(int x); + +inline int inlinedFunction(int x); + +int functionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +inline int inlinedFunctionWithInnerClass(int x) { + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +class Class { +public: + void publicMethod(); + int publicField; + +protected: + void protectedMethod(); + int protectedField; + +private: + void privateMethod(); + int privateField; +}; + +namespace named { +class NamedClass { +public: + void namedPublicMethod(); + int namedPublicField; + +protected: + void namedProtectedMethod(); + int namedProtectedField; + +private: + void namedPrivateMethod(); + int namedPrivateField; +}; + +void namedFunction(); +static void namedStaticFunction(); +inline void namedInlineFunction(); +} // namespace named + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace { +class AnonClass { +public: + void anonPublicMethod(); + int anonPublicField; + +protected: + void anonProtectedMethod(); + int anonProtectedField; + +private: + void anonPrivateMethod(); + int anonPrivateField; +}; + +void anonFunction(); +static void anonStaticFunction(); +inline void anonInlineFunction(); +} // namespace + +// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./Class.md | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: # class Class +// CHECK-0: *Defined at line 32 of test* +// CHECK-0: ## Members +// CHECK-0: int publicField +// CHECK-0: protected int protectedField +// CHECK-0: ## Functions +// CHECK-0: ### void publicMethod() +// CHECK-0: ### void protectedMethod() + +// RUN: cat %t/docs/./named.md | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: # namespace named +// CHECK-1: ## Functions +// CHECK-1: ### void namedFunction() +// CHECK-1: ### void namedInlineFunction() + +// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: # Global Namespace +// CHECK-2: ## Functions +// CHECK-2: ### void function(int x) +// CHECK-2: ### int inlinedFunction(int x) +// CHECK-2: ### int functionWithInnerClass(int x) +// CHECK-2: *Defined at line 14 of test* +// CHECK-2: ### int inlinedFunctionWithInnerClass(int x) +// CHECK-2: *Defined at line 23 of test* + +// RUN: cat %t/docs/named/NamedClass.md | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: # class NamedClass +// CHECK-3: *Defined at line 47 of test* +// CHECK-3: ## Members +// CHECK-3: int namedPublicField +// CHECK-3: protected int namedProtectedField +// CHECK-3: ## Functions +// CHECK-3: ### void namedPublicMethod() +// CHECK-3: ### void namedProtectedMethod() diff --git a/test/clang-doc/md-module.cpp b/test/clang-doc/md-module.cpp new file mode 100644 index 000000000..936f621bc --- /dev/null +++ b/test/clang-doc/md-module.cpp @@ -0,0 +1,24 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +export module M; + +int moduleFunction(int x); // ModuleLinkage + +static int staticModuleFunction(int x); // ModuleInternalLinkage + +export double exportedModuleFunction(double y, int z); // ExternalLinkage + +// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: # Global Namespace +// CHECK-0: ## Functions +// CHECK-0: ### int moduleFunction(int x) +// CHECK-0: ### double exportedModuleFunction(double y, int z) diff --git a/test/clang-doc/md-namespace.cpp b/test/clang-doc/md-namespace.cpp new file mode 100644 index 000000000..6db3fffd2 --- /dev/null +++ b/test/clang-doc/md-namespace.cpp @@ -0,0 +1,46 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +namespace A { + +void f(); + +} // namespace A + +namespace A { + +void f(){}; + +namespace B { + +enum E { X }; + +E func(int i) { return X; } + +} // namespace B +} // namespace A + +// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./A.md | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: # namespace A +// CHECK-0: ## Functions +// CHECK-0: ### void f() +// CHECK-0: *Defined at line 17 of test* + +// RUN: cat %t/docs/A/B.md | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: # namespace B +// CHECK-1: ## Functions +// CHECK-1: ### enum A::B::E func(int i) +// CHECK-1: *Defined at line 23 of test* +// CHECK-1: ## Enums +// CHECK-1: | enum E | +// CHECK-1: -- +// CHECK-1: | X | +// CHECK-1: *Defined at line 21 of test* diff --git a/test/clang-doc/md-record.cpp b/test/clang-doc/md-record.cpp new file mode 100644 index 000000000..1c5a1adf3 --- /dev/null +++ b/test/clang-doc/md-record.cpp @@ -0,0 +1,97 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// This test requires Linux due to system-dependent USR for the inner class. +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void H() { + class I {}; +} + +union A { int X; int Y; }; + +enum B { X, Y }; + +enum class Bc { A, B }; + +struct C { int i; }; + +class D {}; + +class E { +public: + E() {} + ~E() {} + +protected: + void ProtectedMethod(); +}; + +void E::ProtectedMethod() {} + +class F : virtual private D, public E {}; + +class X { + class Y {}; +}; + +// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./F.md | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: # class F +// CHECK-0: *Defined at line 36 of test* +// CHECK-0: Inherits from E, D + +// RUN: cat %t/docs/./D.md | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: # class D +// CHECK-1: *Defined at line 23 of test* + +// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: # Global Namespace +// CHECK-2: ## Functions +// CHECK-2: ### void H() +// CHECK-2: *Defined at line 11 of test* +// CHECK-2: ## Enums +// CHECK-2: | enum B | +// CHECK-2: -- +// CHECK-2: | X | +// CHECK-2: | Y | +// CHECK-2: *Defined at line 17 of test* +// CHECK-2: | enum class Bc | +// CHECK-2: -- +// CHECK-2: | A | +// CHECK-2: | B | +// CHECK-2: *Defined at line 19 of test* + +// RUN: cat %t/docs/./E.md | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: # class E +// CHECK-3: *Defined at line 25 of test* +// CHECK-3: ## Functions +// CHECK-3: ### void E() +// CHECK-3: *Defined at line 27 of test* +// CHECK-3: ### void ~E() +// CHECK-3: *Defined at line 28 of test* +// CHECK-3: ### void ProtectedMethod() +// CHECK-3: *Defined at line 34 of test* + +// RUN: cat %t/docs/./C.md | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: # struct C +// CHECK-4: *Defined at line 21 of test* +// CHECK-4: ## Members +// CHECK-4: int i + +// RUN: cat %t/docs/./X.md | FileCheck %s --check-prefix CHECK-5 +// CHECK-5: # class X +// CHECK-5: *Defined at line 38 of test* + +// RUN: cat %t/docs/./A.md | FileCheck %s --check-prefix CHECK-6 +// CHECK-6: # union A +// CHECK-6: *Defined at line 15 of test* +// CHECK-6: ## Members +// CHECK-6: int X +// CHECK-6: int Y From 6eae8e45034825a616f6789972227f598b31d55f Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Thu, 16 Aug 2018 23:50:51 +0000 Subject: [PATCH 056/686] Revert "Implement a (simple) Markdown generator" This reverts commit r339948, as it's breaking a few bots in ways that I can't reproduce right now. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@339966 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/CMakeLists.txt | 1 - clang-doc/Generators.cpp | 3 - clang-doc/Generators.h | 2 +- clang-doc/MDGenerator.cpp | 314 -------------------------------- clang-doc/Representation.h | 15 +- clang-doc/YAMLGenerator.cpp | 10 +- clang-doc/gen_tests.py | 33 +--- clang-doc/tool/ClangDocMain.cpp | 43 ++--- test/clang-doc/md-comment.cpp | 48 ----- test/clang-doc/md-linkage.cpp | 134 -------------- test/clang-doc/md-module.cpp | 24 --- test/clang-doc/md-namespace.cpp | 46 ----- test/clang-doc/md-record.cpp | 97 ---------- 13 files changed, 38 insertions(+), 732 deletions(-) delete mode 100644 clang-doc/MDGenerator.cpp delete mode 100644 test/clang-doc/md-comment.cpp delete mode 100644 test/clang-doc/md-linkage.cpp delete mode 100644 test/clang-doc/md-module.cpp delete mode 100644 test/clang-doc/md-namespace.cpp delete mode 100644 test/clang-doc/md-record.cpp diff --git a/clang-doc/CMakeLists.txt b/clang-doc/CMakeLists.txt index 1d70bb08f..eaebf616f 100644 --- a/clang-doc/CMakeLists.txt +++ b/clang-doc/CMakeLists.txt @@ -10,7 +10,6 @@ add_clang_library(clangDoc ClangDoc.cpp Generators.cpp Mapper.cpp - MDGenerator.cpp Representation.cpp Serialize.cpp YAMLGenerator.cpp diff --git a/clang-doc/Generators.cpp b/clang-doc/Generators.cpp index 5a0d0c5c1..fe01d6109 100644 --- a/clang-doc/Generators.cpp +++ b/clang-doc/Generators.cpp @@ -29,11 +29,8 @@ findGeneratorByName(llvm::StringRef Format) { // This anchor is used to force the linker to link in the generated object file // and thus register the generators. extern volatile int YAMLGeneratorAnchorSource; -extern volatile int MDGeneratorAnchorSource; static int LLVM_ATTRIBUTE_UNUSED YAMLGeneratorAnchorDest = YAMLGeneratorAnchorSource; -static int LLVM_ATTRIBUTE_UNUSED MDGeneratorAnchorDest = - MDGeneratorAnchorSource; } // namespace doc } // namespace clang diff --git a/clang-doc/Generators.h b/clang-doc/Generators.h index 90a81e82d..9106d2cff 100644 --- a/clang-doc/Generators.h +++ b/clang-doc/Generators.h @@ -27,7 +27,7 @@ class Generator { virtual ~Generator() = default; // Write out the decl info in the specified format. - virtual llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) = 0; + virtual bool generateDocForInfo(Info *I, llvm::raw_ostream &OS) = 0; }; typedef llvm::Registry GeneratorRegistry; diff --git a/clang-doc/MDGenerator.cpp b/clang-doc/MDGenerator.cpp deleted file mode 100644 index f989f3fa6..000000000 --- a/clang-doc/MDGenerator.cpp +++ /dev/null @@ -1,314 +0,0 @@ -//===-- MDGenerator.cpp - Markdown Generator --------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "Generators.h" -#include "Representation.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" -#include - -using namespace llvm; - -namespace clang { -namespace doc { - -// Enum conversion - -std::string getAccess(AccessSpecifier AS) { - switch (AS) { - case AccessSpecifier::AS_public: - return "public"; - case AccessSpecifier::AS_protected: - return "protected"; - case AccessSpecifier::AS_private: - return "private"; - case AccessSpecifier::AS_none: - return {}; - } -} - -std::string getTagType(TagTypeKind AS) { - switch (AS) { - case TagTypeKind::TTK_Class: - return "class"; - case TagTypeKind::TTK_Union: - return "union"; - case TagTypeKind::TTK_Interface: - return "interface"; - case TagTypeKind::TTK_Struct: - return "struct"; - case TagTypeKind::TTK_Enum: - return "enum"; - } -} - -// Markdown generation - -std::string genItalic(const Twine &Text) { return "*" + Text.str() + "*"; } - -std::string genEmphasis(const Twine &Text) { return "**" + Text.str() + "**"; } - -std::string genLink(const Twine &Text, const Twine &Link) { - return "[" + Text.str() + "](" + Link.str() + ")"; -} - -std::string genReferenceList(const llvm::SmallVectorImpl &Refs) { - std::string Buffer; - llvm::raw_string_ostream Stream(Buffer); - bool First = true; - for (const auto &R : Refs) { - if (!First) - Stream << ", "; - Stream << R.Name; - First = false; - } - return Stream.str(); -} - -void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n"; } - -void writeNewLine(raw_ostream &OS) { OS << "\n"; } - -void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) { - OS << std::string(Num, '#') + " " + Text << "\n"; -} - -void writeFileDefinition(const Location &L, raw_ostream &OS) { - OS << genItalic("Defined at line " + std::to_string(L.LineNumber) + " of " + - L.Filename) - << "\n"; -} - -void writeDescription(const CommentInfo &I, raw_ostream &OS) { - if (I.Kind == "FullComment") { - for (const auto &Child : I.Children) - writeDescription(*Child, OS); - } else if (I.Kind == "ParagraphComment") { - for (const auto &Child : I.Children) - writeDescription(*Child, OS); - writeNewLine(OS); - } else if (I.Kind == "BlockCommandComment") { - OS << genEmphasis(I.Name); - for (const auto &Child : I.Children) - writeDescription(*Child, OS); - } else if (I.Kind == "InlineCommandComment") { - OS << genEmphasis(I.Name) << " " << I.Text; - } else if (I.Kind == "ParamCommandComment") { - std::string Direction = I.Explicit ? (" " + I.Direction).str() : ""; - OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n"; - } else if (I.Kind == "TParamCommandComment") { - std::string Direction = I.Explicit ? (" " + I.Direction).str() : ""; - OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n"; - } else if (I.Kind == "VerbatimBlockComment") { - for (const auto &Child : I.Children) - writeDescription(*Child, OS); - } else if (I.Kind == "VerbatimBlockLineComment") { - OS << I.Text; - writeNewLine(OS); - } else if (I.Kind == "VerbatimLineComment") { - OS << I.Text; - writeNewLine(OS); - } else if (I.Kind == "HTMLStartTagComment") { - if (I.AttrKeys.size() != I.AttrValues.size()) - return; - std::string Buffer; - llvm::raw_string_ostream Attrs(Buffer); - for (unsigned Idx = 0; Idx < I.AttrKeys.size(); ++Idx) - Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\""; - - std::string CloseTag = I.SelfClosing ? "/>" : ">"; - writeLine("<" + I.Name + Attrs.str() + CloseTag, OS); - } else if (I.Kind == "HTMLEndTagComment") { - writeLine("", OS); - } else if (I.Kind == "TextComment") { - OS << I.Text; - } else { - OS << "Unknown comment kind: " << I.Kind << ".\n"; - } -} - -void genMarkdown(const EnumInfo &I, llvm::raw_ostream &OS) { - if (I.Scoped) - writeLine("| enum class " + I.Name + " |", OS); - else - writeLine("| enum " + I.Name + " |", OS); - writeLine("--", OS); - - std::string Buffer; - llvm::raw_string_ostream Members(Buffer); - if (!I.Members.empty()) - for (const auto &N : I.Members) - Members << "| " << N << " |\n"; - writeLine(Members.str(), OS); - if (I.DefLoc) - writeFileDefinition(I.DefLoc.getValue(), OS); - - for (const auto &C : I.Description) - writeDescription(C, OS); -} - -void genMarkdown(const FunctionInfo &I, llvm::raw_ostream &OS) { - std::string Buffer; - llvm::raw_string_ostream Stream(Buffer); - bool First = true; - for (const auto &N : I.Params) { - if (!First) - Stream << ", "; - Stream << N.Type.Name + " " + N.Name; - First = false; - } - Twine Signature = - I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")"; - std::string Access = getAccess(I.Access); - if (Access != "") - writeHeader(genItalic(Access) + " " + Signature, 3, OS); - else - writeHeader(Signature, 3, OS); - if (I.DefLoc) - writeFileDefinition(I.DefLoc.getValue(), OS); - - for (const auto &C : I.Description) - writeDescription(C, OS); -} - -void genMarkdown(const NamespaceInfo &I, llvm::raw_ostream &OS) { - if (I.Name == "") - writeHeader("Global Namespace", 1, OS); - else - writeHeader("namespace " + I.Name, 1, OS); - writeNewLine(OS); - - if (!I.Description.empty()) { - for (const auto &C : I.Description) - writeDescription(C, OS); - writeNewLine(OS); - } - - if (!I.ChildNamespaces.empty()) { - writeHeader("Namespaces", 2, OS); - for (const auto &R : I.ChildNamespaces) - writeLine(R.Name, OS); - writeNewLine(OS); - } - if (!I.ChildRecords.empty()) { - writeHeader("Records", 2, OS); - for (const auto &R : I.ChildRecords) - writeLine(R.Name, OS); - writeNewLine(OS); - } - if (!I.ChildFunctions.empty()) { - writeHeader("Functions", 2, OS); - for (const auto &F : I.ChildFunctions) - genMarkdown(F, OS); - writeNewLine(OS); - } - if (!I.ChildEnums.empty()) { - writeHeader("Enums", 2, OS); - for (const auto &E : I.ChildEnums) - genMarkdown(E, OS); - writeNewLine(OS); - } -} - -void genMarkdown(const RecordInfo &I, llvm::raw_ostream &OS) { - writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS); - if (I.DefLoc) - writeFileDefinition(I.DefLoc.getValue(), OS); - - if (!I.Description.empty()) { - for (const auto &C : I.Description) - writeDescription(C, OS); - writeNewLine(OS); - } - - std::string Parents = genReferenceList(I.Parents); - std::string VParents = genReferenceList(I.VirtualParents); - if (!Parents.empty() || !VParents.empty()) { - if (Parents.empty()) - writeLine("Inherits from " + VParents, OS); - else if (VParents.empty()) - writeLine("Inherits from " + Parents, OS); - else - writeLine("Inherits from " + Parents + ", " + VParents, OS); - writeNewLine(OS); - } - - if (!I.Members.empty()) { - writeHeader("Members", 2, OS); - for (const auto Member : I.Members) { - std::string Access = getAccess(Member.Access); - if (Access != "") - writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS); - else - writeLine(Member.Type.Name + " " + Member.Name, OS); - } - writeNewLine(OS); - } - - if (!I.ChildRecords.empty()) { - writeHeader("Records", 2, OS); - for (const auto &R : I.ChildRecords) - writeLine(R.Name, OS); - writeNewLine(OS); - } - if (!I.ChildFunctions.empty()) { - writeHeader("Functions", 2, OS); - for (const auto &F : I.ChildFunctions) - genMarkdown(F, OS); - writeNewLine(OS); - } - if (!I.ChildEnums.empty()) { - writeHeader("Enums", 2, OS); - for (const auto &E : I.ChildEnums) - genMarkdown(E, OS); - writeNewLine(OS); - } -} - -/// Generator for Markdown documentation. -class MDGenerator : public Generator { -public: - static const char *Format; - - llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; -}; - -const char *MDGenerator::Format = "md"; - -llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { - switch (I->IT) { - case InfoType::IT_namespace: - genMarkdown(*static_cast(I), OS); - break; - case InfoType::IT_record: - genMarkdown(*static_cast(I), OS); - break; - case InfoType::IT_enum: - genMarkdown(*static_cast(I), OS); - break; - case InfoType::IT_function: - genMarkdown(*static_cast(I), OS); - break; - case InfoType::IT_default: - return llvm::make_error("Unexpected info type.\n", - llvm::inconvertibleErrorCode()); - } - return llvm::Error::success(); -} - -static GeneratorRegistry::Add MD(MDGenerator::Format, - "Generator for MD output."); - -// This anchor is used to force the linker to link in the generated object file -// and thus register the generator. -volatile int MDGeneratorAnchorSource = 0; - -} // namespace doc -} // namespace clang diff --git a/clang-doc/Representation.h b/clang-doc/Representation.h index 57f510126..9e88bd9c8 100644 --- a/clang-doc/Representation.h +++ b/clang-doc/Representation.h @@ -48,14 +48,13 @@ struct CommentInfo { CommentInfo(CommentInfo &Other) = delete; CommentInfo(CommentInfo &&Other) = default; - SmallString<16> - Kind; // Kind of comment (FullComment, ParagraphComment, TextComment, - // InlineCommandComment, HTMLStartTagComment, HTMLEndTagComment, - // BlockCommandComment, ParamCommandComment, - // TParamCommandComment, VerbatimBlockComment, - // VerbatimBlockLineComment, VerbatimLineComment). - SmallString<64> Text; // Text of the comment. - SmallString<16> Name; // Name of the comment (for Verbatim and HTML). + SmallString<16> Kind; // Kind of comment (TextComment, InlineCommandComment, + // HTMLStartTagComment, HTMLEndTagComment, + // BlockCommandComment, ParamCommandComment, + // TParamCommandComment, VerbatimBlockComment, + // VerbatimBlockLineComment, VerbatimLineComment). + SmallString<64> Text; // Text of the comment. + SmallString<16> Name; // Name of the comment (for Verbatim and HTML). SmallString<8> Direction; // Parameter direction (for (T)ParamCommand). SmallString<16> ParamName; // Parameter name (for (T)ParamCommand). SmallString<16> CloseName; // Closing tag name (for VerbatimBlock). diff --git a/clang-doc/YAMLGenerator.cpp b/clang-doc/YAMLGenerator.cpp index e09390198..58c1e1f36 100644 --- a/clang-doc/YAMLGenerator.cpp +++ b/clang-doc/YAMLGenerator.cpp @@ -242,12 +242,12 @@ class YAMLGenerator : public Generator { public: static const char *Format; - llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; + bool generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; }; const char *YAMLGenerator::Format = "yaml"; -llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { +bool YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { llvm::yaml::Output InfoYAML(OS); switch (I->IT) { case InfoType::IT_namespace: @@ -263,10 +263,10 @@ llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { InfoYAML << *static_cast(I); break; case InfoType::IT_default: - return llvm::make_error("Unexpected info type.\n", - llvm::inconvertibleErrorCode()); + llvm::errs() << "Unexpected info type in index.\n"; + return true; } - return llvm::Error::success(); + return false; } static GeneratorRegistry::Add YAML(YAMLGenerator::Format, diff --git a/clang-doc/gen_tests.py b/clang-doc/gen_tests.py index 9259684d5..ccdb069c4 100644 --- a/clang-doc/gen_tests.py +++ b/clang-doc/gen_tests.py @@ -18,19 +18,16 @@ To generate all current tests: - Generate mapper tests: - python gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix mapper -use-check-next + python gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix mapper - Generate reducer tests: - python gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix bc -use-check-next + python gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix bc - Generate yaml tests: - python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix yaml -use-check-next + python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix yaml - Generate public decl tests: - python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix public -use-check-next - -- Generate Markdown tests: - python gen_tests.py -flag='--format=md' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix md + python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix public This script was written on/for Linux, and has not been tested on any other platform and so it may not work. @@ -98,8 +95,7 @@ def get_test_case_code(test_case_path, flags): return code -def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer, - check_next=True): +def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer): output = '' run_cmd = '' if '--dump-mapper' in flags or '--dump-intermediate' in flags: @@ -123,14 +119,8 @@ def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer, output = re.sub(YAML_USR_REGEX, YAML_USR, output) output = re.sub(BITCODE_USR_REGEX, BITCODE_USR, output) output = CHECK.format(checkname) + output.rstrip() - - if check_next: - check_comment = CHECK_NEXT.format(checkname) - else: - check_comment = CHECK.format(checkname) - - output = output.replace('\n', '\n' + check_comment) - output = run_cmd + output.replace('%s\n' % check_comment, "") + output = run_cmd + output.replace('\n', + '\n' + CHECK_NEXT.format(checkname)) return output + '\n' @@ -161,12 +151,6 @@ def main(): metavar="PATH", default='llvm-bcanalyzer', help='path to llvm-bcanalyzer binary') - parser.add_argument( - '-use-check-next', - dest='check_next', - default=False, - action='store_true', - help='Whether or not to use CHECK-NEXT in the resulting tests.') args = parser.parse_args() flags = ' '.join(args.flags) @@ -204,8 +188,7 @@ def main(): if len(usr) < 2: continue all_output += get_output(root, out_file, out_dir, args.flags, - num_outputs, args.bcanalyzer, - args.check_next) + num_outputs, args.bcanalyzer) num_outputs += 1 # Add test case code to test diff --git a/clang-doc/tool/ClangDocMain.cpp b/clang-doc/tool/ClangDocMain.cpp index 6b50f6ca8..6e4a92d7b 100644 --- a/clang-doc/tool/ClangDocMain.cpp +++ b/clang-doc/tool/ClangDocMain.cpp @@ -34,7 +34,6 @@ #include "clang/Tooling/StandaloneExecution.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/APFloat.h" -#include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -70,18 +69,13 @@ static llvm::cl::opt llvm::cl::init(false), llvm::cl::cat(ClangDocCategory)); enum OutputFormatTy { - md, yaml, }; -static llvm::cl::opt - FormatEnum("format", llvm::cl::desc("Format for outputted docs."), - llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml", - "Documentation in YAML format."), - clEnumValN(OutputFormatTy::md, "md", - "Documentation in MD format.")), - llvm::cl::init(OutputFormatTy::yaml), - llvm::cl::cat(ClangDocCategory)); +static llvm::cl::opt FormatEnum( + "format", llvm::cl::desc("Format for outputted docs."), + llvm::cl::values(clEnumVal(yaml, "Documentation in YAML format.")), + llvm::cl::init(yaml), llvm::cl::cat(ClangDocCategory)); static llvm::cl::opt DoxygenOnly( "doxygen", @@ -161,12 +155,10 @@ getInfoOutputFile(StringRef Root, return Path; } -std::string getFormatString() { - switch (FormatEnum) { - case OutputFormatTy::yaml: +std::string getFormatString(OutputFormatTy Ty) { + switch (Ty) { + case yaml: return "yaml"; - case OutputFormatTy::md: - return "md"; } llvm_unreachable("Unknown OutputFormatTy"); } @@ -199,6 +191,14 @@ int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); std::error_code OK; + // Fail early if an invalid format was provided. + std::string Format = getFormatString(FormatEnum); + auto G = doc::findGeneratorByName(Format); + if (!G) { + llvm::errs() << toString(G.takeError()) << "\n"; + return 1; + } + auto Exec = clang::tooling::createExecutorFromCommandLineArgs( argc, argv, ClangDocCategory); @@ -207,15 +207,6 @@ int main(int argc, const char **argv) { return 1; } - // Fail early if an invalid format was provided. - std::string Format = getFormatString(); - llvm::outs() << "Emiting docs in " << Format << " format.\n"; - auto G = doc::findGeneratorByName(Format); - if (!G) { - llvm::errs() << toString(G.takeError()) << "\n"; - return 1; - } - ArgumentsAdjuster ArgAdjuster; if (!DoxygenOnly) ArgAdjuster = combineAdjusters( @@ -286,8 +277,8 @@ int main(int argc, const char **argv) { continue; } - if (auto Err = G->get()->generateDocForInfo(I, InfoOS)) - llvm::errs() << toString(std::move(Err)) << "\n"; + if (G->get()->generateDocForInfo(I, InfoOS)) + llvm::errs() << "Unable to generate docs for info.\n"; } return 0; diff --git a/test/clang-doc/md-comment.cpp b/test/clang-doc/md-comment.cpp deleted file mode 100644 index ae14eaac2..000000000 --- a/test/clang-doc/md-comment.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -/// \brief Brief description. -/// -/// Extended description that -/// continues onto the next line. -/// -///
    -///
  • Testing. -///
-/// -/// \verbatim -/// The description continues. -/// \endverbatim -/// -- -/// \param [out] I is a parameter. -/// \param J is a parameter. -/// \return void -void F(int I, int J); - -/// Bonus comment on definition -void F(int I, int J) {} - -// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: # Global Namespace -// CHECK-0: ## Functions -// CHECK-0: ### void F(int I, int J) -// CHECK-0: *Defined at line 28 of test* -// CHECK-0: **brief** Brief description. -// CHECK-0: Extended description that continues onto the next line. -// CHECK-0:
    -// CHECK-0:
  • -// CHECK-0: Testing.
-// CHECK-0: The description continues. -// CHECK-0: -- -// CHECK-0: **I** [out] -// CHECK-0: **J** -// CHECK-0: **return** void -// CHECK-0: Bonus comment on definition diff --git a/test/clang-doc/md-linkage.cpp b/test/clang-doc/md-linkage.cpp deleted file mode 100644 index 942ff3533..000000000 --- a/test/clang-doc/md-linkage.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// REQUIRES: system-linux -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -void function(int x); - -inline int inlinedFunction(int x); - -int functionWithInnerClass(int x) { - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -inline int inlinedFunctionWithInnerClass(int x) { - class InnerClass { //VisibleNoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -class Class { -public: - void publicMethod(); - int publicField; - -protected: - void protectedMethod(); - int protectedField; - -private: - void privateMethod(); - int privateField; -}; - -namespace named { -class NamedClass { -public: - void namedPublicMethod(); - int namedPublicField; - -protected: - void namedProtectedMethod(); - int namedProtectedField; - -private: - void namedPrivateMethod(); - int namedPrivateField; -}; - -void namedFunction(); -static void namedStaticFunction(); -inline void namedInlineFunction(); -} // namespace named - -static void staticFunction(int x); //Internal Linkage - -static int staticFunctionWithInnerClass(int x) { - class InnerClass { //NoLinkage - public: - int innerPublicMethod() { return 2; }; - }; //end class - InnerClass temp; - return temp.innerPublicMethod(); -}; - -namespace { -class AnonClass { -public: - void anonPublicMethod(); - int anonPublicField; - -protected: - void anonProtectedMethod(); - int anonProtectedField; - -private: - void anonPrivateMethod(); - int anonPrivateField; -}; - -void anonFunction(); -static void anonStaticFunction(); -inline void anonInlineFunction(); -} // namespace - -// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: cat %t/docs/./Class.md | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: # class Class -// CHECK-0: *Defined at line 32 of test* -// CHECK-0: ## Members -// CHECK-0: int publicField -// CHECK-0: protected int protectedField -// CHECK-0: ## Functions -// CHECK-0: ### void publicMethod() -// CHECK-0: ### void protectedMethod() - -// RUN: cat %t/docs/./named.md | FileCheck %s --check-prefix CHECK-1 -// CHECK-1: # namespace named -// CHECK-1: ## Functions -// CHECK-1: ### void namedFunction() -// CHECK-1: ### void namedInlineFunction() - -// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: # Global Namespace -// CHECK-2: ## Functions -// CHECK-2: ### void function(int x) -// CHECK-2: ### int inlinedFunction(int x) -// CHECK-2: ### int functionWithInnerClass(int x) -// CHECK-2: *Defined at line 14 of test* -// CHECK-2: ### int inlinedFunctionWithInnerClass(int x) -// CHECK-2: *Defined at line 23 of test* - -// RUN: cat %t/docs/named/NamedClass.md | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: # class NamedClass -// CHECK-3: *Defined at line 47 of test* -// CHECK-3: ## Members -// CHECK-3: int namedPublicField -// CHECK-3: protected int namedProtectedField -// CHECK-3: ## Functions -// CHECK-3: ### void namedPublicMethod() -// CHECK-3: ### void namedProtectedMethod() diff --git a/test/clang-doc/md-module.cpp b/test/clang-doc/md-module.cpp deleted file mode 100644 index 936f621bc..000000000 --- a/test/clang-doc/md-module.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -export module M; - -int moduleFunction(int x); // ModuleLinkage - -static int staticModuleFunction(int x); // ModuleInternalLinkage - -export double exportedModuleFunction(double y, int z); // ExternalLinkage - -// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: # Global Namespace -// CHECK-0: ## Functions -// CHECK-0: ### int moduleFunction(int x) -// CHECK-0: ### double exportedModuleFunction(double y, int z) diff --git a/test/clang-doc/md-namespace.cpp b/test/clang-doc/md-namespace.cpp deleted file mode 100644 index 6db3fffd2..000000000 --- a/test/clang-doc/md-namespace.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -namespace A { - -void f(); - -} // namespace A - -namespace A { - -void f(){}; - -namespace B { - -enum E { X }; - -E func(int i) { return X; } - -} // namespace B -} // namespace A - -// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: cat %t/docs/./A.md | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: # namespace A -// CHECK-0: ## Functions -// CHECK-0: ### void f() -// CHECK-0: *Defined at line 17 of test* - -// RUN: cat %t/docs/A/B.md | FileCheck %s --check-prefix CHECK-1 -// CHECK-1: # namespace B -// CHECK-1: ## Functions -// CHECK-1: ### enum A::B::E func(int i) -// CHECK-1: *Defined at line 23 of test* -// CHECK-1: ## Enums -// CHECK-1: | enum E | -// CHECK-1: -- -// CHECK-1: | X | -// CHECK-1: *Defined at line 21 of test* diff --git a/test/clang-doc/md-record.cpp b/test/clang-doc/md-record.cpp deleted file mode 100644 index 1c5a1adf3..000000000 --- a/test/clang-doc/md-record.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// THIS IS A GENERATED TEST. DO NOT EDIT. -// To regenerate, see clang-doc/gen_test.py docstring. -// -// This test requires Linux due to system-dependent USR for the inner class. -// REQUIRES: system-linux -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo "" > %t/compile_flags.txt -// RUN: cp "%s" "%t/test.cpp" - -void H() { - class I {}; -} - -union A { int X; int Y; }; - -enum B { X, Y }; - -enum class Bc { A, B }; - -struct C { int i; }; - -class D {}; - -class E { -public: - E() {} - ~E() {} - -protected: - void ProtectedMethod(); -}; - -void E::ProtectedMethod() {} - -class F : virtual private D, public E {}; - -class X { - class Y {}; -}; - -// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs - - -// RUN: cat %t/docs/./F.md | FileCheck %s --check-prefix CHECK-0 -// CHECK-0: # class F -// CHECK-0: *Defined at line 36 of test* -// CHECK-0: Inherits from E, D - -// RUN: cat %t/docs/./D.md | FileCheck %s --check-prefix CHECK-1 -// CHECK-1: # class D -// CHECK-1: *Defined at line 23 of test* - -// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-2 -// CHECK-2: # Global Namespace -// CHECK-2: ## Functions -// CHECK-2: ### void H() -// CHECK-2: *Defined at line 11 of test* -// CHECK-2: ## Enums -// CHECK-2: | enum B | -// CHECK-2: -- -// CHECK-2: | X | -// CHECK-2: | Y | -// CHECK-2: *Defined at line 17 of test* -// CHECK-2: | enum class Bc | -// CHECK-2: -- -// CHECK-2: | A | -// CHECK-2: | B | -// CHECK-2: *Defined at line 19 of test* - -// RUN: cat %t/docs/./E.md | FileCheck %s --check-prefix CHECK-3 -// CHECK-3: # class E -// CHECK-3: *Defined at line 25 of test* -// CHECK-3: ## Functions -// CHECK-3: ### void E() -// CHECK-3: *Defined at line 27 of test* -// CHECK-3: ### void ~E() -// CHECK-3: *Defined at line 28 of test* -// CHECK-3: ### void ProtectedMethod() -// CHECK-3: *Defined at line 34 of test* - -// RUN: cat %t/docs/./C.md | FileCheck %s --check-prefix CHECK-4 -// CHECK-4: # struct C -// CHECK-4: *Defined at line 21 of test* -// CHECK-4: ## Members -// CHECK-4: int i - -// RUN: cat %t/docs/./X.md | FileCheck %s --check-prefix CHECK-5 -// CHECK-5: # class X -// CHECK-5: *Defined at line 38 of test* - -// RUN: cat %t/docs/./A.md | FileCheck %s --check-prefix CHECK-6 -// CHECK-6: # union A -// CHECK-6: *Defined at line 15 of test* -// CHECK-6: ## Members -// CHECK-6: int X -// CHECK-6: int Y From c1fd0e63c226f9abe6840b68d3d0d79e0923c3da Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Fri, 17 Aug 2018 08:15:22 +0000 Subject: [PATCH 057/686] [clangd] Always use the latest preamble Summary: Fix an inconsistent behavior of using `LastBuiltPreamble`/`NewPreamble` in TUScheduler (see the test for details), AST should always use NewPreamble. This patch makes LastBuiltPreamble always point to NewPreamble. Preamble rarely fails to build, even there are errors in headers, so we assume it would not cause performace issue for code completion. Reviewers: ilya-biryukov Reviewed By: ilya-biryukov Subscribers: javed.absar, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50695 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340001 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/TUScheduler.cpp | 3 +-- unittests/clangd/XRefsTests.cpp | 45 +++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/clangd/TUScheduler.cpp b/clangd/TUScheduler.cpp index f985e7d7f..4d65886e6 100644 --- a/clangd/TUScheduler.cpp +++ b/clangd/TUScheduler.cpp @@ -370,8 +370,7 @@ void ASTWorker::update( bool CanReuseAST = InputsAreTheSame && (OldPreamble == NewPreamble); { std::lock_guard Lock(Mutex); - if (NewPreamble) - LastBuiltPreamble = NewPreamble; + LastBuiltPreamble = NewPreamble; } // Before doing the expensive AST reparse, we want to release our reference // to the old preamble, so it can be freed if there are no other references diff --git a/unittests/clangd/XRefsTests.cpp b/unittests/clangd/XRefsTests.cpp index b08af32df..2558cdefa 100644 --- a/unittests/clangd/XRefsTests.cpp +++ b/unittests/clangd/XRefsTests.cpp @@ -1021,6 +1021,51 @@ TEST(GoToInclude, All) { EXPECT_THAT(*Locations, IsEmpty()); } +TEST(GoToDefinition, WithPreamble) { + // Test stragety: AST should always use the latest preamble instead of last + // good preamble. + MockFSProvider FS; + IgnoreDiagnostics DiagConsumer; + MockCompilationDatabase CDB; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + auto FooCpp = testPath("foo.cpp"); + auto FooCppUri = URIForFile{FooCpp}; + // The trigger locations must be the same. + Annotations FooWithHeader(R"cpp(#include "fo^o.h")cpp"); + Annotations FooWithoutHeader(R"cpp(double [[fo^o]]();)cpp"); + + FS.Files[FooCpp] = FooWithHeader.code(); + + auto FooH = testPath("foo.h"); + auto FooHUri = URIForFile{FooH}; + Annotations FooHeader(R"cpp([[]])cpp"); + FS.Files[FooH] = FooHeader.code(); + + runAddDocument(Server, FooCpp, FooWithHeader.code()); + // GoToDefinition goes to a #include file: the result comes from the preamble. + EXPECT_THAT( + cantFail(runFindDefinitions(Server, FooCpp, FooWithHeader.point())), + ElementsAre(Location{FooHUri, FooHeader.range()})); + + // Only preamble is built, and no AST is built in this request. + Server.addDocument(FooCpp, FooWithoutHeader.code(), WantDiagnostics::No); + // We build AST here, and it should use the latest preamble rather than the + // stale one. + EXPECT_THAT( + cantFail(runFindDefinitions(Server, FooCpp, FooWithoutHeader.point())), + ElementsAre(Location{FooCppUri, FooWithoutHeader.range()})); + + // Reset test environment. + runAddDocument(Server, FooCpp, FooWithHeader.code()); + // Both preamble and AST are built in this request. + Server.addDocument(FooCpp, FooWithoutHeader.code(), WantDiagnostics::Yes); + // Use the AST being built in above request. + EXPECT_THAT( + cantFail(runFindDefinitions(Server, FooCpp, FooWithoutHeader.point())), + ElementsAre(Location{FooCppUri, FooWithoutHeader.range()})); +} + } // namespace } // namespace clangd } // namespace clang From 15d645c5bd624bf754f4cf499f3cf2966e080a40 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Fri, 17 Aug 2018 09:29:38 +0000 Subject: [PATCH 058/686] [clangd] Show function documentation in signature help Summary: Previously, clangd was trying to show documentation for the active parameter instead, which is wrong per LSP specification. Moreover, the code path that attempts to get parameter comments never succeds, because no attempt is made to parse function doc comment and extract parameter-specific parts out of it. So we also remove the code that claims to fetch parameter comments: it is not used anymore and is incorrect. Reviewers: hokein, ioeric, kadircet Reviewed By: ioeric Subscribers: MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50726 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340004 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 5 +++-- clangd/CodeCompletionStrings.cpp | 34 ++++++++------------------------ clangd/CodeCompletionStrings.h | 14 ++----------- 3 files changed, 13 insertions(+), 40 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index f603d1a81..02c65757f 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -729,8 +729,9 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { // FIXME: for headers, we need to get a comment from the index. ScoredSignatures.push_back(processOverloadCandidate( Candidate, *CCS, - getParameterDocComment(S.getASTContext(), Candidate, CurrentArg, - /*CommentsFromHeaders=*/false))); + Candidate.getFunction() + ? getDeclComment(S.getASTContext(), *Candidate.getFunction()) + : "")); } std::sort(ScoredSignatures.begin(), ScoredSignatures.end(), [](const ScoredSignature &L, const ScoredSignature &R) { diff --git a/clangd/CodeCompletionStrings.cpp b/clangd/CodeCompletionStrings.cpp index e0277000c..21db26e08 100644 --- a/clangd/CodeCompletionStrings.cpp +++ b/clangd/CodeCompletionStrings.cpp @@ -51,44 +51,26 @@ std::string getDocComment(const ASTContext &Ctx, // get this declaration, so we don't show documentation in that case. if (Result.Kind != CodeCompletionResult::RK_Declaration) return ""; - auto *Decl = Result.getDeclaration(); - if (!Decl || llvm::isa(Decl)) { + return Result.getDeclaration() ? getDeclComment(Ctx, *Result.getDeclaration()) + : ""; +} + +std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &Decl) { + if (llvm::isa(Decl)) { // Namespaces often have too many redecls for any particular redecl comment // to be useful. Moreover, we often confuse file headers or generated // comments with namespace comments. Therefore we choose to just ignore // the comments for namespaces. return ""; } - const RawComment *RC = getCompletionComment(Ctx, Decl); - if (!RC) - return ""; - - // Sanity check that the comment does not come from the PCH. We choose to not - // write them into PCH, because they are racy and slow to load. - assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc())); - std::string Doc = RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); - if (!looksLikeDocComment(Doc)) - return ""; - return Doc; -} - -std::string -getParameterDocComment(const ASTContext &Ctx, - const CodeCompleteConsumer::OverloadCandidate &Result, - unsigned ArgIndex, bool CommentsFromHeaders) { - auto *Func = Result.getFunction(); - if (!Func) - return ""; - const RawComment *RC = getParameterComment(Ctx, Result, ArgIndex); + const RawComment *RC = getCompletionComment(Ctx, &Decl); if (!RC) return ""; // Sanity check that the comment does not come from the PCH. We choose to not // write them into PCH, because they are racy and slow to load. assert(!Ctx.getSourceManager().isLoadedSourceLocation(RC->getBeginLoc())); std::string Doc = RC->getFormattedText(Ctx.getSourceManager(), Ctx.getDiagnostics()); - if (!looksLikeDocComment(Doc)) - return ""; - return Doc; + return looksLikeDocComment(Doc) ? Doc : ""; } void getSignature(const CodeCompletionString &CCS, std::string *Signature, diff --git a/clangd/CodeCompletionStrings.h b/clangd/CodeCompletionStrings.h index 7b66c5c99..bf44cbda2 100644 --- a/clangd/CodeCompletionStrings.h +++ b/clangd/CodeCompletionStrings.h @@ -33,18 +33,8 @@ std::string getDocComment(const ASTContext &Ctx, const CodeCompletionResult &Result, bool CommentsFromHeaders); -/// Gets a minimally formatted documentation for parameter of \p Result, -/// corresponding to argument number \p ArgIndex. -/// This currently looks for comments attached to the parameter itself, and -/// doesn't extract them from function documentation. -/// Returns empty string when no comment is available. -/// If \p CommentsFromHeaders parameter is set, only comments from the main -/// file will be returned. It is used to workaround crashes when parsing -/// comments in the stale headers, coming from completion preamble. -std::string -getParameterDocComment(const ASTContext &Ctx, - const CodeCompleteConsumer::OverloadCandidate &Result, - unsigned ArgIndex, bool CommentsFromHeaders); +/// Similar to getDocComment, but returns the comment for a NamedDecl. +std::string getDeclComment(const ASTContext &Ctx, const NamedDecl &D); /// Formats the signature for an item, as a display string and snippet. /// e.g. for const_reference std::vector::at(size_type) const, this returns: From 8674f62e1d8484bb2169cc846e21de403c13fb3e Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Fri, 17 Aug 2018 09:32:30 +0000 Subject: [PATCH 059/686] [clangd] Fetch documentation from the Index during signature help Summary: Sema can only be used for documentation in the current file, other doc comments should be fetched from the index. Reviewers: hokein, ioeric, kadircet Reviewed By: hokein, kadircet Subscribers: MaskRay, jkorous, mgrang, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50727 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340005 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 7 +- clangd/CodeComplete.cpp | 157 ++++++++++++++++--------- clangd/CodeComplete.h | 11 +- unittests/clangd/CodeCompleteTests.cpp | 58 ++++++++- 4 files changed, 164 insertions(+), 69 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 01cc82394..104a86fd5 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -177,15 +177,16 @@ void ClangdServer::signatureHelp(PathRef File, Position Pos, auto PCHs = this->PCHs; auto FS = FSProvider.getFileSystem(); - auto Action = [Pos, FS, PCHs](Path File, Callback CB, - llvm::Expected IP) { + auto *Index = this->Index; + auto Action = [Pos, FS, PCHs, Index](Path File, Callback CB, + llvm::Expected IP) { if (!IP) return CB(IP.takeError()); auto PreambleData = IP->Preamble; CB(clangd::signatureHelp(File, IP->Command, PreambleData ? &PreambleData->Preamble : nullptr, - IP->Contents, Pos, FS, PCHs)); + IP->Contents, Pos, FS, PCHs, Index)); }; WorkScheduler.runWithPreamble("SignatureHelp", File, diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 02c65757f..36d0f21ed 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -687,18 +687,23 @@ struct CompletionRecorder : public CodeCompleteConsumer { llvm::unique_function ResultsCallback; }; -using ScoredSignature = - std::pair; +struct ScoredSignature { + // When set, requires documentation to be requested from the index with this + // ID. + llvm::Optional IDForDoc; + SignatureInformation Signature; + SignatureQualitySignals Quality; +}; class SignatureHelpCollector final : public CodeCompleteConsumer { - public: SignatureHelpCollector(const clang::CodeCompleteOptions &CodeCompleteOpts, - SignatureHelp &SigHelp) - : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false), + SymbolIndex *Index, SignatureHelp &SigHelp) + : CodeCompleteConsumer(CodeCompleteOpts, + /*OutputIsBinary=*/false), SigHelp(SigHelp), Allocator(std::make_shared()), - CCTUInfo(Allocator) {} + CCTUInfo(Allocator), Index(Index) {} void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, OverloadCandidate *Candidates, @@ -726,47 +731,74 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { const auto *CCS = Candidate.CreateSignatureString( CurrentArg, S, *Allocator, CCTUInfo, true); assert(CCS && "Expected the CodeCompletionString to be non-null"); - // FIXME: for headers, we need to get a comment from the index. ScoredSignatures.push_back(processOverloadCandidate( Candidate, *CCS, Candidate.getFunction() ? getDeclComment(S.getASTContext(), *Candidate.getFunction()) : "")); } - std::sort(ScoredSignatures.begin(), ScoredSignatures.end(), - [](const ScoredSignature &L, const ScoredSignature &R) { - // Ordering follows: - // - Less number of parameters is better. - // - Function is better than FunctionType which is better than - // Function Template. - // - High score is better. - // - Shorter signature is better. - // - Alphebatically smaller is better. - if (L.first.NumberOfParameters != R.first.NumberOfParameters) - return L.first.NumberOfParameters < - R.first.NumberOfParameters; - if (L.first.NumberOfOptionalParameters != - R.first.NumberOfOptionalParameters) - return L.first.NumberOfOptionalParameters < - R.first.NumberOfOptionalParameters; - if (L.first.Kind != R.first.Kind) { - using OC = CodeCompleteConsumer::OverloadCandidate; - switch (L.first.Kind) { - case OC::CK_Function: - return true; - case OC::CK_FunctionType: - return R.first.Kind != OC::CK_Function; - case OC::CK_FunctionTemplate: - return false; - } - llvm_unreachable("Unknown overload candidate type."); - } - if (L.second.label.size() != R.second.label.size()) - return L.second.label.size() < R.second.label.size(); - return L.second.label < R.second.label; - }); - for (const auto &SS : ScoredSignatures) - SigHelp.signatures.push_back(SS.second); + + // Sema does not load the docs from the preamble, so we need to fetch extra + // docs from the index instead. + llvm::DenseMap FetchedDocs; + if (Index) { + LookupRequest IndexRequest; + for (const auto &S : ScoredSignatures) { + if (!S.IDForDoc) + continue; + IndexRequest.IDs.insert(*S.IDForDoc); + } + Index->lookup(IndexRequest, [&](const Symbol &S) { + if (!S.Detail || S.Detail->Documentation.empty()) + return; + FetchedDocs[S.ID] = S.Detail->Documentation; + }); + log("SigHelp: requested docs for {0} symbols from the index, got {1} " + "symbols with non-empty docs in the response", + IndexRequest.IDs.size(), FetchedDocs.size()); + } + + std::sort( + ScoredSignatures.begin(), ScoredSignatures.end(), + [](const ScoredSignature &L, const ScoredSignature &R) { + // Ordering follows: + // - Less number of parameters is better. + // - Function is better than FunctionType which is better than + // Function Template. + // - High score is better. + // - Shorter signature is better. + // - Alphebatically smaller is better. + if (L.Quality.NumberOfParameters != R.Quality.NumberOfParameters) + return L.Quality.NumberOfParameters < R.Quality.NumberOfParameters; + if (L.Quality.NumberOfOptionalParameters != + R.Quality.NumberOfOptionalParameters) + return L.Quality.NumberOfOptionalParameters < + R.Quality.NumberOfOptionalParameters; + if (L.Quality.Kind != R.Quality.Kind) { + using OC = CodeCompleteConsumer::OverloadCandidate; + switch (L.Quality.Kind) { + case OC::CK_Function: + return true; + case OC::CK_FunctionType: + return R.Quality.Kind != OC::CK_Function; + case OC::CK_FunctionTemplate: + return false; + } + llvm_unreachable("Unknown overload candidate type."); + } + if (L.Signature.label.size() != R.Signature.label.size()) + return L.Signature.label.size() < R.Signature.label.size(); + return L.Signature.label < R.Signature.label; + }); + + for (auto &SS : ScoredSignatures) { + auto IndexDocIt = + SS.IDForDoc ? FetchedDocs.find(*SS.IDForDoc) : FetchedDocs.end(); + if (IndexDocIt != FetchedDocs.end()) + SS.Signature.documentation = IndexDocIt->second; + + SigHelp.signatures.push_back(std::move(SS.Signature)); + } } GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; } @@ -779,11 +811,11 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { ScoredSignature processOverloadCandidate(const OverloadCandidate &Candidate, const CodeCompletionString &CCS, llvm::StringRef DocComment) const { - SignatureInformation Result; + SignatureInformation Signature; SignatureQualitySignals Signal; const char *ReturnType = nullptr; - Result.documentation = formatDocumentation(CCS, DocComment); + Signature.documentation = formatDocumentation(CCS, DocComment); Signal.Kind = Candidate.getKind(); for (const auto &Chunk : CCS) { @@ -802,10 +834,10 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { // A piece of text that describes the parameter that corresponds to // the code-completion location within a function call, message send, // macro invocation, etc. - Result.label += Chunk.Text; + Signature.label += Chunk.Text; ParameterInformation Info; Info.label = Chunk.Text; - Result.parameters.push_back(std::move(Info)); + Signature.parameters.push_back(std::move(Info)); Signal.NumberOfParameters++; Signal.ContainsActiveParameter = true; break; @@ -814,29 +846,36 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { // The rest of the parameters are defaulted/optional. assert(Chunk.Optional && "Expected the optional code completion string to be non-null."); - Result.label += - getOptionalParameters(*Chunk.Optional, Result.parameters, Signal); + Signature.label += getOptionalParameters(*Chunk.Optional, + Signature.parameters, Signal); break; } case CodeCompletionString::CK_VerticalSpace: break; default: - Result.label += Chunk.Text; + Signature.label += Chunk.Text; break; } } if (ReturnType) { - Result.label += " -> "; - Result.label += ReturnType; + Signature.label += " -> "; + Signature.label += ReturnType; } - dlog("Signal for {0}: {1}", Result, Signal); - return {Signal, Result}; + dlog("Signal for {0}: {1}", Signature, Signal); + ScoredSignature Result; + Result.Signature = std::move(Signature); + Result.Quality = Signal; + Result.IDForDoc = + Result.Signature.documentation.empty() && Candidate.getFunction() + ? clangd::getSymbolID(Candidate.getFunction()) + : llvm::None; + return Result; } SignatureHelp &SigHelp; std::shared_ptr Allocator; CodeCompletionTUInfo CCTUInfo; - + const SymbolIndex *Index; }; // SignatureHelpCollector struct SemaCompleteInput { @@ -1315,7 +1354,8 @@ SignatureHelp signatureHelp(PathRef FileName, PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, - std::shared_ptr PCHs) { + std::shared_ptr PCHs, + SymbolIndex *Index) { SignatureHelp Result; clang::CodeCompleteOptions Options; Options.IncludeGlobals = false; @@ -1323,10 +1363,11 @@ SignatureHelp signatureHelp(PathRef FileName, Options.IncludeCodePatterns = false; Options.IncludeBriefComments = false; IncludeStructure PreambleInclusions; // Unused for signatureHelp - semaCodeComplete(llvm::make_unique(Options, Result), - Options, - {FileName, Command, Preamble, Contents, Pos, std::move(VFS), - std::move(PCHs)}); + semaCodeComplete( + llvm::make_unique(Options, Index, Result), + Options, + {FileName, Command, Preamble, Contents, Pos, std::move(VFS), + std::move(PCHs)}); return Result; } diff --git a/clangd/CodeComplete.h b/clangd/CodeComplete.h index 227b49f0d..9ef2b1fba 100644 --- a/clangd/CodeComplete.h +++ b/clangd/CodeComplete.h @@ -172,12 +172,11 @@ CodeCompleteResult codeComplete(PathRef FileName, CodeCompleteOptions Opts); /// Get signature help at a specified \p Pos in \p FileName. -SignatureHelp signatureHelp(PathRef FileName, - const tooling::CompileCommand &Command, - PrecompiledPreamble const *Preamble, - StringRef Contents, Position Pos, - IntrusiveRefCntPtr VFS, - std::shared_ptr PCHs); +SignatureHelp +signatureHelp(PathRef FileName, const tooling::CompileCommand &Command, + PrecompiledPreamble const *Preamble, StringRef Contents, + Position Pos, IntrusiveRefCntPtr VFS, + std::shared_ptr PCHs, SymbolIndex *Index); // For index-based completion, we only consider: // * symbols in namespaces or translation unit scopes (e.g. no class diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index a18175065..a95c91ed5 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -826,11 +826,19 @@ TEST(CompletionTest, IgnoreCompleteInExcludedPPBranchWithRecoveryContext) { EXPECT_TRUE(Results.Completions.empty()); } -SignatureHelp signatures(StringRef Text) { +SignatureHelp signatures(StringRef Text, + std::vector IndexSymbols = {}) { + std::unique_ptr Index; + if (!IndexSymbols.empty()) + Index = memIndex(IndexSymbols); + MockFSProvider FS; MockCompilationDatabase CDB; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer::Options Opts = ClangdServer::optsForTest(); + Opts.StaticIndex = Index.get(); + + ClangdServer Server(CDB, FS, DiagConsumer, Opts); auto File = testPath("foo.cpp"); Annotations Test(Text); runAddDocument(Server, File, Test.code()); @@ -845,6 +853,7 @@ MATCHER_P(ParamsAre, P, "") { return false; return true; } +MATCHER_P(SigDoc, Doc, "") { return arg.documentation == Doc; } Matcher Sig(std::string Label, std::vector Params) { @@ -1594,6 +1603,51 @@ TEST(SignatureHelpTest, InstantiatedSignatures) { ElementsAre(Sig("foo(T, U) -> void", {"T", "U"}))); } +TEST(SignatureHelpTest, IndexDocumentation) { + Symbol::Details DocDetails; + DocDetails.Documentation = "Doc from the index"; + + Symbol Foo0 = sym("foo", index::SymbolKind::Function, "@F@\\0#"); + Foo0.Detail = &DocDetails; + Symbol Foo1 = sym("foo", index::SymbolKind::Function, "@F@\\0#I#"); + Foo1.Detail = &DocDetails; + Symbol Foo2 = sym("foo", index::SymbolKind::Function, "@F@\\0#I#I#"); + + EXPECT_THAT( + signatures(R"cpp( + int foo(); + int foo(double); + + void test() { + foo(^); + } + )cpp", + {Foo0}) + .signatures, + ElementsAre(AllOf(Sig("foo() -> int", {}), SigDoc("Doc from the index")), + AllOf(Sig("foo(double) -> int", {"double"}), SigDoc("")))); + + EXPECT_THAT( + signatures(R"cpp( + int foo(); + // Overriden doc from sema + int foo(int); + // Doc from sema + int foo(int, int); + + void test() { + foo(^); + } + )cpp", + {Foo0, Foo1, Foo2}) + .signatures, + ElementsAre(AllOf(Sig("foo() -> int", {}), SigDoc("Doc from the index")), + AllOf(Sig("foo(int) -> int", {"int"}), + SigDoc("Overriden doc from sema")), + AllOf(Sig("foo(int, int) -> int", {"int", "int"}), + SigDoc("Doc from sema")))); +} + } // namespace } // namespace clangd } // namespace clang From fc55336b6c1b2cedd871098232a572c3f2a08712 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Fri, 17 Aug 2018 10:14:31 +0000 Subject: [PATCH 060/686] [clangd] NFC: Mark Workspace Symbol feature complete in the documentation Workspace Symbol implementation was introduced in D44882 and should be complete now. Reviewed by: ioeric Differential Revision: https://reviews.llvm.org/D50703 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340007 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/clangd.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/clangd.rst b/docs/clangd.rst index 97736efdf..a03f2c150 100644 --- a/docs/clangd.rst +++ b/docs/clangd.rst @@ -64,7 +64,7 @@ extension to the protocol. | Completion | Yes | Yes | +-------------------------------------+------------+----------+ | Diagnostics | Yes | Yes | -+-------------------------------------+------------+----------+ ++-------------------------------------+------------+----------+ | Fix-its | Yes | Yes | +-------------------------------------+------------+----------+ | Go to Definition | Yes | Yes | @@ -83,7 +83,7 @@ extension to the protocol. +-------------------------------------+------------+----------+ | Document Symbols | Yes | Yes | +-------------------------------------+------------+----------+ -| Workspace Symbols | Yes | No | +| Workspace Symbols | Yes | Yes | +-------------------------------------+------------+----------+ | Syntax and Semantic Coloring | No | No | +-------------------------------------+------------+----------+ From 038c0c1eb848e482b0f778990c240578be2a54b4 Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Fri, 17 Aug 2018 10:40:05 +0000 Subject: [PATCH 061/686] Fix clangd tests on older compilers Old versions of gcc struggle with raw string literals inside macros. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340009 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/CodeCompleteTests.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index a95c91ed5..5933bb5ff 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1613,22 +1613,21 @@ TEST(SignatureHelpTest, IndexDocumentation) { Foo1.Detail = &DocDetails; Symbol Foo2 = sym("foo", index::SymbolKind::Function, "@F@\\0#I#I#"); - EXPECT_THAT( - signatures(R"cpp( + StringRef Sig0 = R"cpp( int foo(); int foo(double); void test() { foo(^); } - )cpp", - {Foo0}) - .signatures, + )cpp"; + + EXPECT_THAT( + signatures(Sig0, {Foo0}).signatures, ElementsAre(AllOf(Sig("foo() -> int", {}), SigDoc("Doc from the index")), AllOf(Sig("foo(double) -> int", {"double"}), SigDoc("")))); - EXPECT_THAT( - signatures(R"cpp( + StringRef Sig1 = R"cpp( int foo(); // Overriden doc from sema int foo(int); @@ -1638,9 +1637,10 @@ TEST(SignatureHelpTest, IndexDocumentation) { void test() { foo(^); } - )cpp", - {Foo0, Foo1, Foo2}) - .signatures, + )cpp"; + + EXPECT_THAT( + signatures(Sig1, {Foo0, Foo1, Foo2}).signatures, ElementsAre(AllOf(Sig("foo() -> int", {}), SigDoc("Doc from the index")), AllOf(Sig("foo(int) -> int", {"int"}), SigDoc("Overriden doc from sema")), From d575e433d51f2e53cd8985c019ccdb48e9ed6e74 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Fri, 17 Aug 2018 14:55:57 +0000 Subject: [PATCH 062/686] [clangd] Add a testcase for empty preamble. Summary: This is a patch of add a testcase for https://reviews.llvm.org/D50628. Reviewers: ilya-biryukov Subscribers: javed.absar, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50627 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340035 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/TUSchedulerTests.cpp | 45 +++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/unittests/clangd/TUSchedulerTests.cpp b/unittests/clangd/TUSchedulerTests.cpp index f485b0b8c..372eb5231 100644 --- a/unittests/clangd/TUSchedulerTests.cpp +++ b/unittests/clangd/TUSchedulerTests.cpp @@ -308,6 +308,51 @@ TEST_F(TUSchedulerTests, EvictedAST) { UnorderedElementsAre(Foo, AnyOf(Bar, Baz))); } +TEST_F(TUSchedulerTests, EmptyPreamble) { + TUScheduler S( + /*AsyncThreadsCount=*/4, /*StorePreambleInMemory=*/true, + PreambleParsedCallback(), + /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), + ASTRetentionPolicy()); + + auto Foo = testPath("foo.cpp"); + auto Header = testPath("foo.h"); + + Files[Header] = "void foo()"; + Timestamps[Header] = time_t(0); + auto WithPreamble = R"cpp( + #include "foo.h" + int main() {} + )cpp"; + auto WithEmptyPreamble = R"cpp(int main() {})cpp"; + S.update(Foo, getInputs(Foo, WithPreamble), WantDiagnostics::Auto, + [](std::vector) {}); + S.runWithPreamble("getNonEmptyPreamble", Foo, + [&](llvm::Expected Preamble) { + // We expect to get a non-empty preamble. + EXPECT_GT(cantFail(std::move(Preamble)) + .Preamble->Preamble.getBounds() + .Size, + 0u); + }); + // Wait for the preamble is being built. + ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(1))); + + // Update the file which results in an empty preamble. + S.update(Foo, getInputs(Foo, WithEmptyPreamble), WantDiagnostics::Auto, + [](std::vector) {}); + // Wait for the preamble is being built. + ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(1))); + S.runWithPreamble("getEmptyPreamble", Foo, + [&](llvm::Expected Preamble) { + // We expect to get an empty preamble. + EXPECT_EQ(cantFail(std::move(Preamble)) + .Preamble->Preamble.getBounds() + .Size, + 0u); + }); +} + TEST_F(TUSchedulerTests, RunWaitsForPreamble) { // Testing strategy: we update the file and schedule a few preamble reads at // the same time. All reads should get the same non-null preamble. From f63cc3e7e230cb14252a4d924af3de594be968d8 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Fri, 17 Aug 2018 15:19:19 +0000 Subject: [PATCH 063/686] [clang-tidy] Abseil: integral division of Duration check This check is an abseil specific test that tests to ensure users utilize abseil specific floating point division when trying to divide with abseil duration types. Patch by Deanna Garcia! git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340038 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/AbseilTidyModule.cpp | 3 + clang-tidy/abseil/CMakeLists.txt | 1 + clang-tidy/abseil/DurationDivisionCheck.cpp | 59 +++++++++++++++ clang-tidy/abseil/DurationDivisionCheck.h | 35 +++++++++ docs/ReleaseNotes.rst | 7 ++ docs/clang-tidy/checks/list.rst | 1 + test/clang-tidy/abseil-duration-division.cpp | 75 ++++++++++++++++++++ 7 files changed, 181 insertions(+) create mode 100644 clang-tidy/abseil/DurationDivisionCheck.cpp create mode 100644 clang-tidy/abseil/DurationDivisionCheck.h create mode 100644 test/clang-tidy/abseil-duration-division.cpp diff --git a/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tidy/abseil/AbseilTidyModule.cpp index 8e427f058..061fc7c4d 100644 --- a/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tidy/abseil/AbseilTidyModule.cpp @@ -10,6 +10,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "DurationDivisionCheck.h" #include "StringFindStartswithCheck.h" namespace clang { @@ -19,6 +20,8 @@ namespace abseil { class AbseilModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck( + "abseil-duration-division"); CheckFactories.registerCheck( "abseil-string-find-startswith"); } diff --git a/clang-tidy/abseil/CMakeLists.txt b/clang-tidy/abseil/CMakeLists.txt index dd59dcf0c..a2478fd42 100644 --- a/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tidy/abseil/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyAbseilModule AbseilTidyModule.cpp + DurationDivisionCheck.cpp StringFindStartswithCheck.cpp LINK_LIBS diff --git a/clang-tidy/abseil/DurationDivisionCheck.cpp b/clang-tidy/abseil/DurationDivisionCheck.cpp new file mode 100644 index 000000000..5edc15875 --- /dev/null +++ b/clang-tidy/abseil/DurationDivisionCheck.cpp @@ -0,0 +1,59 @@ +//===--- DurationDivisionCheck.cpp - clang-tidy----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DurationDivisionCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +namespace clang { +namespace tidy { +namespace abseil { + +using namespace clang::ast_matchers; + +void DurationDivisionCheck::registerMatchers(MatchFinder *finder) { + if (!getLangOpts().CPlusPlus) + return; + + const auto DurationExpr = + expr(hasType(cxxRecordDecl(hasName("::absl::Duration")))); + finder->addMatcher( + implicitCastExpr( + hasSourceExpression(ignoringParenCasts( + cxxOperatorCallExpr(hasOverloadedOperatorName("/"), + hasArgument(0, DurationExpr), + hasArgument(1, DurationExpr)) + .bind("OpCall"))), + hasImplicitDestinationType(qualType(unless(isInteger()))), + unless(hasParent(cxxStaticCastExpr())), + unless(hasParent(cStyleCastExpr())), + unless(isInTemplateInstantiation())), + this); +} + +void DurationDivisionCheck::check(const MatchFinder::MatchResult &result) { + const auto *OpCall = result.Nodes.getNodeAs("OpCall"); + diag(OpCall->getOperatorLoc(), + "operator/ on absl::Duration objects performs integer division; " + "did you mean to use FDivDuration()?") + << FixItHint::CreateInsertion(OpCall->getBeginLoc(), + "absl::FDivDuration(") + << FixItHint::CreateReplacement( + SourceRange(OpCall->getOperatorLoc(), OpCall->getOperatorLoc()), + ", ") + << FixItHint::CreateInsertion( + Lexer::getLocForEndOfToken( + result.SourceManager->getSpellingLoc(OpCall->getEndLoc()), 0, + *result.SourceManager, result.Context->getLangOpts()), + ")"); +} + +} // namespace abseil +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/abseil/DurationDivisionCheck.h b/clang-tidy/abseil/DurationDivisionCheck.h new file mode 100644 index 000000000..932d02966 --- /dev/null +++ b/clang-tidy/abseil/DurationDivisionCheck.h @@ -0,0 +1,35 @@ +//===--- DurationDivisionCheck.h - clang-tidy--------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_DURATIONDIVISIONCHECK_H_ +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_DURATIONDIVISIONCHECK_H_ + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace abseil { + +// Find potential incorrect uses of integer division of absl::Duration objects. +// +// For the user-facing documentation see: +// http://clang.llvm.org/extra/clang-tidy/checks/abseil-duration-division.html + +class DurationDivisionCheck : public ClangTidyCheck { +public: + using ClangTidyCheck::ClangTidyCheck; + void registerMatchers(ast_matchers::MatchFinder *finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &result) override; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_DURATIONDIVISIONCHECK_H_ diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index 83d5e017c..cea98b334 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -57,6 +57,13 @@ The improvements are... Improvements to clang-tidy -------------------------- +- New :doc:`abseil-duration-division + ` check. + + Checks for uses of ``absl::Duration`` division that is done in a + floating-point context, and recommends the use of a function that + returns a floating-point value. + - New :doc:`readability-magic-numbers ` check. diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index 25515670c..696905b50 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -4,6 +4,7 @@ Clang-Tidy Checks ================= .. toctree:: + abseil-duration-division abseil-string-find-startswith android-cloexec-accept android-cloexec-accept4 diff --git a/test/clang-tidy/abseil-duration-division.cpp b/test/clang-tidy/abseil-duration-division.cpp new file mode 100644 index 000000000..51d012f15 --- /dev/null +++ b/test/clang-tidy/abseil-duration-division.cpp @@ -0,0 +1,75 @@ +// RUN: %check_clang_tidy %s abseil-duration-division %t + +namespace absl { + +class Duration {}; + +int operator/(Duration lhs, Duration rhs); + +double FDivDuration(Duration num, Duration den); + +} // namespace absl + +void TakesDouble(double); + +#define MACRO_EQ(x, y) (x == y) +#define MACRO_DIVEQ(x,y,z) (x/y == z) +#define CHECK(x) (x) + +void Positives() { + absl::Duration d; + + const double num_double = d/d; + // CHECK-MESSAGES: [[@LINE-1]]:30: warning: operator/ on absl::Duration objects performs integer division; did you mean to use FDivDuration()? [abseil-duration-division] + // CHECK-FIXES: const double num_double = absl::FDivDuration(d, d); + const float num_float = d/d; + // CHECK-MESSAGES: [[@LINE-1]]:28: warning: operator/ on absl::Duration objects + // CHECK-FIXES: const float num_float = absl::FDivDuration(d, d); + const auto SomeVal = 1.0 + d/d; + // CHECK-MESSAGES: [[@LINE-1]]:31: warning: operator/ on absl::Duration objects + // CHECK-FIXES: const auto SomeVal = 1.0 + absl::FDivDuration(d, d); + if (MACRO_EQ(d/d, 0.0)) {} + // CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator/ on absl::Duration objects + // CHECK-FIXES: if (MACRO_EQ(absl::FDivDuration(d, d), 0.0)) {} + if (CHECK(MACRO_EQ(d/d, 0.0))) {} + // CHECK-MESSAGES: [[@LINE-1]]:23: warning: operator/ on absl::Duration objects + // CHECK-FIXES: if (CHECK(MACRO_EQ(absl::FDivDuration(d, d), 0.0))) {} + + // This one generates a message, but no fix. + if (MACRO_DIVEQ(d, d, 0.0)) {} + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: operator/ on absl::Duration objects + // CHECK-FIXES: if (MACRO_DIVEQ(d, d, 0.0)) {} + + TakesDouble(d/d); + // CHECK-MESSAGES: [[@LINE-1]]:16: warning: operator/ on absl::Duration objects + // CHECK-FIXES: TakesDouble(absl::FDivDuration(d, d)); +} + +void TakesInt(int); +template +void TakesGeneric(T); + +void Negatives() { + absl::Duration d; + const int num_int = d/d; + const long num_long = d/d; + const short num_short = d/d; + const char num_char = d/d; + const auto num_auto = d/d; + const auto SomeVal = 1 + d/d; + + TakesInt(d/d); + TakesGeneric(d/d); + // Explicit cast should disable the warning. + const double num_cast1 = static_cast(d/d); + const double num_cast2 = (double)(d/d); +} + +template +double DoubleDivision(T t1, T t2) {return t1/t2;} + +//This also won't trigger a warning +void TemplateDivision() { + absl::Duration d; + DoubleDivision(d, d); +} From 055ea3c9a59c50a9511648ef61acd1134794bd5e Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Fri, 17 Aug 2018 15:42:54 +0000 Subject: [PATCH 064/686] [clangd] Add parantheses while auto-completing functions. Summary: Currently we only add parantheses to the functions if snippets are enabled, which also inserts snippets for parameters into parantheses. Adding a new option to put only parantheses. Also it moves the cursor within parantheses or at the end of them by looking at whether completion item has any parameters or not. Still requires snippets support on the client side. Reviewers: ioeric, ilya-biryukov, hokein Reviewed By: ioeric Subscribers: MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50835 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340040 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 13 ++++++- clangd/CodeComplete.h | 4 ++ unittests/clangd/CodeCompleteTests.cpp | 52 ++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 36d0f21ed..df438b483 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -1413,8 +1413,17 @@ CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const { LSP.additionalTextEdits.push_back(FixIt); } } - if (Opts.EnableSnippets) - LSP.textEdit->newText += SnippetSuffix; + if (Opts.EnableSnippets && !SnippetSuffix.empty()) { + if (!Opts.EnableFunctionArgSnippets && + ((Kind == CompletionItemKind::Function) || + (Kind == CompletionItemKind::Method)) && + (SnippetSuffix.front() == '(') && (SnippetSuffix.back() == ')')) + // Check whether function has any parameters or not. + LSP.textEdit->newText += SnippetSuffix.size() > 2 ? "(${0})" : "()"; + else + LSP.textEdit->newText += SnippetSuffix; + } + // FIXME(kadircet): Do not even fill insertText after making sure textEdit is // compatible with most of the editors. LSP.insertText = LSP.textEdit->newText; diff --git a/clangd/CodeComplete.h b/clangd/CodeComplete.h index 9ef2b1fba..1d85a66ec 100644 --- a/clangd/CodeComplete.h +++ b/clangd/CodeComplete.h @@ -82,6 +82,10 @@ struct CodeCompleteOptions { /// Include completions that require small corrections, e.g. change '.' to /// '->' on member access etc. bool IncludeFixIts = false; + + /// Whether to generate snippets for function arguments on code-completion. + /// Needs snippets to be enabled as well. + bool EnableFunctionArgSnippets = true; }; // Semi-structured representation of a code-complete suggestion for our C++ API. diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 5933bb5ff..3fce86626 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1648,6 +1648,58 @@ TEST(SignatureHelpTest, IndexDocumentation) { SigDoc("Doc from sema")))); } +TEST(CompletionTest, RenderWithSnippetsForFunctionArgsDisabled) { + CodeCompleteOptions Opts; + Opts.EnableFunctionArgSnippets = true; + { + CodeCompletion C; + C.RequiredQualifier = "Foo::"; + C.Name = "x"; + C.SnippetSuffix = "()"; + + auto R = C.render(Opts); + EXPECT_EQ(R.textEdit->newText, "Foo::x"); + EXPECT_EQ(R.insertTextFormat, InsertTextFormat::PlainText); + } + + Opts.EnableSnippets = true; + Opts.EnableFunctionArgSnippets = false; + { + CodeCompletion C; + C.RequiredQualifier = "Foo::"; + C.Name = "x"; + C.SnippetSuffix = ""; + + auto R = C.render(Opts); + EXPECT_EQ(R.textEdit->newText, "Foo::x"); + EXPECT_EQ(R.insertTextFormat, InsertTextFormat::Snippet); + } + + { + CodeCompletion C; + C.RequiredQualifier = "Foo::"; + C.Name = "x"; + C.SnippetSuffix = "()"; + C.Kind = CompletionItemKind::Method; + + auto R = C.render(Opts); + EXPECT_EQ(R.textEdit->newText, "Foo::x()"); + EXPECT_EQ(R.insertTextFormat, InsertTextFormat::Snippet); + } + + { + CodeCompletion C; + C.RequiredQualifier = "Foo::"; + C.Name = "x"; + C.SnippetSuffix = "(${0:bool})"; + C.Kind = CompletionItemKind::Function; + + auto R = C.render(Opts); + EXPECT_EQ(R.textEdit->newText, "Foo::x(${0})"); + EXPECT_EQ(R.insertTextFormat, InsertTextFormat::Snippet); + } +} + } // namespace } // namespace clangd } // namespace clang From fa18b8d892275d16735bf327023b71f156058907 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Fri, 17 Aug 2018 19:50:22 +0000 Subject: [PATCH 065/686] [clang-tidy] Add missing check documentation. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340075 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../checks/abseil-duration-division.rst | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 docs/clang-tidy/checks/abseil-duration-division.rst diff --git a/docs/clang-tidy/checks/abseil-duration-division.rst b/docs/clang-tidy/checks/abseil-duration-division.rst new file mode 100644 index 000000000..b7c863560 --- /dev/null +++ b/docs/clang-tidy/checks/abseil-duration-division.rst @@ -0,0 +1,36 @@ +.. title:: clang-tidy - abseil-duration-division + +abseil-duration-division +======================== + +``absl::Duration`` arithmetic works like it does with integers. That means that +division of two ``absl::Duration`` objects returns an ``int64`` with any fractional +component truncated toward 0. See `this link `_ for more information on arithmetic with ``absl::Duration``. + +For example: + +.. code-block:: c++ + + absl::Duration d = absl::Seconds(3.5); + int64 sec1 = d / absl::Seconds(1); // Truncates toward 0. + int64 sec2 = absl::ToInt64Seconds(d); // Equivalent to division. + assert(sec1 == 3 && sec2 == 3); + + double dsec = d / absl::Seconds(1); // WRONG: Still truncates toward 0. + assert(dsec == 3.0); + +If you want floating-point division, you should use either the +``absl::FDivDuration()`` function, or one of the unit conversion functions such +as ``absl::ToDoubleSeconds()``. For example: + +.. code-block:: c++ + + absl::Duration d = absl::Seconds(3.5); + double dsec1 = absl::FDivDuration(d, absl::Seconds(1)); // GOOD: No truncation. + double dsec2 = absl::ToDoubleSeconds(d); // GOOD: No truncation. + assert(dsec1 == 3.5 && dsec2 == 3.5); + + +This check looks for uses of ``absl::Duration`` division that is done in a +floating-point context, and recommends the use of a function that returns a +floating-point value. From f9fa23f35fe6ebcec2ba0f15249eefa87d54ce41 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 20 Aug 2018 08:47:30 +0000 Subject: [PATCH 066/686] [clangd] Implement TRUE Iterator This patch introduces TRUE Iterator which efficiently handles posting lists containing all items within `[0, Size)` range. Reviewed by: ioeric Differential Revision: https://reviews.llvm.org/D50955 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340155 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.cpp | 39 ++++++++++++++++++++++++++++++ clangd/index/dex/Iterator.h | 4 +++ unittests/clangd/DexIndexTests.cpp | 13 ++++++++++ 3 files changed, 56 insertions(+) diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index a554c6627..f967ec4ad 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -225,6 +225,41 @@ class OrIterator : public Iterator { std::vector> Children; }; +/// TrueIterator handles PostingLists which contain all items of the index. It +/// stores size of the virtual posting list, and all operations are performed +/// in O(1). +class TrueIterator : public Iterator { +public: + TrueIterator(DocID Size) : Size(Size) {} + + bool reachedEnd() const override { return Index >= Size; } + + void advance() override { + assert(!reachedEnd() && "Can't advance iterator after it reached the end."); + ++Index; + } + + void advanceTo(DocID ID) override { + assert(!reachedEnd() && "Can't advance iterator after it reached the end."); + Index = std::min(ID, Size); + } + + DocID peek() const override { + assert(!reachedEnd() && "TrueIterator can't call peek() at the end."); + return Index; + } + +private: + llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { + OS << "(TRUE {" << Index << "} out of " << Size << ")"; + return OS; + } + + DocID Index = 0; + /// Size of the underlying virtual PostingList. + DocID Size; +}; + } // end namespace std::vector consume(Iterator &It, size_t Limit) { @@ -249,6 +284,10 @@ createOr(std::vector> Children) { return llvm::make_unique(move(Children)); } +std::unique_ptr createTrue(DocID Size) { + return llvm::make_unique(Size); +} + } // namespace dex } // namespace clangd } // namespace clang diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 06c7fbca1..4773022ab 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -129,6 +129,10 @@ createAnd(std::vector> Children); std::unique_ptr createOr(std::vector> Children); +/// Returns TRUE Iterator which iterates over "virtual" PostingList containing +/// all items in range [0, Size) in an efficient manner. +std::unique_ptr createTrue(DocID Size); + /// This allows createAnd(create(...), create(...)) syntax. template std::unique_ptr createAnd(Args... args) { std::vector> Children; diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index 8be6fc7e5..6a98f3072 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -262,6 +262,19 @@ TEST(DexIndexIterators, Limit) { EXPECT_THAT(consume(*DocIterator, 0), ElementsAre()); } +TEST(DexIndexIterators, True) { + auto TrueIterator = createTrue(0U); + EXPECT_THAT(TrueIterator->reachedEnd(), true); + EXPECT_THAT(consume(*TrueIterator), ElementsAre()); + + PostingList L0 = {1, 2, 5, 7}; + TrueIterator = createTrue(7U); + EXPECT_THAT(TrueIterator->peek(), 0); + auto AndIterator = createAnd(create(L0), move(TrueIterator)); + EXPECT_THAT(AndIterator->reachedEnd(), false); + EXPECT_THAT(consume(*AndIterator), ElementsAre(1, 2, 5)); +} + testing::Matcher> trigramsAre(std::initializer_list Trigrams) { std::vector Tokens; From 82c8cc026d4c6ad07e386e4579d6e2418b285dd2 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Mon, 20 Aug 2018 09:07:59 +0000 Subject: [PATCH 067/686] [clangd] Add missing lock in the lookup. Reviewers: ioeric Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D50960 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340156 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/MemIndex.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index eca0bfe7f..83c036fc7 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -64,6 +64,7 @@ bool MemIndex::fuzzyFind( void MemIndex::lookup(const LookupRequest &Req, llvm::function_ref Callback) const { + std::lock_guard Lock(Mutex); for (const auto &ID : Req.IDs) { auto I = Index.find(ID); if (I != Index.end()) From 090e9c00a5113c6ad786e9d8cd460938b76dc133 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 20 Aug 2018 09:16:14 +0000 Subject: [PATCH 068/686] [clangd] NFC: Cleanup Dex Iterator comments and simplify tests Proposed changes: * Cleanup comments in `clangd/index/dex/Iterator.h`: Vim's `gq` formatting added redundant spaces instead of newlines in few places * Few comments in `OrIterator` are wrong * Use `EXPECT_TRUE(Condition)` instead of `EXPECT_THAT(Condition, true)` (same with `EXPECT_FALSE`) * Don't expose `dump()` method to the public by misplacing `private:` This patch does not affect functionality. Reviewed by: ioeric Differential Revision: https://reviews.llvm.org/D50956 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340157 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.cpp | 10 ++++---- clangd/index/dex/Iterator.h | 8 +++---- unittests/clangd/DexIndexTests.cpp | 38 +++++++++++++++--------------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index f967ec4ad..81f0a205e 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -46,6 +46,7 @@ class DocumentIterator : public Iterator { return *Index; } +private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << '['; auto Separator = ""; @@ -66,7 +67,6 @@ class DocumentIterator : public Iterator { return OS; } -private: PostingListRef Documents; PostingListRef::const_iterator Index; }; @@ -103,6 +103,7 @@ class AndIterator : public Iterator { DocID peek() const override { return Children.front()->peek(); } +private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << "(& "; auto Separator = ""; @@ -114,7 +115,6 @@ class AndIterator : public Iterator { return OS; } -private: /// Restores class invariants: each child will point to the same element after /// sync. void sync() { @@ -180,7 +180,7 @@ class OrIterator : public Iterator { /// Moves each child pointing to the smallest DocID to the next item. void advance() override { assert(!reachedEnd() && - "OrIterator must have at least one child to advance()."); + "OrIterator can't call advance() after it reached the end."); const auto SmallestID = peek(); for (const auto &Child : Children) if (!Child->reachedEnd() && Child->peek() == SmallestID) @@ -199,7 +199,7 @@ class OrIterator : public Iterator { /// value. DocID peek() const override { assert(!reachedEnd() && - "OrIterator must have at least one child to peek()."); + "OrIterator can't peek() after it reached the end."); DocID Result = std::numeric_limits::max(); for (const auto &Child : Children) @@ -209,6 +209,7 @@ class OrIterator : public Iterator { return Result; } +private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << "(| "; auto Separator = ""; @@ -220,7 +221,6 @@ class OrIterator : public Iterator { return OS; } -private: // FIXME(kbobyrev): Would storing Children in min-heap be faster? std::vector> Children; }; diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 4773022ab..cd36499a5 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -11,14 +11,14 @@ // symbol, such as high fuzzy matching score, scope, type etc. The lists of all // symbols matching some criteria (e.g. belonging to "clang::clangd::" scope) // are expressed in a form of Search Tokens which are stored in the inverted -// index. Inverted index maps these tokens to the posting lists - sorted ( by -// symbol quality) sequences of symbol IDs matching the token, e.g. scope token +// index. Inverted index maps these tokens to the posting lists - sorted (by +// symbol quality) sequences of symbol IDs matching the token, e.g. scope token // "clangd::clangd::" is mapped to the list of IDs of all symbols which are // declared in this namespace. Search queries are build from a set of // requirements which can be combined with each other forming the query trees. // The leafs of such trees are posting lists, and the nodes are operations on -// these posting lists, e.g. intersection or union. Efficient processing of -// these multi-level queries is handled by Iterators. Iterators advance through +// these posting lists, e.g. intersection or union. Efficient processing of +// these multi-level queries is handled by Iterators. Iterators advance through // all leaf posting lists producing the result of search query, which preserves // the sorted order of IDs. Having the resulting IDs sorted is important, // because it allows receiving a certain number of the most valuable items (e.g. diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index 6a98f3072..8cc2e7d12 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -28,22 +28,22 @@ TEST(DexIndexIterators, DocumentIterator) { auto DocIterator = create(L); EXPECT_EQ(DocIterator->peek(), 4U); - EXPECT_EQ(DocIterator->reachedEnd(), false); + EXPECT_FALSE(DocIterator->reachedEnd()); DocIterator->advance(); EXPECT_EQ(DocIterator->peek(), 7U); - EXPECT_EQ(DocIterator->reachedEnd(), false); + EXPECT_FALSE(DocIterator->reachedEnd()); DocIterator->advanceTo(20); EXPECT_EQ(DocIterator->peek(), 20U); - EXPECT_EQ(DocIterator->reachedEnd(), false); + EXPECT_FALSE(DocIterator->reachedEnd()); DocIterator->advanceTo(65); EXPECT_EQ(DocIterator->peek(), 100U); - EXPECT_EQ(DocIterator->reachedEnd(), false); + EXPECT_FALSE(DocIterator->reachedEnd()); DocIterator->advanceTo(420); - EXPECT_EQ(DocIterator->reachedEnd(), true); + EXPECT_TRUE(DocIterator->reachedEnd()); } TEST(DexIndexIterators, AndWithEmpty) { @@ -51,10 +51,10 @@ TEST(DexIndexIterators, AndWithEmpty) { const PostingList L1 = {0, 5, 7, 10, 42, 320, 9000}; auto AndEmpty = createAnd(create(L0)); - EXPECT_EQ(AndEmpty->reachedEnd(), true); + EXPECT_TRUE(AndEmpty->reachedEnd()); auto AndWithEmpty = createAnd(create(L0), create(L1)); - EXPECT_EQ(AndWithEmpty->reachedEnd(), true); + EXPECT_TRUE(AndWithEmpty->reachedEnd()); EXPECT_THAT(consume(*AndWithEmpty), ElementsAre()); } @@ -65,7 +65,7 @@ TEST(DexIndexIterators, AndTwoLists) { auto And = createAnd(create(L1), create(L0)); - EXPECT_EQ(And->reachedEnd(), false); + EXPECT_FALSE(And->reachedEnd()); EXPECT_THAT(consume(*And), ElementsAre(0U, 7U, 10U, 320U, 9000U)); And = createAnd(create(L0), create(L1)); @@ -94,7 +94,7 @@ TEST(DexIndexIterators, AndThreeLists) { EXPECT_EQ(And->peek(), 320U); And->advanceTo(100000); - EXPECT_EQ(And->reachedEnd(), true); + EXPECT_TRUE(And->reachedEnd()); } TEST(DexIndexIterators, OrWithEmpty) { @@ -102,10 +102,10 @@ TEST(DexIndexIterators, OrWithEmpty) { const PostingList L1 = {0, 5, 7, 10, 42, 320, 9000}; auto OrEmpty = createOr(create(L0)); - EXPECT_EQ(OrEmpty->reachedEnd(), true); + EXPECT_TRUE(OrEmpty->reachedEnd()); auto OrWithEmpty = createOr(create(L0), create(L1)); - EXPECT_EQ(OrWithEmpty->reachedEnd(), false); + EXPECT_FALSE(OrWithEmpty->reachedEnd()); EXPECT_THAT(consume(*OrWithEmpty), ElementsAre(0U, 5U, 7U, 10U, 42U, 320U, 9000U)); @@ -117,7 +117,7 @@ TEST(DexIndexIterators, OrTwoLists) { auto Or = createOr(create(L0), create(L1)); - EXPECT_EQ(Or->reachedEnd(), false); + EXPECT_FALSE(Or->reachedEnd()); EXPECT_EQ(Or->peek(), 0U); Or->advance(); EXPECT_EQ(Or->peek(), 4U); @@ -136,7 +136,7 @@ TEST(DexIndexIterators, OrTwoLists) { Or->advanceTo(9000); EXPECT_EQ(Or->peek(), 9000U); Or->advanceTo(9001); - EXPECT_EQ(Or->reachedEnd(), true); + EXPECT_TRUE(Or->reachedEnd()); Or = createOr(create(L0), create(L1)); @@ -151,7 +151,7 @@ TEST(DexIndexIterators, OrThreeLists) { auto Or = createOr(create(L0), create(L1), create(L2)); - EXPECT_EQ(Or->reachedEnd(), false); + EXPECT_FALSE(Or->reachedEnd()); EXPECT_EQ(Or->peek(), 0U); Or->advance(); @@ -166,7 +166,7 @@ TEST(DexIndexIterators, OrThreeLists) { EXPECT_EQ(Or->peek(), 60U); Or->advanceTo(9001); - EXPECT_EQ(Or->reachedEnd(), true); + EXPECT_TRUE(Or->reachedEnd()); } // FIXME(kbobyrev): The testcase below is similar to what is expected in real @@ -208,7 +208,7 @@ TEST(DexIndexIterators, QueryTree) { // Lower Or Iterator: [0, 1, 5] createOr(create(L2), create(L3), create(L4))); - EXPECT_EQ(Root->reachedEnd(), false); + EXPECT_FALSE(Root->reachedEnd()); EXPECT_EQ(Root->peek(), 1U); Root->advanceTo(0); // Advance multiple times. Shouldn't do anything. @@ -220,7 +220,7 @@ TEST(DexIndexIterators, QueryTree) { Root->advanceTo(5); EXPECT_EQ(Root->peek(), 5U); Root->advanceTo(9000); - EXPECT_EQ(Root->reachedEnd(), true); + EXPECT_TRUE(Root->reachedEnd()); } TEST(DexIndexIterators, StringRepresentation) { @@ -264,14 +264,14 @@ TEST(DexIndexIterators, Limit) { TEST(DexIndexIterators, True) { auto TrueIterator = createTrue(0U); - EXPECT_THAT(TrueIterator->reachedEnd(), true); + EXPECT_TRUE(TrueIterator->reachedEnd()); EXPECT_THAT(consume(*TrueIterator), ElementsAre()); PostingList L0 = {1, 2, 5, 7}; TrueIterator = createTrue(7U); EXPECT_THAT(TrueIterator->peek(), 0); auto AndIterator = createAnd(create(L0), move(TrueIterator)); - EXPECT_THAT(AndIterator->reachedEnd(), false); + EXPECT_FALSE(AndIterator->reachedEnd()); EXPECT_THAT(consume(*AndIterator), ElementsAre(1, 2, 5)); } From e852ca25b2e22bb30aaea4c14928d442cdabeba3 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Mon, 20 Aug 2018 09:47:12 +0000 Subject: [PATCH 069/686] [clangd] Simplify the code using UniqueStringSaver, NFC. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340161 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Index.cpp | 16 +++++----------- clangd/index/Index.h | 5 ++++- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index 1ae3d5425..eef43603e 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -77,16 +77,10 @@ SymbolSlab::const_iterator SymbolSlab::find(const SymbolID &ID) const { } // Copy the underlying data of the symbol into the owned arena. -static void own(Symbol &S, DenseSet &Strings, +static void own(Symbol &S, llvm::UniqueStringSaver &Strings, BumpPtrAllocator &Arena) { // Intern replaces V with a reference to the same string owned by the arena. - auto Intern = [&](StringRef &V) { - auto R = Strings.insert(V); - if (R.second) { // New entry added to the table, copy the string. - *R.first = V.copy(Arena); - } - V = *R.first; - }; + auto Intern = [&](StringRef &V) { V = Strings.save(V); }; // We need to copy every StringRef field onto the arena. Intern(S.Name); @@ -114,10 +108,10 @@ void SymbolSlab::Builder::insert(const Symbol &S) { auto R = SymbolIndex.try_emplace(S.ID, Symbols.size()); if (R.second) { Symbols.push_back(S); - own(Symbols.back(), Strings, Arena); + own(Symbols.back(), UniqueStrings, Arena); } else { auto &Copy = Symbols[R.first->second] = S; - own(Copy, Strings, Arena); + own(Copy, UniqueStrings, Arena); } } @@ -128,7 +122,7 @@ SymbolSlab SymbolSlab::Builder::build() && { [](const Symbol &L, const Symbol &R) { return L.ID < R.ID; }); // We may have unused strings from overwritten symbols. Build a new arena. BumpPtrAllocator NewArena; - DenseSet Strings; + llvm::UniqueStringSaver Strings(NewArena); for (auto &S : Symbols) own(S, Strings, NewArena); return SymbolSlab(std::move(NewArena), std::move(Symbols)); diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 4ae5cd323..eac4c5e7d 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -17,6 +17,7 @@ #include "llvm/ADT/Hashing.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/StringSaver.h" #include #include @@ -257,6 +258,8 @@ class SymbolSlab { // The frozen SymbolSlab will use less memory. class Builder { public: + Builder() : UniqueStrings(Arena) {} + // Adds a symbol, overwriting any existing one with the same ID. // This is a deep copy: underlying strings will be owned by the slab. void insert(const Symbol &S); @@ -273,7 +276,7 @@ class SymbolSlab { private: llvm::BumpPtrAllocator Arena; // Intern table for strings. Contents are on the arena. - llvm::DenseSet Strings; + llvm::UniqueStringSaver UniqueStrings; std::vector Symbols; // Values are indices into Symbols vector. llvm::DenseMap SymbolIndex; From f232eadf46446dd1abfc800c3406c6ea7d328800 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 20 Aug 2018 14:39:32 +0000 Subject: [PATCH 070/686] [clangd] DexIndex implementation prototype This patch is a proof-of-concept Dex index implementation. It has several flaws, which don't allow replacing static MemIndex yet, such as: * Not being able to handle queries of small size (less than 3 symbols); a way to solve this is generating trigrams of smaller size and having such incomplete trigrams in the index structure. * Speed measurements: while manually editing files in Vim and requesting autocompletion gives an impression that the performance is at least comparable with the current static index, having actual numbers is important because we don't want to hurt the users and roll out slow code. Eric (@ioeric) suggested that we should only replace MemIndex as soon as we have the evidence that this is not a regression in terms of performance. An approach which is likely to be successful here is to wait until we have benchmark library in the LLVM core repository, which is something I have suggested in the LLVM mailing lists, received positive feedback on and started working on. I will add a dependency as soon as the suggested patch is out for a review (currently there's at least one complication which is being addressed by https://github.com/google/benchmark/pull/649). Key performance improvements for iterators are sorting by cost and the limit iterator. * Quality measurements: currently, boosting iterator and two-phase lookup stage are not implemented, without these the quality is likely to be worse than the current implementation can yield. Measuring quality is tricky, but another suggestion in the offline discussion was that the drop-in replacement should only happen after Boosting iterators implementation (and subsequent query enhancement). The proposed changes do not affect Clangd functionality or performance, `DexIndex` is only used in unit tests and not in production code. Reviewed by: ioeric Differential Revision: https://reviews.llvm.org/D50337 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340175 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 1 + clangd/index/dex/DexIndex.cpp | 167 +++++++++++++++++++++++++++ clangd/index/dex/DexIndex.h | 76 ++++++++++++ clangd/index/dex/Token.h | 2 +- unittests/clangd/CMakeLists.txt | 1 + unittests/clangd/DexIndexTests.cpp | 179 ++++++++++++++++++++++++++++- unittests/clangd/IndexTests.cpp | 84 +------------- unittests/clangd/TestIndex.cpp | 83 +++++++++++++ unittests/clangd/TestIndex.h | 64 +++++++++++ 9 files changed, 573 insertions(+), 84 deletions(-) create mode 100644 clangd/index/dex/DexIndex.cpp create mode 100644 clangd/index/dex/DexIndex.h create mode 100644 unittests/clangd/TestIndex.cpp create mode 100644 unittests/clangd/TestIndex.h diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index 33a6e842d..a3c3a6d7a 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -43,6 +43,7 @@ add_clang_library(clangDaemon index/SymbolCollector.cpp index/SymbolYAML.cpp + index/dex/DexIndex.cpp index/dex/Iterator.cpp index/dex/Trigram.cpp diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/DexIndex.cpp new file mode 100644 index 000000000..d7149a01c --- /dev/null +++ b/clangd/index/dex/DexIndex.cpp @@ -0,0 +1,167 @@ +//===--- DexIndex.cpp - Dex Symbol Index Implementation ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DexIndex.h" +#include "../../FuzzyMatch.h" +#include "../../Logger.h" +#include +#include + +namespace clang { +namespace clangd { +namespace dex { + +namespace { + +// Returns the tokens which are given symbol's characteristics. Currently, the +// generated tokens only contain fuzzy matching trigrams and symbol's scope, +// but in the future this will also return path proximity tokens and other +// types of tokens such as symbol type (if applicable). +// Returns the tokens which are given symbols's characteristics. For example, +// trigrams and scopes. +// FIXME(kbobyrev): Support more token types: +// * Path proximity +// * Types +std::vector generateSearchTokens(const Symbol &Sym) { + std::vector Result = generateIdentifierTrigrams(Sym.Name); + Result.push_back(Token(Token::Kind::Scope, Sym.Scope)); + return Result; +} + +} // namespace + +void DexIndex::build(std::shared_ptr> Syms) { + llvm::DenseMap TempLookupTable; + llvm::DenseMap TempSymbolQuality; + for (const Symbol *Sym : *Syms) { + TempLookupTable[Sym->ID] = Sym; + TempSymbolQuality[Sym] = quality(*Sym); + } + + // Symbols are sorted by symbol qualities so that items in the posting lists + // are stored in the descending order of symbol quality. + std::sort(begin(*Syms), end(*Syms), + [&](const Symbol *LHS, const Symbol *RHS) { + return TempSymbolQuality[LHS] > TempSymbolQuality[RHS]; + }); + llvm::DenseMap TempInvertedIndex; + // Populate TempInvertedIndex with posting lists for index symbols. + for (DocID SymbolRank = 0; SymbolRank < Syms->size(); ++SymbolRank) { + const auto *Sym = (*Syms)[SymbolRank]; + for (const auto &Token : generateSearchTokens(*Sym)) + TempInvertedIndex[Token].push_back(SymbolRank); + } + + { + std::lock_guard Lock(Mutex); + + // Replace outdated index with the new one. + LookupTable = std::move(TempLookupTable); + Symbols = std::move(Syms); + InvertedIndex = std::move(TempInvertedIndex); + SymbolQuality = std::move(TempSymbolQuality); + } +} + +/// Constructs iterators over tokens extracted from the query and exhausts it +/// while applying Callback to each symbol in the order of decreasing quality +/// of the matched symbols. +bool DexIndex::fuzzyFind( + const FuzzyFindRequest &Req, + llvm::function_ref Callback) const { + assert(!StringRef(Req.Query).contains("::") && + "There must be no :: in query."); + FuzzyMatcher Filter(Req.Query); + bool More = false; + + std::vector> TopLevelChildren; + const auto TrigramTokens = generateIdentifierTrigrams(Req.Query); + + { + std::lock_guard Lock(Mutex); + + // Generate query trigrams and construct AND iterator over all query + // trigrams. + std::vector> TrigramIterators; + for (const auto &Trigram : TrigramTokens) { + const auto It = InvertedIndex.find(Trigram); + if (It != InvertedIndex.end()) + TrigramIterators.push_back(create(It->second)); + } + if (!TrigramIterators.empty()) + TopLevelChildren.push_back(createAnd(move(TrigramIterators))); + + // Generate scope tokens for search query. + std::vector> ScopeIterators; + for (const auto &Scope : Req.Scopes) { + const auto It = InvertedIndex.find(Token(Token::Kind::Scope, Scope)); + if (It != InvertedIndex.end()) + ScopeIterators.push_back(create(It->second)); + } + // Add OR iterator for scopes if there are any Scope Iterators. + if (!ScopeIterators.empty()) + TopLevelChildren.push_back(createOr(move(ScopeIterators))); + + // Use TRUE iterator if both trigrams and scopes from the query are not + // present in the symbol index. + auto QueryIterator = TopLevelChildren.empty() + ? createTrue(Symbols->size()) + : createAnd(move(TopLevelChildren)); + // Retrieve more items than it was requested: some of the items with high + // final score might not be retrieved otherwise. + // FIXME(kbobyrev): Pre-scoring retrieval threshold should be adjusted as + // using 100x of the requested number might not be good in practice, e.g. + // when the requested number of items is small. + const unsigned ItemsToRetrieve = 100 * Req.MaxCandidateCount; + std::vector SymbolDocIDs = consume(*QueryIterator, ItemsToRetrieve); + + // Retrieve top Req.MaxCandidateCount items. + std::priority_queue> Top; + for (const auto &SymbolDocID : SymbolDocIDs) { + const auto *Sym = (*Symbols)[SymbolDocID]; + const llvm::Optional Score = Filter.match(Sym->Name); + if (!Score) + continue; + // Multiply score by a negative factor so that Top stores items with the + // highest actual score. + Top.emplace(-(*Score) * SymbolQuality.find(Sym)->second, Sym); + if (Top.size() > Req.MaxCandidateCount) { + More = true; + Top.pop(); + } + } + + // Apply callback to the top Req.MaxCandidateCount items. + for (; !Top.empty(); Top.pop()) + Callback(*Top.top().second); + } + + return More; +} + +void DexIndex::lookup(const LookupRequest &Req, + llvm::function_ref Callback) const { + std::lock_guard Lock(Mutex); + for (const auto &ID : Req.IDs) { + auto I = LookupTable.find(ID); + if (I != LookupTable.end()) + Callback(*I->second); + } +} + + +void DexIndex::findOccurrences( + const OccurrencesRequest &Req, + llvm::function_ref Callback) const { + log("findOccurrences is not implemented."); +} + +} // namespace dex +} // namespace clangd +} // namespace clang diff --git a/clangd/index/dex/DexIndex.h b/clangd/index/dex/DexIndex.h new file mode 100644 index 000000000..31d390324 --- /dev/null +++ b/clangd/index/dex/DexIndex.h @@ -0,0 +1,76 @@ +//===--- DexIndex.h - Dex Symbol Index Implementation -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines Dex - a symbol index implementation based on query iterators +// over symbol tokens, such as fuzzy matching trigrams, scopes, types, etc. +// While consuming more memory and having longer build stage due to +// preprocessing, Dex will have substantially lower latency. It will also allow +// efficient symbol searching which is crucial for operations like code +// completion, and can be very important for a number of different code +// transformations which will be eventually supported by Clangd. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEXINDEX_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEXINDEX_H + +#include "../Index.h" +#include "../MemIndex.h" +#include "Iterator.h" +#include "Token.h" +#include "Trigram.h" +#include + +namespace clang { +namespace clangd { +namespace dex { + +/// In-memory Dex trigram-based index implementation. +// FIXME(kbobyrev): Introduce serialization and deserialization of the symbol +// index so that it can be loaded from the disk. Since static index is not +// changed frequently, it's safe to assume that it has to be built only once +// (when the clangd process starts). Therefore, it can be easier to store built +// index on disk and then load it if available. +class DexIndex : public SymbolIndex { +public: + /// \brief (Re-)Build index for `Symbols`. All symbol pointers must remain + /// accessible as long as `Symbols` is kept alive. + void build(std::shared_ptr> Symbols); + + bool + fuzzyFind(const FuzzyFindRequest &Req, + llvm::function_ref Callback) const override; + + void lookup(const LookupRequest &Req, + llvm::function_ref Callback) const override; + + void findOccurrences(const OccurrencesRequest &Req, + llvm::function_ref + Callback) const override; + +private: + mutable std::mutex Mutex; + + std::shared_ptr> Symbols /*GUARDED_BY(Mutex)*/; + llvm::DenseMap LookupTable /*GUARDED_BY(Mutex)*/; + llvm::DenseMap SymbolQuality /*GUARDED_BY(Mutex)*/; + // Inverted index is a mapping from the search token to the posting list, + // which contains all items which can be characterized by such search token. + // For example, if the search token is scope "std::", the corresponding + // posting list would contain all indices of symbols defined in namespace std. + // Inverted index is used to retrieve posting lists which are processed during + // the fuzzyFind process. + llvm::DenseMap InvertedIndex /*GUARDED_BY(Mutex)*/; +}; + +} // namespace dex +} // namespace clangd +} // namespace clang + +#endif diff --git a/clangd/index/dex/Token.h b/clangd/index/dex/Token.h index f31368cb4..af12f670d 100644 --- a/clangd/index/dex/Token.h +++ b/clangd/index/dex/Token.h @@ -22,9 +22,9 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_DEX_TOKEN_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_DEX_TOKEN_H +#include "../Index.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/raw_ostream.h" - #include #include diff --git a/unittests/clangd/CMakeLists.txt b/unittests/clangd/CMakeLists.txt index 516060eec..ab0a21209 100644 --- a/unittests/clangd/CMakeLists.txt +++ b/unittests/clangd/CMakeLists.txt @@ -30,6 +30,7 @@ add_extra_unittest(ClangdTests SyncAPI.cpp TUSchedulerTests.cpp TestFS.cpp + TestIndex.cpp TestTU.cpp ThreadingTests.cpp TraceTests.cpp diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index 8cc2e7d12..e2cdc3438 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -7,6 +7,10 @@ // //===----------------------------------------------------------------------===// +#include "TestIndex.h" +#include "index/Index.h" +#include "index/Merge.h" +#include "index/dex/DexIndex.h" #include "index/dex/Iterator.h" #include "index/dex/Token.h" #include "index/dex/Trigram.h" @@ -17,11 +21,13 @@ #include #include +using ::testing::ElementsAre; +using ::testing::UnorderedElementsAre; + namespace clang { namespace clangd { namespace dex { - -using ::testing::ElementsAre; +namespace { TEST(DexIndexIterators, DocumentIterator) { const PostingList L = {4, 7, 8, 20, 42, 100}; @@ -359,6 +365,175 @@ TEST(DexIndexTrigrams, QueryTrigrams) { "hij", "ijk", "jkl", "klm"})); } +TEST(DexIndex, Lookup) { + DexIndex I; + I.build(generateSymbols({"ns::abc", "ns::xyz"})); + EXPECT_THAT(lookup(I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); + EXPECT_THAT(lookup(I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), + UnorderedElementsAre("ns::abc", "ns::xyz")); + EXPECT_THAT(lookup(I, {SymbolID("ns::nonono"), SymbolID("ns::xyz")}), + UnorderedElementsAre("ns::xyz")); + EXPECT_THAT(lookup(I, SymbolID("ns::nonono")), UnorderedElementsAre()); +} + +TEST(DexIndex, FuzzyFind) { + DexIndex Index; + Index.build(generateSymbols({"ns::ABC", "ns::BCD", "::ABC", "ns::nested::ABC", + "other::ABC", "other::A"})); + FuzzyFindRequest Req; + Req.Query = "ABC"; + Req.Scopes = {"ns::"}; + EXPECT_THAT(match(Index, Req), UnorderedElementsAre("ns::ABC")); + Req.Scopes = {"ns::", "ns::nested::"}; + EXPECT_THAT(match(Index, Req), + UnorderedElementsAre("ns::ABC", "ns::nested::ABC")); + Req.Query = "A"; + Req.Scopes = {"other::"}; + EXPECT_THAT(match(Index, Req), + UnorderedElementsAre("other::A", "other::ABC")); + Req.Query = ""; + Req.Scopes = {}; + EXPECT_THAT(match(Index, Req), + UnorderedElementsAre("ns::ABC", "ns::BCD", "::ABC", + "ns::nested::ABC", "other::ABC", + "other::A")); +} + +TEST(DexIndexTest, FuzzyMatchQ) { + DexIndex I; + I.build( + generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"})); + FuzzyFindRequest Req; + Req.Query = "lol"; + Req.MaxCandidateCount = 2; + EXPECT_THAT(match(I, Req), + UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); +} + +TEST(DexIndexTest, DexIndexSymbolsRecycled) { + DexIndex I; + std::weak_ptr Symbols; + I.build(generateNumSymbols(0, 10, &Symbols)); + FuzzyFindRequest Req; + Req.Query = "7"; + EXPECT_THAT(match(I, Req), UnorderedElementsAre("7")); + + EXPECT_FALSE(Symbols.expired()); + // Release old symbols. + I.build(generateNumSymbols(0, 0)); + EXPECT_TRUE(Symbols.expired()); +} + +// FIXME(kbobyrev): This test is different for DexIndex and MemIndex: while +// MemIndex manages response deduplication, DexIndex simply returns all matched +// symbols which means there might be equivalent symbols in the response. +// Before drop-in replacement of MemIndex with DexIndex happens, FileIndex +// should handle deduplication instead. +TEST(DexIndexTest, DexIndexDeduplicate) { + auto Symbols = generateNumSymbols(0, 10); + + // Inject duplicates. + auto Sym = symbol("7"); + Symbols->push_back(&Sym); + Symbols->push_back(&Sym); + Symbols->push_back(&Sym); + + FuzzyFindRequest Req; + Req.Query = "7"; + DexIndex I; + I.build(std::move(Symbols)); + auto Matches = match(I, Req); + EXPECT_EQ(Matches.size(), 4u); +} + +TEST(DexIndexTest, DexIndexLimitedNumMatches) { + DexIndex I; + I.build(generateNumSymbols(0, 100)); + FuzzyFindRequest Req; + Req.Query = "5"; + Req.MaxCandidateCount = 3; + bool Incomplete; + auto Matches = match(I, Req, &Incomplete); + EXPECT_EQ(Matches.size(), Req.MaxCandidateCount); + EXPECT_TRUE(Incomplete); +} + +TEST(DexIndexTest, FuzzyMatch) { + DexIndex I; + I.build( + generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"})); + FuzzyFindRequest Req; + Req.Query = "lol"; + Req.MaxCandidateCount = 2; + EXPECT_THAT(match(I, Req), + UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); +} + +TEST(DexIndexTest, MatchQualifiedNamesWithoutSpecificScope) { + DexIndex I; + I.build(generateSymbols({"a::y1", "b::y2", "y3"})); + FuzzyFindRequest Req; + Req.Query = "y"; + EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3")); +} + +TEST(DexIndexTest, MatchQualifiedNamesWithGlobalScope) { + DexIndex I; + I.build(generateSymbols({"a::y1", "b::y2", "y3"})); + FuzzyFindRequest Req; + Req.Query = "y"; + Req.Scopes = {""}; + EXPECT_THAT(match(I, Req), UnorderedElementsAre("y3")); +} + +TEST(DexIndexTest, MatchQualifiedNamesWithOneScope) { + DexIndex I; + I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"})); + FuzzyFindRequest Req; + Req.Query = "y"; + Req.Scopes = {"a::"}; + EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1", "a::y2")); +} + +TEST(DexIndexTest, MatchQualifiedNamesWithMultipleScopes) { + DexIndex I; + I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"})); + FuzzyFindRequest Req; + Req.Query = "y"; + Req.Scopes = {"a::", "b::"}; + EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1", "a::y2", "b::y3")); +} + +TEST(DexIndexTest, NoMatchNestedScopes) { + DexIndex I; + I.build(generateSymbols({"a::y1", "a::b::y2"})); + FuzzyFindRequest Req; + Req.Query = "y"; + Req.Scopes = {"a::"}; + EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1")); +} + +TEST(DexIndexTest, IgnoreCases) { + DexIndex I; + I.build(generateSymbols({"ns::ABC", "ns::abc"})); + FuzzyFindRequest Req; + Req.Query = "AB"; + Req.Scopes = {"ns::"}; + EXPECT_THAT(match(I, Req), UnorderedElementsAre("ns::ABC", "ns::abc")); +} + +TEST(DexIndexTest, Lookup) { + DexIndex I; + I.build(generateSymbols({"ns::abc", "ns::xyz"})); + EXPECT_THAT(lookup(I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); + EXPECT_THAT(lookup(I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), + UnorderedElementsAre("ns::abc", "ns::xyz")); + EXPECT_THAT(lookup(I, {SymbolID("ns::nonono"), SymbolID("ns::xyz")}), + UnorderedElementsAre("ns::xyz")); + EXPECT_THAT(lookup(I, SymbolID("ns::nonono")), UnorderedElementsAre()); +} + +} // namespace } // namespace dex } // namespace clangd } // namespace clang diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp index 3ddf2d26a..5d3e85ff5 100644 --- a/unittests/clangd/IndexTests.cpp +++ b/unittests/clangd/IndexTests.cpp @@ -7,33 +7,20 @@ // //===----------------------------------------------------------------------===// +#include "TestIndex.h" #include "index/Index.h" #include "index/MemIndex.h" #include "index/Merge.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -using testing::UnorderedElementsAre; using testing::Pointee; +using testing::UnorderedElementsAre; namespace clang { namespace clangd { namespace { -Symbol symbol(llvm::StringRef QName) { - Symbol Sym; - Sym.ID = SymbolID(QName.str()); - size_t Pos = QName.rfind("::"); - if (Pos == llvm::StringRef::npos) { - Sym.Name = QName; - Sym.Scope = ""; - } else { - Sym.Name = QName.substr(Pos + 2); - Sym.Scope = QName.substr(0, Pos + 2); - } - return Sym; -} - MATCHER_P(Named, N, "") { return arg.Name == N; } TEST(SymbolSlab, FindAndIterate) { @@ -52,59 +39,6 @@ TEST(SymbolSlab, FindAndIterate) { EXPECT_THAT(*S.find(SymbolID(Sym)), Named(Sym)); } -struct SlabAndPointers { - SymbolSlab Slab; - std::vector Pointers; -}; - -// Create a slab of symbols with the given qualified names as both IDs and -// names. The life time of the slab is managed by the returned shared pointer. -// If \p WeakSymbols is provided, it will be pointed to the managed object in -// the returned shared pointer. -std::shared_ptr> -generateSymbols(std::vector QualifiedNames, - std::weak_ptr *WeakSymbols = nullptr) { - SymbolSlab::Builder Slab; - for (llvm::StringRef QName : QualifiedNames) - Slab.insert(symbol(QName)); - - auto Storage = std::make_shared(); - Storage->Slab = std::move(Slab).build(); - for (const auto &Sym : Storage->Slab) - Storage->Pointers.push_back(&Sym); - if (WeakSymbols) - *WeakSymbols = Storage; - auto *Pointers = &Storage->Pointers; - return {std::move(Storage), Pointers}; -} - -// Create a slab of symbols with IDs and names [Begin, End], otherwise identical -// to the `generateSymbols` above. -std::shared_ptr> -generateNumSymbols(int Begin, int End, - std::weak_ptr *WeakSymbols = nullptr) { - std::vector Names; - for (int i = Begin; i <= End; i++) - Names.push_back(std::to_string(i)); - return generateSymbols(Names, WeakSymbols); -} - -std::string getQualifiedName(const Symbol &Sym) { - return (Sym.Scope + Sym.Name).str(); -} - -std::vector match(const SymbolIndex &I, - const FuzzyFindRequest &Req, - bool *Incomplete = nullptr) { - std::vector Matches; - bool IsIncomplete = I.fuzzyFind(Req, [&](const Symbol &Sym) { - Matches.push_back(getQualifiedName(Sym)); - }); - if (Incomplete) - *Incomplete = IsIncomplete; - return Matches; -} - TEST(MemIndexTest, MemIndexSymbolsRecycled) { MemIndex I; std::weak_ptr Symbols; @@ -212,18 +146,6 @@ TEST(MemIndexTest, IgnoreCases) { EXPECT_THAT(match(I, Req), UnorderedElementsAre("ns::ABC", "ns::abc")); } -// Returns qualified names of symbols with any of IDs in the index. -std::vector lookup(const SymbolIndex &I, - llvm::ArrayRef IDs) { - LookupRequest Req; - Req.IDs.insert(IDs.begin(), IDs.end()); - std::vector Results; - I.lookup(Req, [&](const Symbol &Sym) { - Results.push_back(getQualifiedName(Sym)); - }); - return Results; -} - TEST(MemIndexTest, Lookup) { MemIndex I; I.build(generateSymbols({"ns::abc", "ns::xyz"})); @@ -269,7 +191,7 @@ TEST(MergeIndexTest, FuzzyFind) { TEST(MergeTest, Merge) { Symbol L, R; L.ID = R.ID = SymbolID("hello"); - L.Name = R.Name = "Foo"; // same in both + L.Name = R.Name = "Foo"; // same in both L.CanonicalDeclaration.FileURI = "file:///left.h"; // differs R.CanonicalDeclaration.FileURI = "file:///right.h"; L.References = 1; diff --git a/unittests/clangd/TestIndex.cpp b/unittests/clangd/TestIndex.cpp new file mode 100644 index 000000000..760abee61 --- /dev/null +++ b/unittests/clangd/TestIndex.cpp @@ -0,0 +1,83 @@ +//===-- IndexHelpers.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestIndex.h" + +namespace clang { +namespace clangd { + +Symbol symbol(llvm::StringRef QName) { + Symbol Sym; + Sym.ID = SymbolID(QName.str()); + size_t Pos = QName.rfind("::"); + if (Pos == llvm::StringRef::npos) { + Sym.Name = QName; + Sym.Scope = ""; + } else { + Sym.Name = QName.substr(Pos + 2); + Sym.Scope = QName.substr(0, Pos + 2); + } + return Sym; +} + +std::shared_ptr> +generateSymbols(std::vector QualifiedNames, + std::weak_ptr *WeakSymbols) { + SymbolSlab::Builder Slab; + for (llvm::StringRef QName : QualifiedNames) + Slab.insert(symbol(QName)); + + auto Storage = std::make_shared(); + Storage->Slab = std::move(Slab).build(); + for (const auto &Sym : Storage->Slab) + Storage->Pointers.push_back(&Sym); + if (WeakSymbols) + *WeakSymbols = Storage; + auto *Pointers = &Storage->Pointers; + return {std::move(Storage), Pointers}; +} + +std::shared_ptr> +generateNumSymbols(int Begin, int End, + std::weak_ptr *WeakSymbols) { + std::vector Names; + for (int i = Begin; i <= End; i++) + Names.push_back(std::to_string(i)); + return generateSymbols(Names, WeakSymbols); +} + +std::string getQualifiedName(const Symbol &Sym) { + return (Sym.Scope + Sym.Name).str(); +} + +std::vector match(const SymbolIndex &I, + const FuzzyFindRequest &Req, bool *Incomplete) { + std::vector Matches; + bool IsIncomplete = I.fuzzyFind(Req, [&](const Symbol &Sym) { + Matches.push_back(clang::clangd::getQualifiedName(Sym)); + }); + if (Incomplete) + *Incomplete = IsIncomplete; + return Matches; +} + +// Returns qualified names of symbols with any of IDs in the index. +std::vector lookup(const SymbolIndex &I, + llvm::ArrayRef IDs) { + LookupRequest Req; + Req.IDs.insert(IDs.begin(), IDs.end()); + std::vector Results; + I.lookup(Req, [&](const Symbol &Sym) { + Results.push_back(getQualifiedName(Sym)); + }); + return Results; +} + +} // namespace clangd +} // namespace clang diff --git a/unittests/clangd/TestIndex.h b/unittests/clangd/TestIndex.h new file mode 100644 index 000000000..f49c9c351 --- /dev/null +++ b/unittests/clangd/TestIndex.h @@ -0,0 +1,64 @@ +//===-- IndexHelpers.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_INDEXTESTCOMMON_H +#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_INDEXTESTCOMMON_H + +#include "index/Index.h" +#include "index/Merge.h" +#include "index/dex/DexIndex.h" +#include "index/dex/Iterator.h" +#include "index/dex/Token.h" +#include "index/dex/Trigram.h" + +namespace clang { +namespace clangd { + +// Creates Symbol instance and sets SymbolID to given QualifiedName. +Symbol symbol(llvm::StringRef QName); + +// Bundles symbol pointers with the actual symbol slab the pointers refer to in +// order to ensure that the slab isn't destroyed while it's used by and index. +struct SlabAndPointers { + SymbolSlab Slab; + std::vector Pointers; +}; + +// Create a slab of symbols with the given qualified names as both IDs and +// names. The life time of the slab is managed by the returned shared pointer. +// If \p WeakSymbols is provided, it will be pointed to the managed object in +// the returned shared pointer. +std::shared_ptr> +generateSymbols(std::vector QualifiedNames, + std::weak_ptr *WeakSymbols = nullptr); + +// Create a slab of symbols with IDs and names [Begin, End], otherwise identical +// to the `generateSymbols` above. +std::shared_ptr> +generateNumSymbols(int Begin, int End, + std::weak_ptr *WeakSymbols = nullptr); + +// Returns fully-qualified name out of given symbol. +std::string getQualifiedName(const Symbol &Sym); + +// Performs fuzzy matching-based symbol lookup given a query and an index. +// Incomplete is set true if more items than requested can be retrieved, false +// otherwise. +std::vector match(const SymbolIndex &I, + const FuzzyFindRequest &Req, + bool *Incomplete = nullptr); + +// Returns qualified names of symbols with any of IDs in the index. +std::vector lookup(const SymbolIndex &I, + llvm::ArrayRef IDs); + +} // namespace clangd +} // namespace clang + +#endif From 8083822340f1f2bc969317ebcb2d8ddcaa17addb Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 21 Aug 2018 10:32:27 +0000 Subject: [PATCH 071/686] [clangd] Allow using experimental Dex index This patch adds hidden Clangd flag ("use-dex-index") which replaces (currently) default `MemIndex` with `DexIndex` for the static index. Reviewed by: ioeric Differential Revision: https://reviews.llvm.org/D50897 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340262 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/MemIndex.cpp | 28 ++++++++++++++++------------ clangd/index/MemIndex.h | 5 +++++ clangd/index/dex/DexIndex.cpp | 6 ++++++ clangd/index/dex/DexIndex.h | 3 +++ clangd/tool/ClangdMain.cpp | 11 ++++++++++- 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index 83c036fc7..db11244d8 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -28,6 +28,12 @@ void MemIndex::build(std::shared_ptr> Syms) { } } +std::unique_ptr MemIndex::build(SymbolSlab Slab) { + auto Idx = llvm::make_unique(); + Idx->build(getSymbolsFromSlab(std::move(Slab))); + return std::move(Idx); +} + bool MemIndex::fuzzyFind( const FuzzyFindRequest &Req, llvm::function_ref Callback) const { @@ -72,7 +78,14 @@ void MemIndex::lookup(const LookupRequest &Req, } } -std::unique_ptr MemIndex::build(SymbolSlab Slab) { +void MemIndex::findOccurrences( + const OccurrencesRequest &Req, + llvm::function_ref Callback) const { + log("findOccurrences is not implemented."); +} + +std::shared_ptr> +getSymbolsFromSlab(SymbolSlab Slab) { struct Snapshot { SymbolSlab Slab; std::vector Pointers; @@ -81,17 +94,8 @@ std::unique_ptr MemIndex::build(SymbolSlab Slab) { Snap->Slab = std::move(Slab); for (auto &Sym : Snap->Slab) Snap->Pointers.push_back(&Sym); - auto S = std::shared_ptr>(std::move(Snap), - &Snap->Pointers); - auto MemIdx = llvm::make_unique(); - MemIdx->build(std::move(S)); - return std::move(MemIdx); -} - -void MemIndex::findOccurrences( - const OccurrencesRequest &Req, - llvm::function_ref Callback) const { - log("findOccurrences is not implemented."); + return std::shared_ptr>(std::move(Snap), + &Snap->Pointers); } } // namespace clangd diff --git a/clangd/index/MemIndex.h b/clangd/index/MemIndex.h index 0f59b8900..8b12f8c79 100644 --- a/clangd/index/MemIndex.h +++ b/clangd/index/MemIndex.h @@ -47,6 +47,11 @@ class MemIndex : public SymbolIndex { mutable std::mutex Mutex; }; +// Returns pointers to the symbols in given slab and bundles slab lifetime with +// returned symbol pointers so that the pointers are never invalid. +std::shared_ptr> +getSymbolsFromSlab(SymbolSlab Slab); + } // namespace clangd } // namespace clang diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/DexIndex.cpp index d7149a01c..d2d2bf91e 100644 --- a/clangd/index/dex/DexIndex.cpp +++ b/clangd/index/dex/DexIndex.cpp @@ -69,6 +69,12 @@ void DexIndex::build(std::shared_ptr> Syms) { } } +std::unique_ptr DexIndex::build(SymbolSlab Slab) { + auto Idx = llvm::make_unique(); + Idx->build(getSymbolsFromSlab(std::move(Slab))); + return std::move(Idx); +} + /// Constructs iterators over tokens extracted from the query and exhausts it /// while applying Callback to each symbol in the order of decreasing quality /// of the matched symbols. diff --git a/clangd/index/dex/DexIndex.h b/clangd/index/dex/DexIndex.h index 31d390324..4485150b1 100644 --- a/clangd/index/dex/DexIndex.h +++ b/clangd/index/dex/DexIndex.h @@ -43,6 +43,9 @@ class DexIndex : public SymbolIndex { /// accessible as long as `Symbols` is kept alive. void build(std::shared_ptr> Symbols); + /// \brief Build index from a symbol slab. + static std::unique_ptr build(SymbolSlab Slab); + bool fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref Callback) const override; diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 04a7358aa..f7decf39f 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -12,6 +12,7 @@ #include "Path.h" #include "Trace.h" #include "index/SymbolYAML.h" +#include "index/dex/DexIndex.h" #include "clang/Basic/Version.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" @@ -29,6 +30,7 @@ using namespace clang; using namespace clang::clangd; namespace { + enum class PCHStorageFlag { Disk, Memory }; // Build an in-memory static index for global symbols from a YAML-format file. @@ -45,8 +47,10 @@ std::unique_ptr buildStaticIndex(llvm::StringRef YamlSymbolFile) { for (auto Sym : Slab) SymsBuilder.insert(Sym); - return MemIndex::build(std::move(SymsBuilder).build()); + return UseDex ? DexIndex::build(std::move(SymsBuilder).build()) + : MemIndex::build(std::move(SymsBuilder).build()); } + } // namespace static llvm::cl::opt CompileCommandsDir( @@ -185,6 +189,11 @@ static llvm::cl::opt CompileArgsFrom( "'compile_commands.json' files")), llvm::cl::init(FilesystemCompileArgs), llvm::cl::Hidden); +static llvm::cl::opt + UseDex("use-dex-index", + llvm::cl::desc("Use experimental Dex static index."), + llvm::cl::init(false), llvm::cl::Hidden); + int main(int argc, char *argv[]) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) { From ee81cfbaacf20abaa578e09bdbe3aaf57dcf9ed9 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 21 Aug 2018 10:40:19 +0000 Subject: [PATCH 072/686] [clangd] NFC: Fix broken build git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340263 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/tool/ClangdMain.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index f7decf39f..0149caec8 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -29,6 +29,11 @@ using namespace clang; using namespace clang::clangd; +static llvm::cl::opt + UseDex("use-dex-index", + llvm::cl::desc("Use experimental Dex static index."), + llvm::cl::init(false), llvm::cl::Hidden); + namespace { enum class PCHStorageFlag { Disk, Memory }; @@ -47,7 +52,7 @@ std::unique_ptr buildStaticIndex(llvm::StringRef YamlSymbolFile) { for (auto Sym : Slab) SymsBuilder.insert(Sym); - return UseDex ? DexIndex::build(std::move(SymsBuilder).build()) + return UseDex ? dex::DexIndex::build(std::move(SymsBuilder).build()) : MemIndex::build(std::move(SymsBuilder).build()); } @@ -189,11 +194,6 @@ static llvm::cl::opt CompileArgsFrom( "'compile_commands.json' files")), llvm::cl::init(FilesystemCompileArgs), llvm::cl::Hidden); -static llvm::cl::opt - UseDex("use-dex-index", - llvm::cl::desc("Use experimental Dex static index."), - llvm::cl::init(false), llvm::cl::Hidden); - int main(int argc, char *argv[]) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) { From 14bc21503b7acc54a0357ecb5f6babaf7d34d940 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Wed, 22 Aug 2018 07:17:59 +0000 Subject: [PATCH 073/686] [clangd] Cleanup after D50897 The wrong diff that was uploaded to Phabricator was building the wrong index. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340388 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/DexIndex.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/DexIndex.cpp index d2d2bf91e..847fa02c3 100644 --- a/clangd/index/dex/DexIndex.cpp +++ b/clangd/index/dex/DexIndex.cpp @@ -70,7 +70,7 @@ void DexIndex::build(std::shared_ptr> Syms) { } std::unique_ptr DexIndex::build(SymbolSlab Slab) { - auto Idx = llvm::make_unique(); + auto Idx = llvm::make_unique(); Idx->build(getSymbolsFromSlab(std::move(Slab))); return std::move(Idx); } From ccf09bca932025f1f07e17439ea80e1242e7c739 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Wed, 22 Aug 2018 11:39:16 +0000 Subject: [PATCH 074/686] [clangd] Add callbacks on parsed AST in addition to parsed preambles Summary: Will be used for updating the dynamic index on updates to the open files. Currently we collect only information coming from the preamble AST. This has a bunch of limitations: - Dynamic index misses important information from the body of the file, e.g. locations of definitions. - XRefs cannot be collected at all, since we can only obtain full information for the current file (preamble is parsed with skipped function bodies, therefore not reliable). This patch only adds the new callback, actually updates to the index will be done in a follow-up patch. Reviewers: hokein Reviewed By: hokein Subscribers: kadircet, javed.absar, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50847 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340401 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 30 ++++++++++++++------ clangd/ClangdServer.h | 2 ++ clangd/TUScheduler.cpp | 41 ++++++++++++++++----------- clangd/TUScheduler.h | 28 ++++++++++++++++-- unittests/clangd/TUSchedulerTests.cpp | 26 ++++++++--------- 5 files changed, 86 insertions(+), 41 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 104a86fd5..efaa687af 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -66,6 +66,24 @@ class RefactoringResultCollector final Optional> Result; }; +class UpdateFileIndex : public ParsingCallbacks { +public: + UpdateFileIndex(FileIndex *FileIdx) : FileIdx(FileIdx) {} + + void onPreambleAST(PathRef Path, ASTContext &Ctx, + std::shared_ptr PP) override { + if (FileIdx) + FileIdx->update(Path, &Ctx, std::move(PP)); + } + + void onMainAST(PathRef Path, ParsedAST &AST) override { + // FIXME: merge results from the main file into the index too. + } + +private: + FileIndex *FileIdx; +}; + } // namespace ClangdServer::Options ClangdServer::optsForTest() { @@ -85,20 +103,16 @@ ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB, : getStandardResourceDir()), FileIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex(Opts.URISchemes) : nullptr), + FileIdxUpdater(llvm::make_unique(FileIdx.get())), PCHs(std::make_shared()), // Pass a callback into `WorkScheduler` to extract symbols from a newly // parsed file and rebuild the file index synchronously each time an AST // is parsed. // FIXME(ioeric): this can be slow and we may be able to index on less // critical paths. - WorkScheduler( - Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory, - FileIdx - ? [this](PathRef Path, ASTContext &AST, - std::shared_ptr - PP) { FileIdx->update(Path, &AST, std::move(PP)); } - : PreambleParsedCallback(), - Opts.UpdateDebounce, Opts.RetentionPolicy) { + WorkScheduler(Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory, + *FileIdxUpdater, Opts.UpdateDebounce, + Opts.RetentionPolicy) { if (FileIdx && Opts.StaticIndex) { MergedIndex = mergeIndex(FileIdx.get(), Opts.StaticIndex); Index = MergedIndex.get(); diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h index 745f8c307..ebe1390ff 100644 --- a/clangd/ClangdServer.h +++ b/clangd/ClangdServer.h @@ -223,6 +223,8 @@ class ClangdServer { SymbolIndex *Index; // If present, an up-to-date of symbols in open files. Read via Index. std::unique_ptr FileIdx; + /// Callbacks responsible for updating FileIdx. + std::unique_ptr FileIdxUpdater; // If present, a merged view of FileIdx and an external index. Read via Index. std::unique_ptr MergedIndex; // If set, this represents the workspace path. diff --git a/clangd/TUScheduler.cpp b/clangd/TUScheduler.cpp index 4d65886e6..e61a54d4f 100644 --- a/clangd/TUScheduler.cpp +++ b/clangd/TUScheduler.cpp @@ -158,8 +158,7 @@ class ASTWorker { Semaphore &Barrier, bool RunSync, steady_clock::duration UpdateDebounce, std::shared_ptr PCHs, - bool StorePreamblesInMemory, - PreambleParsedCallback PreambleCallback); + bool StorePreamblesInMemory, ParsingCallbacks &Callbacks); public: /// Create a new ASTWorker and return a handle to it. @@ -173,7 +172,7 @@ class ASTWorker { steady_clock::duration UpdateDebounce, std::shared_ptr PCHs, bool StorePreamblesInMemory, - PreambleParsedCallback PreambleCallback); + ParsingCallbacks &Callbacks); ~ASTWorker(); void update(ParseInputs Inputs, WantDiagnostics, @@ -228,8 +227,8 @@ class ASTWorker { const Path FileName; /// Whether to keep the built preambles in memory or on disk. const bool StorePreambleInMemory; - /// Callback, passed to the preamble builder. - const PreambleParsedCallback PreambleCallback; + /// Callback, invoked when preamble or main file AST is built. + ParsingCallbacks &Callbacks; /// Helper class required to build the ASTs. const std::shared_ptr PCHs; @@ -299,10 +298,10 @@ ASTWorkerHandle ASTWorker::create(PathRef FileName, steady_clock::duration UpdateDebounce, std::shared_ptr PCHs, bool StorePreamblesInMemory, - PreambleParsedCallback PreambleCallback) { + ParsingCallbacks &Callbacks) { std::shared_ptr Worker(new ASTWorker( FileName, IdleASTs, Barrier, /*RunSync=*/!Tasks, UpdateDebounce, - std::move(PCHs), StorePreamblesInMemory, std::move(PreambleCallback))); + std::move(PCHs), StorePreamblesInMemory, Callbacks)); if (Tasks) Tasks->runAsync("worker:" + llvm::sys::path::filename(FileName), [Worker]() { Worker->run(); }); @@ -314,12 +313,11 @@ ASTWorker::ASTWorker(PathRef FileName, TUScheduler::ASTCache &LRUCache, Semaphore &Barrier, bool RunSync, steady_clock::duration UpdateDebounce, std::shared_ptr PCHs, - bool StorePreamblesInMemory, - PreambleParsedCallback PreambleCallback) + bool StorePreamblesInMemory, ParsingCallbacks &Callbacks) : IdleASTs(LRUCache), RunSync(RunSync), UpdateDebounce(UpdateDebounce), FileName(FileName), StorePreambleInMemory(StorePreamblesInMemory), - PreambleCallback(std::move(PreambleCallback)), PCHs(std::move(PCHs)), - Barrier(Barrier), Done(false) {} + Callbacks(Callbacks), PCHs(std::move(PCHs)), Barrier(Barrier), + Done(false) {} ASTWorker::~ASTWorker() { // Make sure we remove the cached AST, if any. @@ -365,7 +363,11 @@ void ASTWorker::update( getPossiblyStalePreamble(); std::shared_ptr NewPreamble = buildPreamble(FileName, *Invocation, OldPreamble, OldCommand, Inputs, - PCHs, StorePreambleInMemory, PreambleCallback); + PCHs, StorePreambleInMemory, + [this](PathRef Path, ASTContext &Ctx, + std::shared_ptr PP) { + Callbacks.onPreambleAST(FileName, Ctx, std::move(PP)); + }); bool CanReuseAST = InputsAreTheSame && (OldPreamble == NewPreamble); { @@ -415,6 +417,7 @@ void ASTWorker::update( // Note *AST can be still be null if buildAST fails. if (*AST) { OnUpdated((*AST)->getDiagnostics()); + Callbacks.onMainAST(FileName, **AST); DiagsWereReported = true; } // Stash the AST in the cache for further use. @@ -618,6 +621,11 @@ unsigned getDefaultAsyncThreadsCount() { return HardwareConcurrency; } +ParsingCallbacks &noopParsingCallbacks() { + static ParsingCallbacks *Instance = new ParsingCallbacks; + return *Instance; +} + struct TUScheduler::FileData { /// Latest inputs, passed to TUScheduler::update(). std::string Contents; @@ -627,12 +635,12 @@ struct TUScheduler::FileData { TUScheduler::TUScheduler(unsigned AsyncThreadsCount, bool StorePreamblesInMemory, - PreambleParsedCallback PreambleCallback, + ParsingCallbacks &Callbacks, std::chrono::steady_clock::duration UpdateDebounce, ASTRetentionPolicy RetentionPolicy) : StorePreamblesInMemory(StorePreamblesInMemory), - PCHOps(std::make_shared()), - PreambleCallback(std::move(PreambleCallback)), Barrier(AsyncThreadsCount), + PCHOps(std::make_shared()), Callbacks(Callbacks), + Barrier(AsyncThreadsCount), IdleASTs(llvm::make_unique(RetentionPolicy.MaxRetainedASTs)), UpdateDebounce(UpdateDebounce) { if (0 < AsyncThreadsCount) { @@ -670,8 +678,7 @@ void TUScheduler::update( // Create a new worker to process the AST-related tasks. ASTWorkerHandle Worker = ASTWorker::create( File, *IdleASTs, WorkerThreads ? WorkerThreads.getPointer() : nullptr, - Barrier, UpdateDebounce, PCHOps, StorePreamblesInMemory, - PreambleCallback); + Barrier, UpdateDebounce, PCHOps, StorePreamblesInMemory, Callbacks); FD = std::unique_ptr(new FileData{ Inputs.Contents, Inputs.CompileCommand, std::move(Worker)}); } else { diff --git a/clangd/TUScheduler.h b/clangd/TUScheduler.h index 90f3fe7b5..27542c662 100644 --- a/clangd/TUScheduler.h +++ b/clangd/TUScheduler.h @@ -51,6 +51,30 @@ struct ASTRetentionPolicy { unsigned MaxRetainedASTs = 3; }; +class ParsingCallbacks { +public: + virtual ~ParsingCallbacks() = default; + + /// Called on the AST that was built for emitting the preamble. The built AST + /// contains only AST nodes from the #include directives at the start of the + /// file. AST node in the current file should be observed on onMainAST call. + virtual void onPreambleAST(PathRef Path, ASTContext &Ctx, + std::shared_ptr PP) {} + /// Called on the AST built for the file itself. Note that preamble AST nodes + /// are not deserialized and should be processed in the onPreambleAST call + /// instead. + /// The \p AST always contains all AST nodes for the main file itself, and + /// only a portion of the AST nodes deserialized from the preamble. Note that + /// some nodes from the preamble may have been deserialized and may also be + /// accessed from the main file AST, e.g. redecls of functions from preamble, + /// etc. Clients are expected to process only the AST nodes from the main file + /// in this callback (obtained via ParsedAST::getLocalTopLevelDecls) to obtain + /// optimal performance. + virtual void onMainAST(PathRef Path, ParsedAST &AST) {} +}; + +ParsingCallbacks &noopParsingCallbacks(); + /// Handles running tasks for ClangdServer and managing the resources (e.g., /// preambles and ASTs) for opened files. /// TUScheduler is not thread-safe, only one thread should be providing updates @@ -61,7 +85,7 @@ struct ASTRetentionPolicy { class TUScheduler { public: TUScheduler(unsigned AsyncThreadsCount, bool StorePreamblesInMemory, - PreambleParsedCallback PreambleCallback, + ParsingCallbacks &ASTCallbacks, std::chrono::steady_clock::duration UpdateDebounce, ASTRetentionPolicy RetentionPolicy); ~TUScheduler(); @@ -132,7 +156,7 @@ class TUScheduler { private: const bool StorePreamblesInMemory; const std::shared_ptr PCHOps; - const PreambleParsedCallback PreambleCallback; + ParsingCallbacks &Callbacks; Semaphore Barrier; llvm::StringMap> Files; std::unique_ptr IdleASTs; diff --git a/unittests/clangd/TUSchedulerTests.cpp b/unittests/clangd/TUSchedulerTests.cpp index 372eb5231..2842d5086 100644 --- a/unittests/clangd/TUSchedulerTests.cpp +++ b/unittests/clangd/TUSchedulerTests.cpp @@ -17,10 +17,11 @@ namespace clang { namespace clangd { +namespace { using ::testing::_; -using ::testing::Each; using ::testing::AnyOf; +using ::testing::Each; using ::testing::Pair; using ::testing::Pointee; using ::testing::UnorderedElementsAre; @@ -44,8 +45,7 @@ class TUSchedulerTests : public ::testing::Test { TEST_F(TUSchedulerTests, MissingFiles) { TUScheduler S(getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - /*PreambleParsedCallback=*/nullptr, + /*StorePreamblesInMemory=*/true, noopParsingCallbacks(), /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); @@ -101,8 +101,7 @@ TEST_F(TUSchedulerTests, WantDiagnostics) { Notification Ready; TUScheduler S( getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - /*PreambleParsedCallback=*/nullptr, + /*StorePreamblesInMemory=*/true, noopParsingCallbacks(), /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); auto Path = testPath("foo.cpp"); @@ -130,8 +129,7 @@ TEST_F(TUSchedulerTests, Debounce) { std::atomic CallbackCount(0); { TUScheduler S(getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - /*PreambleParsedCallback=*/nullptr, + /*StorePreamblesInMemory=*/true, noopParsingCallbacks(), /*UpdateDebounce=*/std::chrono::seconds(1), ASTRetentionPolicy()); // FIXME: we could probably use timeouts lower than 1 second here. @@ -162,8 +160,7 @@ TEST_F(TUSchedulerTests, ManyUpdates) { // Run TUScheduler and collect some stats. { TUScheduler S(getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, - /*PreambleParsedCallback=*/nullptr, + /*StorePreamblesInMemory=*/true, noopParsingCallbacks(), /*UpdateDebounce=*/std::chrono::milliseconds(50), ASTRetentionPolicy()); @@ -261,7 +258,7 @@ TEST_F(TUSchedulerTests, EvictedAST) { Policy.MaxRetainedASTs = 2; TUScheduler S( /*AsyncThreadsCount=*/1, /*StorePreambleInMemory=*/true, - PreambleParsedCallback(), + noopParsingCallbacks(), /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), Policy); llvm::StringLiteral SourceContents = R"cpp( @@ -311,7 +308,7 @@ TEST_F(TUSchedulerTests, EvictedAST) { TEST_F(TUSchedulerTests, EmptyPreamble) { TUScheduler S( /*AsyncThreadsCount=*/4, /*StorePreambleInMemory=*/true, - PreambleParsedCallback(), + noopParsingCallbacks(), /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); @@ -358,7 +355,7 @@ TEST_F(TUSchedulerTests, RunWaitsForPreamble) { // the same time. All reads should get the same non-null preamble. TUScheduler S( /*AsyncThreadsCount=*/4, /*StorePreambleInMemory=*/true, - PreambleParsedCallback(), + noopParsingCallbacks(), /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); auto Foo = testPath("foo.cpp"); @@ -391,7 +388,7 @@ TEST_F(TUSchedulerTests, RunWaitsForPreamble) { TEST_F(TUSchedulerTests, NoopOnEmptyChanges) { TUScheduler S( /*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(), - /*StorePreambleInMemory=*/true, PreambleParsedCallback(), + /*StorePreambleInMemory=*/true, noopParsingCallbacks(), /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); @@ -444,7 +441,7 @@ TEST_F(TUSchedulerTests, NoopOnEmptyChanges) { TEST_F(TUSchedulerTests, NoChangeDiags) { TUScheduler S( /*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(), - /*StorePreambleInMemory=*/true, PreambleParsedCallback(), + /*StorePreambleInMemory=*/true, noopParsingCallbacks(), /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); @@ -475,5 +472,6 @@ TEST_F(TUSchedulerTests, NoChangeDiags) { ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(1))); } +} // namespace } // namespace clangd } // namespace clang From c8fb39efb5cd428381652de3470098ed9d023cfe Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Wed, 22 Aug 2018 12:43:17 +0000 Subject: [PATCH 075/686] [clangd] Make FileIndex aware of the main file Summary: It was previously only indexing the preamble decls. The new implementation will index both the preamble and the main AST and report both sets of symbols, preferring the ones from the main AST whenever the symbol is present in both. The symbols in the main AST slab always store all information available in the preamble symbols, possibly adding more, e.g. definition locations. Reviewers: hokein, ioeric Reviewed By: ioeric Subscribers: kadircet, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50889 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340404 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 50 +++++++++++++++++++++++++------------- clangd/ClangdServer.h | 13 +++++----- clangd/index/FileIndex.cpp | 20 +++++++++------ clangd/index/FileIndex.h | 14 ++++++++--- 4 files changed, 64 insertions(+), 33 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index efaa687af..0a3a26c82 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -65,27 +65,39 @@ class RefactoringResultCollector final Optional> Result; }; +} // namespace -class UpdateFileIndex : public ParsingCallbacks { +/// Manages dynamic index for open files. Each file might contribute two sets +/// of symbols to the dynamic index: symbols from the preamble and symbols +/// from the file itself. Those have different lifetimes and we merge results +/// from both +class ClangdServer::DynamicIndex : public ParsingCallbacks { public: - UpdateFileIndex(FileIndex *FileIdx) : FileIdx(FileIdx) {} + DynamicIndex(std::vector URISchemes) + : PreambleIdx(URISchemes), MainFileIdx(URISchemes), + MergedIndex(mergeIndex(&MainFileIdx, &PreambleIdx)) {} + + SymbolIndex &index() const { return *MergedIndex; } void onPreambleAST(PathRef Path, ASTContext &Ctx, std::shared_ptr PP) override { - if (FileIdx) - FileIdx->update(Path, &Ctx, std::move(PP)); + PreambleIdx.update(Path, &Ctx, PP, /*TopLevelDecls=*/llvm::None); } void onMainAST(PathRef Path, ParsedAST &AST) override { - // FIXME: merge results from the main file into the index too. + + MainFileIdx.update(Path, &AST.getASTContext(), AST.getPreprocessorPtr(), + AST.getLocalTopLevelDecls()); } private: - FileIndex *FileIdx; + FileIndex PreambleIdx; + FileIndex MainFileIdx; + /// Merged view into both indexes. Merges are performed in a similar manner + /// to the merges of dynamic and static index. + std::unique_ptr MergedIndex; }; -} // namespace - ClangdServer::Options ClangdServer::optsForTest() { ClangdServer::Options Opts; Opts.UpdateDebounce = std::chrono::steady_clock::duration::zero(); // Faster! @@ -101,9 +113,9 @@ ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB, : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider), ResourceDir(Opts.ResourceDir ? Opts.ResourceDir->str() : getStandardResourceDir()), - FileIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex(Opts.URISchemes) - : nullptr), - FileIdxUpdater(llvm::make_unique(FileIdx.get())), + DynamicIdx(Opts.BuildDynamicSymbolIndex + ? new DynamicIndex(Opts.URISchemes) + : nullptr), PCHs(std::make_shared()), // Pass a callback into `WorkScheduler` to extract symbols from a newly // parsed file and rebuild the file index synchronously each time an AST @@ -111,19 +123,23 @@ ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB, // FIXME(ioeric): this can be slow and we may be able to index on less // critical paths. WorkScheduler(Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory, - *FileIdxUpdater, Opts.UpdateDebounce, - Opts.RetentionPolicy) { - if (FileIdx && Opts.StaticIndex) { - MergedIndex = mergeIndex(FileIdx.get(), Opts.StaticIndex); + DynamicIdx ? *DynamicIdx : noopParsingCallbacks(), + Opts.UpdateDebounce, Opts.RetentionPolicy) { + if (DynamicIdx && Opts.StaticIndex) { + MergedIndex = mergeIndex(&DynamicIdx->index(), Opts.StaticIndex); Index = MergedIndex.get(); - } else if (FileIdx) - Index = FileIdx.get(); + } else if (DynamicIdx) + Index = &DynamicIdx->index(); else if (Opts.StaticIndex) Index = Opts.StaticIndex; else Index = nullptr; } +// Destructor has to be in .cpp file to see the definition of +// ClangdServer::DynamicIndex. +ClangdServer::~ClangdServer() = default; + void ClangdServer::setRootPath(PathRef RootPath) { auto FS = FSProvider.getFileSystem(); auto Status = FS->status(RootPath); diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h index ebe1390ff..561355381 100644 --- a/clangd/ClangdServer.h +++ b/clangd/ClangdServer.h @@ -99,6 +99,7 @@ class ClangdServer { /// synchronize access to shared state. ClangdServer(GlobalCompilationDatabase &CDB, FileSystemProvider &FSProvider, DiagnosticsConsumer &DiagConsumer, const Options &Opts); + ~ClangdServer(); /// Set the root path of the workspace. void setRootPath(PathRef RootPath); @@ -200,6 +201,7 @@ class ClangdServer { formatCode(llvm::StringRef Code, PathRef File, ArrayRef Ranges); + class DynamicIndex; typedef uint64_t DocVersion; void consumeDiagnostics(PathRef File, DocVersion Version, @@ -217,15 +219,14 @@ class ClangdServer { Path ResourceDir; // The index used to look up symbols. This could be: // - null (all index functionality is optional) - // - the dynamic index owned by ClangdServer (FileIdx) + // - the dynamic index owned by ClangdServer (DynamicIdx) // - the static index passed to the constructor // - a merged view of a static and dynamic index (MergedIndex) SymbolIndex *Index; - // If present, an up-to-date of symbols in open files. Read via Index. - std::unique_ptr FileIdx; - /// Callbacks responsible for updating FileIdx. - std::unique_ptr FileIdxUpdater; - // If present, a merged view of FileIdx and an external index. Read via Index. + /// If present, an up-to-date of symbols in open files. Read via Index. + std::unique_ptr DynamicIdx; + // If present, a merged view of DynamicIdx and an external index. Read via + // Index. std::unique_ptr MergedIndex; // If set, this represents the workspace path. llvm::Optional RootPath; diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 8ee5018ed..3d8e17bfb 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -8,8 +8,8 @@ //===----------------------------------------------------------------------===// #include "FileIndex.h" -#include "SymbolCollector.h" #include "../Logger.h" +#include "SymbolCollector.h" #include "clang/Index/IndexingAction.h" #include "clang/Lex/Preprocessor.h" @@ -17,6 +17,7 @@ namespace clang { namespace clangd { SymbolSlab indexAST(ASTContext &AST, std::shared_ptr PP, + llvm::Optional> TopLevelDecls, llvm::ArrayRef URISchemes) { SymbolCollector::Options CollectorOpts; // FIXME(ioeric): we might also want to collect include headers. We would need @@ -38,10 +39,14 @@ SymbolSlab indexAST(ASTContext &AST, std::shared_ptr PP, index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly; IndexOpts.IndexFunctionLocals = false; - std::vector TopLevelDecls( - AST.getTranslationUnitDecl()->decls().begin(), - AST.getTranslationUnitDecl()->decls().end()); - index::indexTopLevelDecls(AST, TopLevelDecls, Collector, IndexOpts); + std::vector DeclsToIndex; + if (TopLevelDecls) + DeclsToIndex.assign(TopLevelDecls->begin(), TopLevelDecls->end()); + else + DeclsToIndex.assign(AST.getTranslationUnitDecl()->decls().begin(), + AST.getTranslationUnitDecl()->decls().end()); + + index::indexTopLevelDecls(AST, DeclsToIndex, Collector, IndexOpts); return Collector.takeSymbols(); } @@ -81,13 +86,14 @@ std::shared_ptr> FileSymbols::allSymbols() { } void FileIndex::update(PathRef Path, ASTContext *AST, - std::shared_ptr PP) { + std::shared_ptr PP, + llvm::Optional> TopLevelDecls) { if (!AST) { FSymbols.update(Path, nullptr); } else { assert(PP); auto Slab = llvm::make_unique(); - *Slab = indexAST(*AST, PP, URISchemes); + *Slab = indexAST(*AST, PP, TopLevelDecls, URISchemes); FSymbols.update(Path, std::move(Slab)); } auto Symbols = FSymbols.allSymbols(); diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index 59bf58c6f..6f24f9d2c 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -64,7 +64,11 @@ class FileIndex : public SymbolIndex { /// nullptr, this removes all symbols in the file. /// If \p AST is not null, \p PP cannot be null and it should be the /// preprocessor that was used to build \p AST. - void update(PathRef Path, ASTContext *AST, std::shared_ptr PP); + /// If \p TopLevelDecls is set, only these decls are indexed. Otherwise, all + /// top level decls obtained from \p AST are indexed. + void + update(PathRef Path, ASTContext *AST, std::shared_ptr PP, + llvm::Optional> TopLevelDecls = llvm::None); bool fuzzyFind(const FuzzyFindRequest &Req, @@ -86,8 +90,12 @@ class FileIndex : public SymbolIndex { /// Retrieves namespace and class level symbols in \p AST. /// Exposed to assist in unit tests. /// If URISchemes is empty, the default schemes in SymbolCollector will be used. -SymbolSlab indexAST(ASTContext &AST, std::shared_ptr PP, - llvm::ArrayRef URISchemes = {}); +/// If \p TopLevelDecls is set, only these decls are indexed. Otherwise, all top +/// level decls obtained from \p AST are indexed. +SymbolSlab +indexAST(ASTContext &AST, std::shared_ptr PP, + llvm::Optional> TopLevelDecls = llvm::None, + llvm::ArrayRef URISchemes = {}); } // namespace clangd } // namespace clang From 0412bdb06ddb1d8b4a6b5f6343cacf8d183bdecb Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Wed, 22 Aug 2018 13:44:15 +0000 Subject: [PATCH 076/686] [clangd] Implement BOOST iterator This patch introduces BOOST iterator - a substantial block for efficient and high-quality symbol retrieval. The concept of boosting allows performing computationally inexpensive scoring on the query side so that the final (expensive) scoring can only be applied on the items with the highest preliminary score while eliminating the need to score too many items. Reviewed by: ilya-biryukov Differential Revision: https://reviews.llvm.org/D50970 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340409 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/DexIndex.cpp | 9 ++- clangd/index/dex/Iterator.cpp | 73 ++++++++++++++++++++-- clangd/index/dex/Iterator.h | 44 +++++++++++-- unittests/clangd/DexIndexTests.cpp | 99 ++++++++++++++++++++++-------- 4 files changed, 185 insertions(+), 40 deletions(-) diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/DexIndex.cpp index 847fa02c3..534f51e0c 100644 --- a/clangd/index/dex/DexIndex.cpp +++ b/clangd/index/dex/DexIndex.cpp @@ -125,11 +125,15 @@ bool DexIndex::fuzzyFind( // using 100x of the requested number might not be good in practice, e.g. // when the requested number of items is small. const unsigned ItemsToRetrieve = 100 * Req.MaxCandidateCount; - std::vector SymbolDocIDs = consume(*QueryIterator, ItemsToRetrieve); + // FIXME(kbobyrev): Add boosting to the query and utilize retrieved + // boosting scores. + std::vector> SymbolDocIDs = + consume(*QueryIterator, ItemsToRetrieve); // Retrieve top Req.MaxCandidateCount items. std::priority_queue> Top; - for (const auto &SymbolDocID : SymbolDocIDs) { + for (const auto &P : SymbolDocIDs) { + const DocID SymbolDocID = P.first; const auto *Sym = (*Symbols)[SymbolDocID]; const llvm::Optional Score = Filter.match(Sym->Name); if (!Score) @@ -161,7 +165,6 @@ void DexIndex::lookup(const LookupRequest &Req, } } - void DexIndex::findOccurrences( const OccurrencesRequest &Req, llvm::function_ref Callback) const { diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index 81f0a205e..e128e1dd6 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -46,6 +46,8 @@ class DocumentIterator : public Iterator { return *Index; } + float consume(DocID ID) override { return DEFAULT_BOOST_SCORE; } + private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << '['; @@ -103,6 +105,19 @@ class AndIterator : public Iterator { DocID peek() const override { return Children.front()->peek(); } + // If not exhausted and points to the given item, consume() returns the + // product of Children->consume(ID). Otherwise, DEFAULT_BOOST_SCORE is + // returned. + float consume(DocID ID) override { + if (reachedEnd() || peek() != ID) + return DEFAULT_BOOST_SCORE; + return std::accumulate( + begin(Children), end(Children), DEFAULT_BOOST_SCORE, + [&](float Current, const std::unique_ptr &Child) { + return Current * Child->consume(ID); + }); + } + private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << "(& "; @@ -209,6 +224,20 @@ class OrIterator : public Iterator { return Result; } + // Returns the maximum boosting score among all Children when iterator is not + // exhausted and points to the given ID, DEFAULT_BOOST_SCORE otherwise. + float consume(DocID ID) override { + if (reachedEnd() || peek() != ID) + return DEFAULT_BOOST_SCORE; + return std::accumulate( + begin(Children), end(Children), DEFAULT_BOOST_SCORE, + [&](float Current, const std::unique_ptr &Child) { + return (!Child->reachedEnd() && Child->peek() == ID) + ? std::max(Current, Child->consume(ID)) + : Current; + }); + } + private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << "(| "; @@ -249,6 +278,8 @@ class TrueIterator : public Iterator { return Index; } + float consume(DocID) override { return DEFAULT_BOOST_SCORE; } + private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << "(TRUE {" << Index << "} out of " << Size << ")"; @@ -260,13 +291,42 @@ class TrueIterator : public Iterator { DocID Size; }; +/// Boost iterator is a wrapper around its child which multiplies scores of +/// each retrieved item by a given factor. +class BoostIterator : public Iterator { +public: + BoostIterator(std::unique_ptr Child, float Factor) + : Child(move(Child)), Factor(Factor) {} + + bool reachedEnd() const override { return Child->reachedEnd(); } + + void advance() override { Child->advance(); } + + void advanceTo(DocID ID) override { Child->advanceTo(ID); } + + DocID peek() const override { return Child->peek(); } + + float consume(DocID ID) override { return Child->consume(ID) * Factor; } + +private: + llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { + OS << "(BOOST " << Factor << ' ' << *Child << ')'; + return OS; + } + + std::unique_ptr Child; + float Factor; +}; + } // end namespace -std::vector consume(Iterator &It, size_t Limit) { - std::vector Result; +std::vector> consume(Iterator &It, size_t Limit) { + std::vector> Result; for (size_t Retrieved = 0; !It.reachedEnd() && Retrieved < Limit; - It.advance(), ++Retrieved) - Result.push_back(It.peek()); + It.advance(), ++Retrieved) { + DocID Document = It.peek(); + Result.push_back(std::make_pair(Document, It.consume(Document))); + } return Result; } @@ -288,6 +348,11 @@ std::unique_ptr createTrue(DocID Size) { return llvm::make_unique(Size); } +std::unique_ptr createBoost(std::unique_ptr Child, + float Factor) { + return llvm::make_unique(move(Child), Factor); +} + } // namespace dex } // namespace clangd } // namespace clang diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index cd36499a5..68e7a6f62 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -66,11 +66,9 @@ using PostingListRef = llvm::ArrayRef; /// (their children) to form a multi-level Query Tree. The interface is designed /// to be extensible in order to support multiple types of iterators. class Iterator { - // FIXME(kbobyrev): Provide callback for matched documents. - // FIXME(kbobyrev): Implement new types of iterators: Label, Boost (with - // scoring), Limit. // FIXME(kbobyrev): Implement iterator cost, an estimate of advance() calls // before iterator exhaustion. + // FIXME(kbobyrev): Implement Limit iterator. public: /// Returns true if all valid DocIDs were processed and hence the iterator is /// exhausted. @@ -89,6 +87,13 @@ class Iterator { /// /// Note: reachedEnd() must be false. virtual DocID peek() const = 0; + /// Retrieves boosting score. Query tree root should pass Root->peek() to this + /// function, the parameter is needed to propagate through the tree. Given ID + /// should be compared against BOOST iterator peek()s: some of the iterators + /// would not point to the item which was propagated to the top of the query + /// tree (e.g. if these iterators are branches of OR iterator) and hence + /// shouldn't apply any boosting to the consumed item. + virtual float consume(DocID ID) = 0; virtual ~Iterator() {} @@ -107,32 +112,59 @@ class Iterator { return Iterator.dump(OS); } + constexpr static float DEFAULT_BOOST_SCORE = 1; + private: virtual llvm::raw_ostream &dump(llvm::raw_ostream &OS) const = 0; }; /// Advances the iterator until it is either exhausted or the number of -/// requested items is reached. The result contains sorted DocumentIDs. -std::vector consume(Iterator &It, - size_t Limit = std::numeric_limits::max()); +/// requested items is reached. Returns pairs of document IDs with the +/// corresponding boosting score. +/// +/// Boosting can be seen as a compromise between retrieving too many items and +/// calculating finals score for each of them (which might be very expensive) +/// and not retrieving enough items so that items with very high final score +/// would not be processed. Boosting score is a computationally efficient way +/// to acquire preliminary scores of requested items. +std::vector> +consume(Iterator &It, size_t Limit = std::numeric_limits::max()); /// Returns a document iterator over given PostingList. +/// +/// DocumentIterator returns DEFAULT_BOOST_SCORE for each processed item. std::unique_ptr create(PostingListRef Documents); /// Returns AND Iterator which performs the intersection of the PostingLists of /// its children. +/// +/// consume(): AND Iterator returns the product of Childrens' boosting scores +/// when not exhausted and DEFAULT_BOOST_SCORE otherwise. std::unique_ptr createAnd(std::vector> Children); /// Returns OR Iterator which performs the union of the PostingLists of its /// children. +/// +/// consume(): OR Iterator returns the highest boost value among children +/// pointing to requested item when not exhausted and DEFAULT_BOOST_SCORE +/// otherwise. std::unique_ptr createOr(std::vector> Children); /// Returns TRUE Iterator which iterates over "virtual" PostingList containing /// all items in range [0, Size) in an efficient manner. +/// +/// TRUE returns DEFAULT_BOOST_SCORE for each processed item. std::unique_ptr createTrue(DocID Size); +/// Returns BOOST iterator which multiplies the score of each item by given +/// factor. Boosting can be used as a computationally inexpensive filtering. +/// Users can return significantly more items using consumeAndBoost() and then +/// trim Top K using retrieval score. +std::unique_ptr createBoost(std::unique_ptr Child, + float Factor); + /// This allows createAnd(create(...), create(...)) syntax. template std::unique_ptr createAnd(Args... args) { std::vector> Children; diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index e2cdc3438..ca20e5a8b 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -29,6 +29,15 @@ namespace clangd { namespace dex { namespace { +std::vector +consumeIDs(Iterator &It, size_t Limit = std::numeric_limits::max()) { + auto IDAndScore = consume(It, Limit); + std::vector IDs(IDAndScore.size()); + for (size_t I = 0; I < IDAndScore.size(); ++I) + IDs[I] = IDAndScore[I].first; + return IDs; +} + TEST(DexIndexIterators, DocumentIterator) { const PostingList L = {4, 7, 8, 20, 42, 100}; auto DocIterator = create(L); @@ -62,7 +71,7 @@ TEST(DexIndexIterators, AndWithEmpty) { auto AndWithEmpty = createAnd(create(L0), create(L1)); EXPECT_TRUE(AndWithEmpty->reachedEnd()); - EXPECT_THAT(consume(*AndWithEmpty), ElementsAre()); + EXPECT_THAT(consumeIDs(*AndWithEmpty), ElementsAre()); } TEST(DexIndexIterators, AndTwoLists) { @@ -72,7 +81,7 @@ TEST(DexIndexIterators, AndTwoLists) { auto And = createAnd(create(L1), create(L0)); EXPECT_FALSE(And->reachedEnd()); - EXPECT_THAT(consume(*And), ElementsAre(0U, 7U, 10U, 320U, 9000U)); + EXPECT_THAT(consumeIDs(*And), ElementsAre(0U, 7U, 10U, 320U, 9000U)); And = createAnd(create(L0), create(L1)); @@ -113,7 +122,7 @@ TEST(DexIndexIterators, OrWithEmpty) { auto OrWithEmpty = createOr(create(L0), create(L1)); EXPECT_FALSE(OrWithEmpty->reachedEnd()); - EXPECT_THAT(consume(*OrWithEmpty), + EXPECT_THAT(consumeIDs(*OrWithEmpty), ElementsAre(0U, 5U, 7U, 10U, 42U, 320U, 9000U)); } @@ -146,7 +155,7 @@ TEST(DexIndexIterators, OrTwoLists) { Or = createOr(create(L0), create(L1)); - EXPECT_THAT(consume(*Or), + EXPECT_THAT(consumeIDs(*Or), ElementsAre(0U, 4U, 5U, 7U, 10U, 30U, 42U, 60U, 320U, 9000U)); } @@ -178,41 +187,45 @@ TEST(DexIndexIterators, OrThreeLists) { // FIXME(kbobyrev): The testcase below is similar to what is expected in real // queries. It should be updated once new iterators (such as boosting, limiting, // etc iterators) appear. However, it is not exhaustive and it would be -// beneficial to implement automatic generation of query trees for more -// comprehensive testing. +// beneficial to implement automatic generation (e.g. fuzzing) of query trees +// for more comprehensive testing. TEST(DexIndexIterators, QueryTree) { - // An example of more complicated query // // +-----------------+ // |And Iterator:1, 5| // +--------+--------+ // | // | - // +------------------------------------+ + // +-------------+----------------------+ // | | // | | - // +----------v----------+ +----------v---------+ - // |And Iterator: 1, 5, 9| |Or Iterator: 0, 1, 5| - // +----------+----------+ +----------+---------+ + // +----------v----------+ +----------v------------+ + // |And Iterator: 1, 5, 9| |Or Iterator: 0, 1, 3, 5| + // +----------+----------+ +----------+------------+ // | | - // +------+-----+ +---------+-----------+ + // +------+-----+ +---------------------+ // | | | | | - // +-------v-----+ +----v-----+ +--v--+ +-V--+ +---v---+ - // |1, 3, 5, 8, 9| |1, 5, 7, 9| |Empty| |0, 5| |0, 1, 5| - // +-------------+ +----------+ +-----+ +----+ +-------+ - + // +-------v-----+ +----+---+ +--v--+ +---v----+ +----v---+ + // |1, 3, 5, 8, 9| |Boost: 2| |Empty| |Boost: 3| |Boost: 4| + // +-------------+ +----+---+ +-----+ +---+----+ +----+---+ + // | | | + // +----v-----+ +-v--+ +---v---+ + // |1, 5, 7, 9| |1, 5| |0, 3, 5| + // +----------+ +----+ +-------+ + // const PostingList L0 = {1, 3, 5, 8, 9}; const PostingList L1 = {1, 5, 7, 9}; - const PostingList L2 = {0, 5}; - const PostingList L3 = {0, 1, 5}; - const PostingList L4; + const PostingList L3; + const PostingList L4 = {1, 5}; + const PostingList L5 = {0, 3, 5}; // Root of the query tree: [1, 5] auto Root = createAnd( // Lower And Iterator: [1, 5, 9] - createAnd(create(L0), create(L1)), + createAnd(create(L0), createBoost(create(L1), 2U)), // Lower Or Iterator: [0, 1, 5] - createOr(create(L2), create(L3), create(L4))); + createOr(create(L3), createBoost(create(L4), 3U), + createBoost(create(L5), 4U))); EXPECT_FALSE(Root->reachedEnd()); EXPECT_EQ(Root->peek(), 1U); @@ -221,10 +234,14 @@ TEST(DexIndexIterators, QueryTree) { Root->advanceTo(1); Root->advanceTo(0); EXPECT_EQ(Root->peek(), 1U); + auto ElementBoost = Root->consume(Root->peek()); + EXPECT_THAT(ElementBoost, 6); Root->advance(); EXPECT_EQ(Root->peek(), 5U); Root->advanceTo(5); EXPECT_EQ(Root->peek(), 5U); + ElementBoost = Root->consume(Root->peek()); + EXPECT_THAT(ElementBoost, 8); Root->advanceTo(9000); EXPECT_TRUE(Root->reachedEnd()); } @@ -256,29 +273,57 @@ TEST(DexIndexIterators, Limit) { const PostingList L5; auto DocIterator = create(L0); - EXPECT_THAT(consume(*DocIterator, 42), ElementsAre(4, 7, 8, 20, 42, 100)); + EXPECT_THAT(consumeIDs(*DocIterator, 42), ElementsAre(4, 7, 8, 20, 42, 100)); DocIterator = create(L0); - EXPECT_THAT(consume(*DocIterator), ElementsAre(4, 7, 8, 20, 42, 100)); + EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(4, 7, 8, 20, 42, 100)); DocIterator = create(L0); - EXPECT_THAT(consume(*DocIterator, 3), ElementsAre(4, 7, 8)); + EXPECT_THAT(consumeIDs(*DocIterator, 3), ElementsAre(4, 7, 8)); DocIterator = create(L0); - EXPECT_THAT(consume(*DocIterator, 0), ElementsAre()); + EXPECT_THAT(consumeIDs(*DocIterator, 0), ElementsAre()); } TEST(DexIndexIterators, True) { auto TrueIterator = createTrue(0U); EXPECT_TRUE(TrueIterator->reachedEnd()); - EXPECT_THAT(consume(*TrueIterator), ElementsAre()); + EXPECT_THAT(consumeIDs(*TrueIterator), ElementsAre()); PostingList L0 = {1, 2, 5, 7}; TrueIterator = createTrue(7U); EXPECT_THAT(TrueIterator->peek(), 0); auto AndIterator = createAnd(create(L0), move(TrueIterator)); EXPECT_FALSE(AndIterator->reachedEnd()); - EXPECT_THAT(consume(*AndIterator), ElementsAre(1, 2, 5)); + EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(1, 2, 5)); +} + +TEST(DexIndexIterators, Boost) { + auto BoostIterator = createBoost(createTrue(5U), 42U); + EXPECT_FALSE(BoostIterator->reachedEnd()); + auto ElementBoost = BoostIterator->consume(BoostIterator->peek()); + EXPECT_THAT(ElementBoost, 42U); + + const PostingList L0 = {2, 4}; + const PostingList L1 = {1, 4}; + auto Root = createOr(createTrue(5U), createBoost(create(L0), 2U), + createBoost(create(L1), 3U)); + + ElementBoost = Root->consume(Root->peek()); + EXPECT_THAT(ElementBoost, Iterator::DEFAULT_BOOST_SCORE); + Root->advance(); + EXPECT_THAT(Root->peek(), 1U); + ElementBoost = Root->consume(Root->peek()); + EXPECT_THAT(ElementBoost, 3); + + Root->advance(); + EXPECT_THAT(Root->peek(), 2U); + ElementBoost = Root->consume(Root->peek()); + EXPECT_THAT(ElementBoost, 2); + + Root->advanceTo(4); + ElementBoost = Root->consume(Root->peek()); + EXPECT_THAT(ElementBoost, 3); } testing::Matcher> From 13c11ff14f4a24bab6079e33eb5dc5c7c261c971 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Wed, 22 Aug 2018 13:51:19 +0000 Subject: [PATCH 077/686] [clangd] Get rid of regexes in CanonicalIncludes Summary: Replace them with suffix mappings. Reviewers: ioeric, kbobyrev Reviewed By: ioeric Subscribers: MaskRay, jkorous, arphaman, jfb, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51088 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340410 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/CanonicalIncludes.cpp | 1342 ++++++++++++++-------------- clangd/index/CanonicalIncludes.h | 24 +- 2 files changed, 687 insertions(+), 679 deletions(-) diff --git a/clangd/index/CanonicalIncludes.cpp b/clangd/index/CanonicalIncludes.cpp index a3493e316..4172b94f8 100644 --- a/clangd/index/CanonicalIncludes.cpp +++ b/clangd/index/CanonicalIncludes.cpp @@ -10,7 +10,8 @@ #include "CanonicalIncludes.h" #include "../Headers.h" #include "clang/Driver/Types.h" -#include "llvm/Support/Regex.h" +#include "llvm/Support/Path.h" +#include namespace clang { namespace clangd { @@ -18,15 +19,17 @@ namespace { const char IWYUPragma[] = "// IWYU pragma: private, include "; } // namespace -void CanonicalIncludes::addMapping(llvm::StringRef Path, - llvm::StringRef CanonicalPath) { - addRegexMapping((llvm::Twine("^") + llvm::Regex::escape(Path) + "$").str(), - CanonicalPath); +void CanonicalIncludes::addPathSuffixMapping(llvm::StringRef Suffix, + llvm::StringRef CanonicalPath) { + int Components = std::distance(llvm::sys::path::begin(Suffix), + llvm::sys::path::end(Suffix)); + MaxSuffixComponents = std::max(MaxSuffixComponents, Components); + SuffixHeaderMapping[Suffix] = CanonicalPath; } -void CanonicalIncludes::addRegexMapping(llvm::StringRef RE, - llvm::StringRef CanonicalPath) { - this->RegexHeaderMappingTable.emplace_back(llvm::Regex(RE), CanonicalPath); +void CanonicalIncludes::addMapping(llvm::StringRef Path, + llvm::StringRef CanonicalPath) { + FullPathMapping[Path] = CanonicalPath; } void CanonicalIncludes::addSymbolMapping(llvm::StringRef QualifiedName, @@ -41,7 +44,6 @@ CanonicalIncludes::mapHeader(llvm::ArrayRef Headers, auto SE = SymbolMapping.find(QualifiedName); if (SE != SymbolMapping.end()) return SE->second; - std::lock_guard Lock(RegexMutex); // Find the first header such that the extension is not '.inc', and isn't a // recognized non-header file auto I = @@ -62,13 +64,19 @@ CanonicalIncludes::mapHeader(llvm::ArrayRef Headers, if ((ExtType != driver::types::TY_INVALID) && !driver::types::onlyPrecompileType(ExtType)) return Headers[0]; - for (auto &Entry : RegexHeaderMappingTable) { -#ifndef NDEBUG - std::string Dummy; - assert(Entry.first.isValid(Dummy) && "Regex should never be invalid!"); -#endif - if (Entry.first.match(Header)) - return Entry.second; + + auto MapIt = FullPathMapping.find(Header); + if (MapIt != FullPathMapping.end()) + return MapIt->second; + + int Components = 1; + for (auto It = llvm::sys::path::rbegin(Header), + End = llvm::sys::path::rend(Header); + It != End && Components <= MaxSuffixComponents; ++It, ++Components) { + auto SubPath = Header.substr(It->data() - Header.begin()); + auto MappingIt = SuffixHeaderMapping.find(SubPath); + if (MappingIt != SuffixHeaderMapping.end()) + return MappingIt->second; } return Header; } @@ -153,660 +161,660 @@ void addSystemHeadersMapping(CanonicalIncludes *Includes) { static const std::vector> SystemHeaderMap = { - {"include/__stddef_max_align_t.h$", ""}, - {"include/__wmmintrin_aes.h$", ""}, - {"include/__wmmintrin_pclmul.h$", ""}, - {"include/adxintrin.h$", ""}, - {"include/ammintrin.h$", ""}, - {"include/avx2intrin.h$", ""}, - {"include/avx512bwintrin.h$", ""}, - {"include/avx512cdintrin.h$", ""}, - {"include/avx512dqintrin.h$", ""}, - {"include/avx512erintrin.h$", ""}, - {"include/avx512fintrin.h$", ""}, - {"include/avx512ifmaintrin.h$", ""}, - {"include/avx512ifmavlintrin.h$", ""}, - {"include/avx512pfintrin.h$", ""}, - {"include/avx512vbmiintrin.h$", ""}, - {"include/avx512vbmivlintrin.h$", ""}, - {"include/avx512vlbwintrin.h$", ""}, - {"include/avx512vlcdintrin.h$", ""}, - {"include/avx512vldqintrin.h$", ""}, - {"include/avx512vlintrin.h$", ""}, - {"include/avxintrin.h$", ""}, - {"include/bmi2intrin.h$", ""}, - {"include/bmiintrin.h$", ""}, - {"include/emmintrin.h$", ""}, - {"include/f16cintrin.h$", ""}, - {"include/float.h$", ""}, - {"include/fma4intrin.h$", ""}, - {"include/fmaintrin.h$", ""}, - {"include/fxsrintrin.h$", ""}, - {"include/ia32intrin.h$", ""}, - {"include/immintrin.h$", ""}, - {"include/inttypes.h$", ""}, - {"include/limits.h$", ""}, - {"include/lzcntintrin.h$", ""}, - {"include/mm3dnow.h$", ""}, - {"include/mm_malloc.h$", ""}, - {"include/mmintrin.h$", ""}, - {"include/mwaitxintrin.h$", ""}, - {"include/pkuintrin.h$", ""}, - {"include/pmmintrin.h$", ""}, - {"include/popcntintrin.h$", ""}, - {"include/prfchwintrin.h$", ""}, - {"include/rdseedintrin.h$", ""}, - {"include/rtmintrin.h$", ""}, - {"include/shaintrin.h$", ""}, - {"include/smmintrin.h$", ""}, - {"include/stdalign.h$", ""}, - {"include/stdarg.h$", ""}, - {"include/stdbool.h$", ""}, - {"include/stddef.h$", ""}, - {"include/stdint.h$", ""}, - {"include/tbmintrin.h$", ""}, - {"include/tmmintrin.h$", ""}, - {"include/wmmintrin.h$", ""}, - {"include/x86intrin.h$", ""}, - {"include/xmmintrin.h$", ""}, - {"include/xopintrin.h$", ""}, - {"include/xsavecintrin.h$", ""}, - {"include/xsaveintrin.h$", ""}, - {"include/xsaveoptintrin.h$", ""}, - {"include/xsavesintrin.h$", ""}, - {"include/xtestintrin.h$", ""}, - {"include/_G_config.h$", ""}, - {"include/assert.h$", ""}, - {"algorithm$", ""}, - {"valarray$", ""}, - {"array$", ""}, - {"atomic$", ""}, - {"backward/auto_ptr.h$", ""}, - {"backward/binders.h$", ""}, - {"bits/algorithmfwd.h$", ""}, - {"bits/alloc_traits.h$", ""}, - {"bits/allocated_ptr.h$", ""}, - {"bits/allocator.h$", ""}, - {"bits/atomic_base.h$", ""}, - {"bits/atomic_lockfree_defines.h$", ""}, - {"bits/atomic_futex.h$", ""}, - {"bits/basic_ios.h$", ""}, - {"bits/basic_ios.tcc$", ""}, - {"bits/basic_string.h$", ""}, - {"bits/basic_string.tcc$", ""}, - {"bits/char_traits.h$", ""}, - {"bits/codecvt.h$", ""}, - {"bits/concept_check.h$", ""}, - {"bits/cpp_type_traits.h$", ""}, - {"bits/cxxabi_forced.h$", ""}, - {"bits/deque.tcc$", ""}, - {"bits/exception.h$", ""}, - {"bits/exception_defines.h$", ""}, - {"bits/exception_ptr.h$", ""}, - {"bits/forward_list.h$", ""}, - {"bits/forward_list.tcc$", ""}, - {"bits/fstream.tcc$", ""}, - {"bits/functexcept.h$", ""}, - {"bits/functional_hash.h$", ""}, - {"bits/gslice.h$", ""}, - {"bits/gslice_array.h$", ""}, - {"bits/hash_bytes.h$", ""}, - {"bits/hashtable.h$", ""}, - {"bits/hashtable_policy.h$", ""}, - {"bits/indirect_array.h$", ""}, - {"bits/invoke.h$", ""}, - {"bits/ios_base.h$", ""}, - {"bits/istream.tcc$", ""}, - {"bits/list.tcc$", ""}, - {"bits/locale_classes.h$", ""}, - {"bits/locale_classes.tcc$", ""}, - {"bits/locale_conv.h$", ""}, - {"bits/locale_facets.h$", ""}, - {"bits/locale_facets.tcc$", ""}, - {"bits/locale_facets_nonio.h$", ""}, - {"bits/locale_facets_nonio.tcc$", ""}, - {"bits/localefwd.h$", ""}, - {"bits/mask_array.h$", ""}, - {"bits/memoryfwd.h$", ""}, - {"bits/move.h$", ""}, - {"bits/nested_exception.h$", ""}, - {"bits/ostream.tcc$", ""}, - {"bits/ostream_insert.h$", ""}, - {"bits/parse_numbers.h$", ""}, - {"bits/postypes.h$", ""}, - {"bits/predefined_ops.h$", ""}, - {"bits/ptr_traits.h$", ""}, - {"bits/quoted_string.h$", ""}, - {"bits/random.h$", ""}, - {"bits/random.tcc$", ""}, - {"bits/range_access.h$", ""}, - {"bits/refwrap.h$", ""}, - {"bits/regex.h$", ""}, - {"bits/regex_automaton.h$", ""}, - {"bits/regex_compiler.h$", ""}, - {"bits/regex_constants.h$", ""}, - {"bits/regex_cursor.h$", ""}, - {"bits/regex_error.h$", ""}, - {"bits/regex_executor.h$", ""}, - {"bits/regex_grep_matcher.h$", ""}, - {"bits/regex_grep_matcher.tcc$", ""}, - {"bits/regex_nfa.h$", ""}, - {"bits/regex_scanner.h$", ""}, - {"bits/shared_ptr.h$", ""}, - {"bits/shared_ptr_base.h$", ""}, - {"bits/shared_ptr_atomic.h$", ""}, - {"bits/slice_array.h$", ""}, - {"bits/sstream.tcc$", ""}, - {"bits/std_abs.h$", ""}, - {"bits/std_function.h$", ""}, - {"bits/std_mutex.h$", ""}, - {"bits/stl_algo.h$", ""}, - {"bits/stl_algobase.h$", ""}, - {"bits/stl_bvector.h$", ""}, - {"bits/stl_construct.h$", ""}, - {"bits/stl_deque.h$", ""}, - {"bits/stl_function.h$", ""}, - {"bits/stl_heap.h$", ""}, - {"bits/stl_iterator.h$", ""}, - {"bits/stl_iterator_base_funcs.h$", ""}, - {"bits/stl_iterator_base_types.h$", ""}, - {"bits/stl_list.h$", ""}, - {"bits/stl_map.h$", ""}, - {"bits/stl_multimap.h$", ""}, - {"bits/stl_multiset.h$", ""}, - {"bits/stl_numeric.h$", ""}, - {"bits/stl_pair.h$", ""}, - {"bits/stl_queue.h$", ""}, - {"bits/stl_raw_storage_iter.h$", ""}, - {"bits/stl_relops.h$", ""}, - {"bits/stl_set.h$", ""}, - {"bits/stl_stack.h$", ""}, - {"bits/stl_tempbuf.h$", ""}, - {"bits/stl_tree.h$", ""}, - {"bits/stl_uninitialized.h$", ""}, - {"bits/stl_vector.h$", ""}, - {"bits/stream_iterator.h$", ""}, - {"bits/streambuf.tcc$", ""}, - {"bits/streambuf_iterator.h$", ""}, - {"bits/stringfwd.h$", ""}, - {"bits/uniform_int_dist.h$", ""}, - {"bits/unique_ptr.h$", ""}, - {"bits/unordered_map.h$", ""}, - {"bits/unordered_set.h$", ""}, - {"bits/uses_allocator.h$", ""}, - {"bits/valarray_after.h$", ""}, - {"bits/valarray_array.h$", ""}, - {"bits/valarray_array.tcc$", ""}, - {"bits/valarray_before.h$", ""}, - {"bits/vector.tcc$", ""}, - {"bitset$", ""}, - {"ccomplex$", ""}, - {"cctype$", ""}, - {"cerrno$", ""}, - {"cfenv$", ""}, - {"cfloat$", ""}, - {"chrono$", ""}, - {"cinttypes$", ""}, - {"climits$", ""}, - {"clocale$", ""}, - {"cmath$", ""}, - {"complex$", ""}, - {"complex.h$", ""}, - {"condition_variable$", ""}, - {"csetjmp$", ""}, - {"csignal$", ""}, - {"cstdalign$", ""}, - {"cstdarg$", ""}, - {"cstdbool$", ""}, - {"cstdint$", ""}, - {"cstdio$", ""}, - {"cstdlib$", ""}, - {"cstring$", ""}, - {"ctgmath$", ""}, - {"ctime$", ""}, - {"cwchar$", ""}, - {"cwctype$", ""}, - {"cxxabi.h$", ""}, - {"debug/debug.h$", ""}, - {"debug/map.h$", ""}, - {"debug/multimap.h$", ""}, - {"debug/multiset.h$", ""}, - {"debug/set.h$", ""}, - {"deque$", ""}, - {"exception$", ""}, - {"ext/alloc_traits.h$", ""}, - {"ext/atomicity.h$", ""}, - {"ext/concurrence.h$", ""}, - {"ext/new_allocator.h$", ""}, - {"ext/numeric_traits.h$", ""}, - {"ext/string_conversions.h$", ""}, - {"ext/type_traits.h$", ""}, - {"fenv.h$", ""}, - {"forward_list$", ""}, - {"fstream$", ""}, - {"functional$", ""}, - {"future$", ""}, - {"initializer_list$", ""}, - {"iomanip$", ""}, - {"ios$", ""}, - {"iosfwd$", ""}, - {"iostream$", ""}, - {"istream$", ""}, - {"iterator$", ""}, - {"limits$", ""}, - {"list$", ""}, - {"locale$", ""}, - {"map$", ""}, - {"memory$", ""}, - {"shared_mutex$", ""}, - {"mutex$", ""}, - {"new$", ""}, - {"numeric$", ""}, - {"ostream$", ""}, - {"queue$", ""}, - {"random$", ""}, - {"ratio$", ""}, - {"regex$", ""}, - {"scoped_allocator$", ""}, - {"set$", ""}, - {"sstream$", ""}, - {"stack$", ""}, - {"stdexcept$", ""}, - {"streambuf$", ""}, - {"string$", ""}, - {"system_error$", ""}, - {"tgmath.h$", ""}, - {"thread$", ""}, - {"tuple$", ""}, - {"type_traits$", ""}, - {"typeindex$", ""}, - {"typeinfo$", ""}, - {"unordered_map$", ""}, - {"unordered_set$", ""}, - {"utility$", ""}, - {"valarray$", ""}, - {"vector$", ""}, - {"include/complex.h$", ""}, - {"include/ctype.h$", ""}, - {"include/errno.h$", ""}, - {"include/fenv.h$", ""}, - {"include/inttypes.h$", ""}, - {"include/libio.h$", ""}, - {"include/limits.h$", ""}, - {"include/locale.h$", ""}, - {"include/math.h$", ""}, - {"include/setjmp.h$", ""}, - {"include/signal.h$", ""}, - {"include/stdint.h$", ""}, - {"include/stdio.h$", ""}, - {"include/stdlib.h$", ""}, - {"include/string.h$", ""}, - {"include/time.h$", ""}, - {"include/wchar.h$", ""}, - {"include/wctype.h$", ""}, - {"bits/cmathcalls.h$", ""}, - {"bits/errno.h$", ""}, - {"bits/fenv.h$", ""}, - {"bits/huge_val.h$", ""}, - {"bits/huge_valf.h$", ""}, - {"bits/huge_vall.h$", ""}, - {"bits/inf.h$", ""}, - {"bits/local_lim.h$", ""}, - {"bits/locale.h$", ""}, - {"bits/mathcalls.h$", ""}, - {"bits/mathdef.h$", ""}, - {"bits/nan.h$", ""}, - {"bits/posix1_lim.h$", ""}, - {"bits/posix2_lim.h$", ""}, - {"bits/setjmp.h$", ""}, - {"bits/sigaction.h$", ""}, - {"bits/sigcontext.h$", ""}, - {"bits/siginfo.h$", ""}, - {"bits/signum.h$", ""}, - {"bits/sigset.h$", ""}, - {"bits/sigstack.h$", ""}, - {"bits/stdio_lim.h$", ""}, - {"bits/sys_errlist.h$", ""}, - {"bits/time.h$", ""}, - {"bits/timex.h$", ""}, - {"bits/typesizes.h$", ""}, - {"bits/wchar.h$", ""}, - {"bits/wordsize.h$", ""}, - {"bits/xopen_lim.h$", ""}, - {"include/xlocale.h$", ""}, - {"bits/atomic_word.h$", ""}, - {"bits/basic_file.h$", ""}, - {"bits/c\\+\\+allocator.h$", ""}, - {"bits/c\\+\\+config.h$", ""}, - {"bits/c\\+\\+io.h$", ""}, - {"bits/c\\+\\+locale.h$", ""}, - {"bits/cpu_defines.h$", ""}, - {"bits/ctype_base.h$", ""}, - {"bits/cxxabi_tweaks.h$", ""}, - {"bits/error_constants.h$", ""}, - {"bits/gthr-default.h$", ""}, - {"bits/gthr.h$", ""}, - {"bits/opt_random.h$", ""}, - {"bits/os_defines.h$", ""}, + {"include/__stddef_max_align_t.h", ""}, + {"include/__wmmintrin_aes.h", ""}, + {"include/__wmmintrin_pclmul.h", ""}, + {"include/adxintrin.h", ""}, + {"include/ammintrin.h", ""}, + {"include/avx2intrin.h", ""}, + {"include/avx512bwintrin.h", ""}, + {"include/avx512cdintrin.h", ""}, + {"include/avx512dqintrin.h", ""}, + {"include/avx512erintrin.h", ""}, + {"include/avx512fintrin.h", ""}, + {"include/avx512ifmaintrin.h", ""}, + {"include/avx512ifmavlintrin.h", ""}, + {"include/avx512pfintrin.h", ""}, + {"include/avx512vbmiintrin.h", ""}, + {"include/avx512vbmivlintrin.h", ""}, + {"include/avx512vlbwintrin.h", ""}, + {"include/avx512vlcdintrin.h", ""}, + {"include/avx512vldqintrin.h", ""}, + {"include/avx512vlintrin.h", ""}, + {"include/avxintrin.h", ""}, + {"include/bmi2intrin.h", ""}, + {"include/bmiintrin.h", ""}, + {"include/emmintrin.h", ""}, + {"include/f16cintrin.h", ""}, + {"include/float.h", ""}, + {"include/fma4intrin.h", ""}, + {"include/fmaintrin.h", ""}, + {"include/fxsrintrin.h", ""}, + {"include/ia32intrin.h", ""}, + {"include/immintrin.h", ""}, + {"include/inttypes.h", ""}, + {"include/limits.h", ""}, + {"include/lzcntintrin.h", ""}, + {"include/mm3dnow.h", ""}, + {"include/mm_malloc.h", ""}, + {"include/mmintrin.h", ""}, + {"include/mwaitxintrin.h", ""}, + {"include/pkuintrin.h", ""}, + {"include/pmmintrin.h", ""}, + {"include/popcntintrin.h", ""}, + {"include/prfchwintrin.h", ""}, + {"include/rdseedintrin.h", ""}, + {"include/rtmintrin.h", ""}, + {"include/shaintrin.h", ""}, + {"include/smmintrin.h", ""}, + {"include/stdalign.h", ""}, + {"include/stdarg.h", ""}, + {"include/stdbool.h", ""}, + {"include/stddef.h", ""}, + {"include/stdint.h", ""}, + {"include/tbmintrin.h", ""}, + {"include/tmmintrin.h", ""}, + {"include/wmmintrin.h", ""}, + {"include/x86intrin.h", ""}, + {"include/xmmintrin.h", ""}, + {"include/xopintrin.h", ""}, + {"include/xsavecintrin.h", ""}, + {"include/xsaveintrin.h", ""}, + {"include/xsaveoptintrin.h", ""}, + {"include/xsavesintrin.h", ""}, + {"include/xtestintrin.h", ""}, + {"include/_G_config.h", ""}, + {"include/assert.h", ""}, + {"algorithm", ""}, + {"valarray", ""}, + {"array", ""}, + {"atomic", ""}, + {"backward/auto_ptr.h", ""}, + {"backward/binders.h", ""}, + {"bits/algorithmfwd.h", ""}, + {"bits/alloc_traits.h", ""}, + {"bits/allocated_ptr.h", ""}, + {"bits/allocator.h", ""}, + {"bits/atomic_base.h", ""}, + {"bits/atomic_lockfree_defines.h", ""}, + {"bits/atomic_futex.h", ""}, + {"bits/basic_ios.h", ""}, + {"bits/basic_ios.tcc", ""}, + {"bits/basic_string.h", ""}, + {"bits/basic_string.tcc", ""}, + {"bits/char_traits.h", ""}, + {"bits/codecvt.h", ""}, + {"bits/concept_check.h", ""}, + {"bits/cpp_type_traits.h", ""}, + {"bits/cxxabi_forced.h", ""}, + {"bits/deque.tcc", ""}, + {"bits/exception.h", ""}, + {"bits/exception_defines.h", ""}, + {"bits/exception_ptr.h", ""}, + {"bits/forward_list.h", ""}, + {"bits/forward_list.tcc", ""}, + {"bits/fstream.tcc", ""}, + {"bits/functexcept.h", ""}, + {"bits/functional_hash.h", ""}, + {"bits/gslice.h", ""}, + {"bits/gslice_array.h", ""}, + {"bits/hash_bytes.h", ""}, + {"bits/hashtable.h", ""}, + {"bits/hashtable_policy.h", ""}, + {"bits/indirect_array.h", ""}, + {"bits/invoke.h", ""}, + {"bits/ios_base.h", ""}, + {"bits/istream.tcc", ""}, + {"bits/list.tcc", ""}, + {"bits/locale_classes.h", ""}, + {"bits/locale_classes.tcc", ""}, + {"bits/locale_conv.h", ""}, + {"bits/locale_facets.h", ""}, + {"bits/locale_facets.tcc", ""}, + {"bits/locale_facets_nonio.h", ""}, + {"bits/locale_facets_nonio.tcc", ""}, + {"bits/localefwd.h", ""}, + {"bits/mask_array.h", ""}, + {"bits/memoryfwd.h", ""}, + {"bits/move.h", ""}, + {"bits/nested_exception.h", ""}, + {"bits/ostream.tcc", ""}, + {"bits/ostream_insert.h", ""}, + {"bits/parse_numbers.h", ""}, + {"bits/postypes.h", ""}, + {"bits/predefined_ops.h", ""}, + {"bits/ptr_traits.h", ""}, + {"bits/quoted_string.h", ""}, + {"bits/random.h", ""}, + {"bits/random.tcc", ""}, + {"bits/range_access.h", ""}, + {"bits/refwrap.h", ""}, + {"bits/regex.h", ""}, + {"bits/regex_automaton.h", ""}, + {"bits/regex_compiler.h", ""}, + {"bits/regex_constants.h", ""}, + {"bits/regex_cursor.h", ""}, + {"bits/regex_error.h", ""}, + {"bits/regex_executor.h", ""}, + {"bits/regex_grep_matcher.h", ""}, + {"bits/regex_grep_matcher.tcc", ""}, + {"bits/regex_nfa.h", ""}, + {"bits/regex_scanner.h", ""}, + {"bits/shared_ptr.h", ""}, + {"bits/shared_ptr_base.h", ""}, + {"bits/shared_ptr_atomic.h", ""}, + {"bits/slice_array.h", ""}, + {"bits/sstream.tcc", ""}, + {"bits/std_abs.h", ""}, + {"bits/std_function.h", ""}, + {"bits/std_mutex.h", ""}, + {"bits/stl_algo.h", ""}, + {"bits/stl_algobase.h", ""}, + {"bits/stl_bvector.h", ""}, + {"bits/stl_construct.h", ""}, + {"bits/stl_deque.h", ""}, + {"bits/stl_function.h", ""}, + {"bits/stl_heap.h", ""}, + {"bits/stl_iterator.h", ""}, + {"bits/stl_iterator_base_funcs.h", ""}, + {"bits/stl_iterator_base_types.h", ""}, + {"bits/stl_list.h", ""}, + {"bits/stl_map.h", ""}, + {"bits/stl_multimap.h", ""}, + {"bits/stl_multiset.h", ""}, + {"bits/stl_numeric.h", ""}, + {"bits/stl_pair.h", ""}, + {"bits/stl_queue.h", ""}, + {"bits/stl_raw_storage_iter.h", ""}, + {"bits/stl_relops.h", ""}, + {"bits/stl_set.h", ""}, + {"bits/stl_stack.h", ""}, + {"bits/stl_tempbuf.h", ""}, + {"bits/stl_tree.h", ""}, + {"bits/stl_uninitialized.h", ""}, + {"bits/stl_vector.h", ""}, + {"bits/stream_iterator.h", ""}, + {"bits/streambuf.tcc", ""}, + {"bits/streambuf_iterator.h", ""}, + {"bits/stringfwd.h", ""}, + {"bits/uniform_int_dist.h", ""}, + {"bits/unique_ptr.h", ""}, + {"bits/unordered_map.h", ""}, + {"bits/unordered_set.h", ""}, + {"bits/uses_allocator.h", ""}, + {"bits/valarray_after.h", ""}, + {"bits/valarray_array.h", ""}, + {"bits/valarray_array.tcc", ""}, + {"bits/valarray_before.h", ""}, + {"bits/vector.tcc", ""}, + {"bitset", ""}, + {"ccomplex", ""}, + {"cctype", ""}, + {"cerrno", ""}, + {"cfenv", ""}, + {"cfloat", ""}, + {"chrono", ""}, + {"cinttypes", ""}, + {"climits", ""}, + {"clocale", ""}, + {"cmath", ""}, + {"complex", ""}, + {"complex.h", ""}, + {"condition_variable", ""}, + {"csetjmp", ""}, + {"csignal", ""}, + {"cstdalign", ""}, + {"cstdarg", ""}, + {"cstdbool", ""}, + {"cstdint", ""}, + {"cstdio", ""}, + {"cstdlib", ""}, + {"cstring", ""}, + {"ctgmath", ""}, + {"ctime", ""}, + {"cwchar", ""}, + {"cwctype", ""}, + {"cxxabi.h", ""}, + {"debug/debug.h", ""}, + {"debug/map.h", ""}, + {"debug/multimap.h", ""}, + {"debug/multiset.h", ""}, + {"debug/set.h", ""}, + {"deque", ""}, + {"exception", ""}, + {"ext/alloc_traits.h", ""}, + {"ext/atomicity.h", ""}, + {"ext/concurrence.h", ""}, + {"ext/new_allocator.h", ""}, + {"ext/numeric_traits.h", ""}, + {"ext/string_conversions.h", ""}, + {"ext/type_traits.h", ""}, + {"fenv.h", ""}, + {"forward_list", ""}, + {"fstream", ""}, + {"functional", ""}, + {"future", ""}, + {"initializer_list", ""}, + {"iomanip", ""}, + {"ios", ""}, + {"iosfwd", ""}, + {"iostream", ""}, + {"istream", ""}, + {"iterator", ""}, + {"limits", ""}, + {"list", ""}, + {"locale", ""}, + {"map", ""}, + {"memory", ""}, + {"shared_mutex", ""}, + {"mutex", ""}, + {"new", ""}, + {"numeric", ""}, + {"ostream", ""}, + {"queue", ""}, + {"random", ""}, + {"ratio", ""}, + {"regex", ""}, + {"scoped_allocator", ""}, + {"set", ""}, + {"sstream", ""}, + {"stack", ""}, + {"stdexcept", ""}, + {"streambuf", ""}, + {"string", ""}, + {"system_error", ""}, + {"tgmath.h", ""}, + {"thread", ""}, + {"tuple", ""}, + {"type_traits", ""}, + {"typeindex", ""}, + {"typeinfo", ""}, + {"unordered_map", ""}, + {"unordered_set", ""}, + {"utility", ""}, + {"valarray", ""}, + {"vector", ""}, + {"include/complex.h", ""}, + {"include/ctype.h", ""}, + {"include/errno.h", ""}, + {"include/fenv.h", ""}, + {"include/inttypes.h", ""}, + {"include/libio.h", ""}, + {"include/limits.h", ""}, + {"include/locale.h", ""}, + {"include/math.h", ""}, + {"include/setjmp.h", ""}, + {"include/signal.h", ""}, + {"include/stdint.h", ""}, + {"include/stdio.h", ""}, + {"include/stdlib.h", ""}, + {"include/string.h", ""}, + {"include/time.h", ""}, + {"include/wchar.h", ""}, + {"include/wctype.h", ""}, + {"bits/cmathcalls.h", ""}, + {"bits/errno.h", ""}, + {"bits/fenv.h", ""}, + {"bits/huge_val.h", ""}, + {"bits/huge_valf.h", ""}, + {"bits/huge_vall.h", ""}, + {"bits/inf.h", ""}, + {"bits/local_lim.h", ""}, + {"bits/locale.h", ""}, + {"bits/mathcalls.h", ""}, + {"bits/mathdef.h", ""}, + {"bits/nan.h", ""}, + {"bits/posix1_lim.h", ""}, + {"bits/posix2_lim.h", ""}, + {"bits/setjmp.h", ""}, + {"bits/sigaction.h", ""}, + {"bits/sigcontext.h", ""}, + {"bits/siginfo.h", ""}, + {"bits/signum.h", ""}, + {"bits/sigset.h", ""}, + {"bits/sigstack.h", ""}, + {"bits/stdio_lim.h", ""}, + {"bits/sys_errlist.h", ""}, + {"bits/time.h", ""}, + {"bits/timex.h", ""}, + {"bits/typesizes.h", ""}, + {"bits/wchar.h", ""}, + {"bits/wordsize.h", ""}, + {"bits/xopen_lim.h", ""}, + {"include/xlocale.h", ""}, + {"bits/atomic_word.h", ""}, + {"bits/basic_file.h", ""}, + {"bits/c\\+\\+allocator.h", ""}, + {"bits/c\\+\\+config.h", ""}, + {"bits/c\\+\\+io.h", ""}, + {"bits/c\\+\\+locale.h", ""}, + {"bits/cpu_defines.h", ""}, + {"bits/ctype_base.h", ""}, + {"bits/cxxabi_tweaks.h", ""}, + {"bits/error_constants.h", ""}, + {"bits/gthr-default.h", ""}, + {"bits/gthr.h", ""}, + {"bits/opt_random.h", ""}, + {"bits/os_defines.h", ""}, // GNU C headers - {"include/aio.h$", ""}, - {"include/aliases.h$", ""}, - {"include/alloca.h$", ""}, - {"include/ar.h$", ""}, - {"include/argp.h$", ""}, - {"include/argz.h$", ""}, - {"include/arpa/nameser.h$", ""}, - {"include/arpa/nameser_compat.h$", ""}, - {"include/byteswap.h$", ""}, - {"include/cpio.h$", ""}, - {"include/crypt.h$", ""}, - {"include/dirent.h$", ""}, - {"include/dlfcn.h$", ""}, - {"include/elf.h$", ""}, - {"include/endian.h$", ""}, - {"include/envz.h$", ""}, - {"include/err.h$", ""}, - {"include/error.h$", ""}, - {"include/execinfo.h$", ""}, - {"include/fcntl.h$", ""}, - {"include/features.h$", ""}, - {"include/fenv.h$", ""}, - {"include/fmtmsg.h$", ""}, - {"include/fnmatch.h$", ""}, - {"include/fstab.h$", ""}, - {"include/fts.h$", ""}, - {"include/ftw.h$", ""}, - {"include/gconv.h$", ""}, - {"include/getopt.h$", ""}, - {"include/glob.h$", ""}, - {"include/grp.h$", ""}, - {"include/gshadow.h$", ""}, - {"include/iconv.h$", ""}, - {"include/ifaddrs.h$", ""}, - {"include/kdb.h$", ""}, - {"include/langinfo.h$", ""}, - {"include/libgen.h$", ""}, - {"include/libintl.h$", ""}, - {"include/link.h$", ""}, - {"include/malloc.h$", ""}, - {"include/mcheck.h$", ""}, - {"include/memory.h$", ""}, - {"include/mntent.h$", ""}, - {"include/monetary.h$", ""}, - {"include/mqueue.h$", ""}, - {"include/netdb.h$", ""}, - {"include/netinet/in.h$", ""}, - {"include/nl_types.h$", ""}, - {"include/nss.h$", ""}, - {"include/obstack.h$", ""}, - {"include/panel.h$", ""}, - {"include/paths.h$", ""}, - {"include/printf.h$", ""}, - {"include/profile.h$", ""}, - {"include/pthread.h$", ""}, - {"include/pty.h$", ""}, - {"include/pwd.h$", ""}, - {"include/re_comp.h$", ""}, - {"include/regex.h$", ""}, - {"include/regexp.h$", ""}, - {"include/resolv.h$", ""}, - {"include/rpc/netdb.h$", ""}, - {"include/sched.h$", ""}, - {"include/search.h$", ""}, - {"include/semaphore.h$", ""}, - {"include/sgtty.h$", ""}, - {"include/shadow.h$", ""}, - {"include/spawn.h$", ""}, - {"include/stab.h$", ""}, - {"include/stdc-predef.h$", ""}, - {"include/stdio_ext.h$", ""}, - {"include/strings.h$", ""}, - {"include/stropts.h$", ""}, - {"include/sudo_plugin.h$", ""}, - {"include/sysexits.h$", ""}, - {"include/tar.h$", ""}, - {"include/tcpd.h$", ""}, - {"include/term.h$", ""}, - {"include/term_entry.h$", ""}, - {"include/termcap.h$", ""}, - {"include/termios.h$", ""}, - {"include/thread_db.h$", ""}, - {"include/tic.h$", ""}, - {"include/ttyent.h$", ""}, - {"include/uchar.h$", ""}, - {"include/ucontext.h$", ""}, - {"include/ulimit.h$", ""}, - {"include/unctrl.h$", ""}, - {"include/unistd.h$", ""}, - {"include/utime.h$", ""}, - {"include/utmp.h$", ""}, - {"include/utmpx.h$", ""}, - {"include/values.h$", ""}, - {"include/wordexp.h$", ""}, - {"fpu_control.h$", ""}, - {"ieee754.h$", ""}, - {"include/xlocale.h$", ""}, - {"gnu/lib-names.h$", ""}, - {"gnu/libc-version.h$", ""}, - {"gnu/option-groups.h$", ""}, - {"gnu/stubs-32.h$", ""}, - {"gnu/stubs-64.h$", ""}, - {"gnu/stubs-x32.h$", ""}, - {"include/rpc/auth_des.h$", ""}, - {"include/rpc/rpc_msg.h$", ""}, - {"include/rpc/pmap_clnt.h$", ""}, - {"include/rpc/rpc.h$", ""}, - {"include/rpc/types.h$", ""}, - {"include/rpc/auth_unix.h$", ""}, - {"include/rpc/key_prot.h$", ""}, - {"include/rpc/pmap_prot.h$", ""}, - {"include/rpc/auth.h$", ""}, - {"include/rpc/svc_auth.h$", ""}, - {"include/rpc/xdr.h$", ""}, - {"include/rpc/pmap_rmt.h$", ""}, - {"include/rpc/des_crypt.h$", ""}, - {"include/rpc/svc.h$", ""}, - {"include/rpc/rpc_des.h$", ""}, - {"include/rpc/clnt.h$", ""}, - {"include/scsi/scsi.h$", ""}, - {"include/scsi/sg.h$", ""}, - {"include/scsi/scsi_ioctl.h$", ""}, - {"include/netrose/rose.h$", ""}, - {"include/nfs/nfs.h$", ""}, - {"include/netatalk/at.h$", ""}, - {"include/netinet/ether.h$", ""}, - {"include/netinet/icmp6.h$", ""}, - {"include/netinet/if_ether.h$", ""}, - {"include/netinet/if_fddi.h$", ""}, - {"include/netinet/if_tr.h$", ""}, - {"include/netinet/igmp.h$", ""}, - {"include/netinet/in.h$", ""}, - {"include/netinet/in_systm.h$", ""}, - {"include/netinet/ip.h$", ""}, - {"include/netinet/ip6.h$", ""}, - {"include/netinet/ip_icmp.h$", ""}, - {"include/netinet/tcp.h$", ""}, - {"include/netinet/udp.h$", ""}, - {"include/netrom/netrom.h$", ""}, - {"include/protocols/routed.h$", ""}, - {"include/protocols/rwhod.h$", ""}, - {"include/protocols/talkd.h$", ""}, - {"include/protocols/timed.h$", ""}, - {"include/rpcsvc/klm_prot.x$", ""}, - {"include/rpcsvc/rstat.h$", ""}, - {"include/rpcsvc/spray.x$", ""}, - {"include/rpcsvc/nlm_prot.x$", ""}, - {"include/rpcsvc/nis_callback.x$", ""}, - {"include/rpcsvc/yp.h$", ""}, - {"include/rpcsvc/yp.x$", ""}, - {"include/rpcsvc/nfs_prot.h$", ""}, - {"include/rpcsvc/rex.h$", ""}, - {"include/rpcsvc/yppasswd.h$", ""}, - {"include/rpcsvc/rex.x$", ""}, - {"include/rpcsvc/nis_tags.h$", ""}, - {"include/rpcsvc/nis_callback.h$", ""}, - {"include/rpcsvc/nfs_prot.x$", ""}, - {"include/rpcsvc/bootparam_prot.x$", ""}, - {"include/rpcsvc/rusers.x$", ""}, - {"include/rpcsvc/rquota.x$", ""}, - {"include/rpcsvc/nis.h$", ""}, - {"include/rpcsvc/nislib.h$", ""}, - {"include/rpcsvc/ypupd.h$", ""}, - {"include/rpcsvc/bootparam.h$", ""}, - {"include/rpcsvc/spray.h$", ""}, - {"include/rpcsvc/key_prot.h$", ""}, - {"include/rpcsvc/klm_prot.h$", ""}, - {"include/rpcsvc/sm_inter.h$", ""}, - {"include/rpcsvc/nlm_prot.h$", ""}, - {"include/rpcsvc/yp_prot.h$", ""}, - {"include/rpcsvc/ypclnt.h$", ""}, - {"include/rpcsvc/rstat.x$", ""}, - {"include/rpcsvc/rusers.h$", ""}, - {"include/rpcsvc/key_prot.x$", ""}, - {"include/rpcsvc/sm_inter.x$", ""}, - {"include/rpcsvc/rquota.h$", ""}, - {"include/rpcsvc/nis.x$", ""}, - {"include/rpcsvc/bootparam_prot.h$", ""}, - {"include/rpcsvc/mount.h$", ""}, - {"include/rpcsvc/mount.x$", ""}, - {"include/rpcsvc/nis_object.x$", ""}, - {"include/rpcsvc/yppasswd.x$", ""}, - {"sys/acct.h$", ""}, - {"sys/auxv.h$", ""}, - {"sys/cdefs.h$", ""}, - {"sys/debugreg.h$", ""}, - {"sys/dir.h$", ""}, - {"sys/elf.h$", ""}, - {"sys/epoll.h$", ""}, - {"sys/eventfd.h$", ""}, - {"sys/fanotify.h$", ""}, - {"sys/file.h$", ""}, - {"sys/fsuid.h$", ""}, - {"sys/gmon.h$", ""}, - {"sys/gmon_out.h$", ""}, - {"sys/inotify.h$", ""}, - {"sys/io.h$", ""}, - {"sys/ioctl.h$", ""}, - {"sys/ipc.h$", ""}, - {"sys/kd.h$", ""}, - {"sys/kdaemon.h$", ""}, - {"sys/klog.h$", ""}, - {"sys/mman.h$", ""}, - {"sys/mount.h$", ""}, - {"sys/msg.h$", ""}, - {"sys/mtio.h$", ""}, - {"sys/param.h$", ""}, - {"sys/pci.h$", ""}, - {"sys/perm.h$", ""}, - {"sys/personality.h$", ""}, - {"sys/poll.h$", ""}, - {"sys/prctl.h$", ""}, - {"sys/procfs.h$", ""}, - {"sys/profil.h$", ""}, - {"sys/ptrace.h$", ""}, - {"sys/queue.h$", ""}, - {"sys/quota.h$", ""}, - {"sys/raw.h$", ""}, - {"sys/reboot.h$", ""}, - {"sys/reg.h$", ""}, - {"sys/resource.h$", ""}, - {"sys/select.h$", ""}, - {"sys/sem.h$", ""}, - {"sys/sendfile.h$", ""}, - {"sys/shm.h$", ""}, - {"sys/signalfd.h$", ""}, - {"sys/socket.h$", ""}, - {"sys/stat.h$", ""}, - {"sys/statfs.h$", ""}, - {"sys/statvfs.h$", ""}, - {"sys/swap.h$", ""}, - {"sys/syscall.h$", ""}, - {"sys/sysctl.h$", ""}, - {"sys/sysinfo.h$", ""}, - {"sys/syslog.h$", ""}, - {"sys/sysmacros.h$", ""}, - {"sys/termios.h$", ""}, - {"sys/time.h$", ""}, - {"sys/timeb.h$", ""}, - {"sys/timerfd.h$", ""}, - {"sys/times.h$", ""}, - {"sys/timex.h$", ""}, - {"sys/ttychars.h$", ""}, - {"sys/ttydefaults.h$", ""}, - {"sys/types.h$", ""}, - {"sys/ucontext.h$", ""}, - {"sys/uio.h$", ""}, - {"sys/un.h$", ""}, - {"sys/user.h$", ""}, - {"sys/ustat.h$", ""}, - {"sys/utsname.h$", ""}, - {"sys/vlimit.h$", ""}, - {"sys/vm86.h$", ""}, - {"sys/vtimes.h$", ""}, - {"sys/wait.h$", ""}, - {"sys/xattr.h$", ""}, - {"bits/epoll.h$", ""}, - {"bits/eventfd.h$", ""}, - {"bits/inotify.h$", ""}, - {"bits/ipc.h$", ""}, - {"bits/ipctypes.h$", ""}, - {"bits/mman-linux.h$", ""}, - {"bits/mman.h$", ""}, - {"bits/msq.h$", ""}, - {"bits/resource.h$", ""}, - {"bits/sem.h$", ""}, - {"bits/shm.h$", ""}, - {"bits/signalfd.h$", ""}, - {"bits/statfs.h$", ""}, - {"bits/statvfs.h$", ""}, - {"bits/timerfd.h$", ""}, - {"bits/utsname.h$", ""}, - {"bits/auxv.h$", ""}, - {"bits/byteswap-16.h$", ""}, - {"bits/byteswap.h$", ""}, - {"bits/confname.h$", ""}, - {"bits/dirent.h$", ""}, - {"bits/dlfcn.h$", ""}, - {"bits/elfclass.h$", ""}, - {"bits/endian.h$", ""}, - {"bits/environments.h$", ""}, - {"bits/fcntl-linux.h$", ""}, - {"bits/fcntl.h$", ""}, - {"bits/in.h$", ""}, - {"bits/ioctl-types.h$", ""}, - {"bits/ioctls.h$", ""}, - {"bits/link.h$", ""}, - {"bits/mqueue.h$", ""}, - {"bits/netdb.h$", ""}, - {"bits/param.h$", ""}, - {"bits/poll.h$", ""}, - {"bits/posix_opt.h$", ""}, - {"bits/pthreadtypes.h$", ""}, - {"bits/sched.h$", ""}, - {"bits/select.h$", ""}, - {"bits/semaphore.h$", ""}, - {"bits/sigthread.h$", ""}, - {"bits/sockaddr.h$", ""}, - {"bits/socket.h$", ""}, - {"bits/socket_type.h$", ""}, - {"bits/stab.def$", ""}, - {"bits/stat.h$", ""}, - {"bits/stropts.h$", ""}, - {"bits/syscall.h$", ""}, - {"bits/syslog-path.h$", ""}, - {"bits/termios.h$", ""}, - {"bits/types.h$", ""}, - {"bits/typesizes.h$", ""}, - {"bits/uio.h$", ""}, - {"bits/ustat.h$", ""}, - {"bits/utmp.h$", ""}, - {"bits/utmpx.h$", ""}, - {"bits/waitflags.h$", ""}, - {"bits/waitstatus.h$", ""}, - {"bits/xtitypes.h$", ""}, + {"include/aio.h", ""}, + {"include/aliases.h", ""}, + {"include/alloca.h", ""}, + {"include/ar.h", ""}, + {"include/argp.h", ""}, + {"include/argz.h", ""}, + {"include/arpa/nameser.h", ""}, + {"include/arpa/nameser_compat.h", ""}, + {"include/byteswap.h", ""}, + {"include/cpio.h", ""}, + {"include/crypt.h", ""}, + {"include/dirent.h", ""}, + {"include/dlfcn.h", ""}, + {"include/elf.h", ""}, + {"include/endian.h", ""}, + {"include/envz.h", ""}, + {"include/err.h", ""}, + {"include/error.h", ""}, + {"include/execinfo.h", ""}, + {"include/fcntl.h", ""}, + {"include/features.h", ""}, + {"include/fenv.h", ""}, + {"include/fmtmsg.h", ""}, + {"include/fnmatch.h", ""}, + {"include/fstab.h", ""}, + {"include/fts.h", ""}, + {"include/ftw.h", ""}, + {"include/gconv.h", ""}, + {"include/getopt.h", ""}, + {"include/glob.h", ""}, + {"include/grp.h", ""}, + {"include/gshadow.h", ""}, + {"include/iconv.h", ""}, + {"include/ifaddrs.h", ""}, + {"include/kdb.h", ""}, + {"include/langinfo.h", ""}, + {"include/libgen.h", ""}, + {"include/libintl.h", ""}, + {"include/link.h", ""}, + {"include/malloc.h", ""}, + {"include/mcheck.h", ""}, + {"include/memory.h", ""}, + {"include/mntent.h", ""}, + {"include/monetary.h", ""}, + {"include/mqueue.h", ""}, + {"include/netdb.h", ""}, + {"include/netinet/in.h", ""}, + {"include/nl_types.h", ""}, + {"include/nss.h", ""}, + {"include/obstack.h", ""}, + {"include/panel.h", ""}, + {"include/paths.h", ""}, + {"include/printf.h", ""}, + {"include/profile.h", ""}, + {"include/pthread.h", ""}, + {"include/pty.h", ""}, + {"include/pwd.h", ""}, + {"include/re_comp.h", ""}, + {"include/regex.h", ""}, + {"include/regexp.h", ""}, + {"include/resolv.h", ""}, + {"include/rpc/netdb.h", ""}, + {"include/sched.h", ""}, + {"include/search.h", ""}, + {"include/semaphore.h", ""}, + {"include/sgtty.h", ""}, + {"include/shadow.h", ""}, + {"include/spawn.h", ""}, + {"include/stab.h", ""}, + {"include/stdc-predef.h", ""}, + {"include/stdio_ext.h", ""}, + {"include/strings.h", ""}, + {"include/stropts.h", ""}, + {"include/sudo_plugin.h", ""}, + {"include/sysexits.h", ""}, + {"include/tar.h", ""}, + {"include/tcpd.h", ""}, + {"include/term.h", ""}, + {"include/term_entry.h", ""}, + {"include/termcap.h", ""}, + {"include/termios.h", ""}, + {"include/thread_db.h", ""}, + {"include/tic.h", ""}, + {"include/ttyent.h", ""}, + {"include/uchar.h", ""}, + {"include/ucontext.h", ""}, + {"include/ulimit.h", ""}, + {"include/unctrl.h", ""}, + {"include/unistd.h", ""}, + {"include/utime.h", ""}, + {"include/utmp.h", ""}, + {"include/utmpx.h", ""}, + {"include/values.h", ""}, + {"include/wordexp.h", ""}, + {"fpu_control.h", ""}, + {"ieee754.h", ""}, + {"include/xlocale.h", ""}, + {"gnu/lib-names.h", ""}, + {"gnu/libc-version.h", ""}, + {"gnu/option-groups.h", ""}, + {"gnu/stubs-32.h", ""}, + {"gnu/stubs-64.h", ""}, + {"gnu/stubs-x32.h", ""}, + {"include/rpc/auth_des.h", ""}, + {"include/rpc/rpc_msg.h", ""}, + {"include/rpc/pmap_clnt.h", ""}, + {"include/rpc/rpc.h", ""}, + {"include/rpc/types.h", ""}, + {"include/rpc/auth_unix.h", ""}, + {"include/rpc/key_prot.h", ""}, + {"include/rpc/pmap_prot.h", ""}, + {"include/rpc/auth.h", ""}, + {"include/rpc/svc_auth.h", ""}, + {"include/rpc/xdr.h", ""}, + {"include/rpc/pmap_rmt.h", ""}, + {"include/rpc/des_crypt.h", ""}, + {"include/rpc/svc.h", ""}, + {"include/rpc/rpc_des.h", ""}, + {"include/rpc/clnt.h", ""}, + {"include/scsi/scsi.h", ""}, + {"include/scsi/sg.h", ""}, + {"include/scsi/scsi_ioctl.h", ""}, + {"include/netrose/rose.h", ""}, + {"include/nfs/nfs.h", ""}, + {"include/netatalk/at.h", ""}, + {"include/netinet/ether.h", ""}, + {"include/netinet/icmp6.h", ""}, + {"include/netinet/if_ether.h", ""}, + {"include/netinet/if_fddi.h", ""}, + {"include/netinet/if_tr.h", ""}, + {"include/netinet/igmp.h", ""}, + {"include/netinet/in.h", ""}, + {"include/netinet/in_systm.h", ""}, + {"include/netinet/ip.h", ""}, + {"include/netinet/ip6.h", ""}, + {"include/netinet/ip_icmp.h", ""}, + {"include/netinet/tcp.h", ""}, + {"include/netinet/udp.h", ""}, + {"include/netrom/netrom.h", ""}, + {"include/protocols/routed.h", ""}, + {"include/protocols/rwhod.h", ""}, + {"include/protocols/talkd.h", ""}, + {"include/protocols/timed.h", ""}, + {"include/rpcsvc/klm_prot.x", ""}, + {"include/rpcsvc/rstat.h", ""}, + {"include/rpcsvc/spray.x", ""}, + {"include/rpcsvc/nlm_prot.x", ""}, + {"include/rpcsvc/nis_callback.x", ""}, + {"include/rpcsvc/yp.h", ""}, + {"include/rpcsvc/yp.x", ""}, + {"include/rpcsvc/nfs_prot.h", ""}, + {"include/rpcsvc/rex.h", ""}, + {"include/rpcsvc/yppasswd.h", ""}, + {"include/rpcsvc/rex.x", ""}, + {"include/rpcsvc/nis_tags.h", ""}, + {"include/rpcsvc/nis_callback.h", ""}, + {"include/rpcsvc/nfs_prot.x", ""}, + {"include/rpcsvc/bootparam_prot.x", ""}, + {"include/rpcsvc/rusers.x", ""}, + {"include/rpcsvc/rquota.x", ""}, + {"include/rpcsvc/nis.h", ""}, + {"include/rpcsvc/nislib.h", ""}, + {"include/rpcsvc/ypupd.h", ""}, + {"include/rpcsvc/bootparam.h", ""}, + {"include/rpcsvc/spray.h", ""}, + {"include/rpcsvc/key_prot.h", ""}, + {"include/rpcsvc/klm_prot.h", ""}, + {"include/rpcsvc/sm_inter.h", ""}, + {"include/rpcsvc/nlm_prot.h", ""}, + {"include/rpcsvc/yp_prot.h", ""}, + {"include/rpcsvc/ypclnt.h", ""}, + {"include/rpcsvc/rstat.x", ""}, + {"include/rpcsvc/rusers.h", ""}, + {"include/rpcsvc/key_prot.x", ""}, + {"include/rpcsvc/sm_inter.x", ""}, + {"include/rpcsvc/rquota.h", ""}, + {"include/rpcsvc/nis.x", ""}, + {"include/rpcsvc/bootparam_prot.h", ""}, + {"include/rpcsvc/mount.h", ""}, + {"include/rpcsvc/mount.x", ""}, + {"include/rpcsvc/nis_object.x", ""}, + {"include/rpcsvc/yppasswd.x", ""}, + {"sys/acct.h", ""}, + {"sys/auxv.h", ""}, + {"sys/cdefs.h", ""}, + {"sys/debugreg.h", ""}, + {"sys/dir.h", ""}, + {"sys/elf.h", ""}, + {"sys/epoll.h", ""}, + {"sys/eventfd.h", ""}, + {"sys/fanotify.h", ""}, + {"sys/file.h", ""}, + {"sys/fsuid.h", ""}, + {"sys/gmon.h", ""}, + {"sys/gmon_out.h", ""}, + {"sys/inotify.h", ""}, + {"sys/io.h", ""}, + {"sys/ioctl.h", ""}, + {"sys/ipc.h", ""}, + {"sys/kd.h", ""}, + {"sys/kdaemon.h", ""}, + {"sys/klog.h", ""}, + {"sys/mman.h", ""}, + {"sys/mount.h", ""}, + {"sys/msg.h", ""}, + {"sys/mtio.h", ""}, + {"sys/param.h", ""}, + {"sys/pci.h", ""}, + {"sys/perm.h", ""}, + {"sys/personality.h", ""}, + {"sys/poll.h", ""}, + {"sys/prctl.h", ""}, + {"sys/procfs.h", ""}, + {"sys/profil.h", ""}, + {"sys/ptrace.h", ""}, + {"sys/queue.h", ""}, + {"sys/quota.h", ""}, + {"sys/raw.h", ""}, + {"sys/reboot.h", ""}, + {"sys/reg.h", ""}, + {"sys/resource.h", ""}, + {"sys/select.h", ""}, + {"sys/sem.h", ""}, + {"sys/sendfile.h", ""}, + {"sys/shm.h", ""}, + {"sys/signalfd.h", ""}, + {"sys/socket.h", ""}, + {"sys/stat.h", ""}, + {"sys/statfs.h", ""}, + {"sys/statvfs.h", ""}, + {"sys/swap.h", ""}, + {"sys/syscall.h", ""}, + {"sys/sysctl.h", ""}, + {"sys/sysinfo.h", ""}, + {"sys/syslog.h", ""}, + {"sys/sysmacros.h", ""}, + {"sys/termios.h", ""}, + {"sys/time.h", ""}, + {"sys/timeb.h", ""}, + {"sys/timerfd.h", ""}, + {"sys/times.h", ""}, + {"sys/timex.h", ""}, + {"sys/ttychars.h", ""}, + {"sys/ttydefaults.h", ""}, + {"sys/types.h", ""}, + {"sys/ucontext.h", ""}, + {"sys/uio.h", ""}, + {"sys/un.h", ""}, + {"sys/user.h", ""}, + {"sys/ustat.h", ""}, + {"sys/utsname.h", ""}, + {"sys/vlimit.h", ""}, + {"sys/vm86.h", ""}, + {"sys/vtimes.h", ""}, + {"sys/wait.h", ""}, + {"sys/xattr.h", ""}, + {"bits/epoll.h", ""}, + {"bits/eventfd.h", ""}, + {"bits/inotify.h", ""}, + {"bits/ipc.h", ""}, + {"bits/ipctypes.h", ""}, + {"bits/mman-linux.h", ""}, + {"bits/mman.h", ""}, + {"bits/msq.h", ""}, + {"bits/resource.h", ""}, + {"bits/sem.h", ""}, + {"bits/shm.h", ""}, + {"bits/signalfd.h", ""}, + {"bits/statfs.h", ""}, + {"bits/statvfs.h", ""}, + {"bits/timerfd.h", ""}, + {"bits/utsname.h", ""}, + {"bits/auxv.h", ""}, + {"bits/byteswap-16.h", ""}, + {"bits/byteswap.h", ""}, + {"bits/confname.h", ""}, + {"bits/dirent.h", ""}, + {"bits/dlfcn.h", ""}, + {"bits/elfclass.h", ""}, + {"bits/endian.h", ""}, + {"bits/environments.h", ""}, + {"bits/fcntl-linux.h", ""}, + {"bits/fcntl.h", ""}, + {"bits/in.h", ""}, + {"bits/ioctl-types.h", ""}, + {"bits/ioctls.h", ""}, + {"bits/link.h", ""}, + {"bits/mqueue.h", ""}, + {"bits/netdb.h", ""}, + {"bits/param.h", ""}, + {"bits/poll.h", ""}, + {"bits/posix_opt.h", ""}, + {"bits/pthreadtypes.h", ""}, + {"bits/sched.h", ""}, + {"bits/select.h", ""}, + {"bits/semaphore.h", ""}, + {"bits/sigthread.h", ""}, + {"bits/sockaddr.h", ""}, + {"bits/socket.h", ""}, + {"bits/socket_type.h", ""}, + {"bits/stab.def", ""}, + {"bits/stat.h", ""}, + {"bits/stropts.h", ""}, + {"bits/syscall.h", ""}, + {"bits/syslog-path.h", ""}, + {"bits/termios.h", ""}, + {"bits/types.h", ""}, + {"bits/typesizes.h", ""}, + {"bits/uio.h", ""}, + {"bits/ustat.h", ""}, + {"bits/utmp.h", ""}, + {"bits/utmpx.h", ""}, + {"bits/waitflags.h", ""}, + {"bits/waitstatus.h", ""}, + {"bits/xtitypes.h", ""}, }; for (const auto &Pair : SystemHeaderMap) - Includes->addRegexMapping(Pair.first, Pair.second); + Includes->addPathSuffixMapping(Pair.first, Pair.second); } } // namespace clangd diff --git a/clangd/index/CanonicalIncludes.h b/clangd/index/CanonicalIncludes.h index 2488f5653..3751b0003 100644 --- a/clangd/index/CanonicalIncludes.h +++ b/clangd/index/CanonicalIncludes.h @@ -40,8 +40,10 @@ class CanonicalIncludes { /// Adds a string-to-string mapping from \p Path to \p CanonicalPath. void addMapping(llvm::StringRef Path, llvm::StringRef CanonicalPath); - /// Maps all files matching \p RE to \p CanonicalPath - void addRegexMapping(llvm::StringRef RE, llvm::StringRef CanonicalPath); + /// Maps files with last path components matching \p Suffix to \p + /// CanonicalPath. + void addPathSuffixMapping(llvm::StringRef Suffix, + llvm::StringRef CanonicalPath); /// Sets the canonical include for any symbol with \p QualifiedName. /// Symbol mappings take precedence over header mappings. @@ -55,17 +57,15 @@ class CanonicalIncludes { llvm::StringRef QualifiedName) const; private: - // A map from header patterns to header names. This needs to be mutable so - // that we can match again a Regex in a const function member. - // FIXME(ioeric): All the regexes we have so far are suffix matches. The - // performance could be improved by allowing only suffix matches instead of - // arbitrary regexes. - mutable std::vector> - RegexHeaderMappingTable; - // A map from fully qualified symbol names to header names. + /// A map from full include path to a canonical path. + llvm::StringMap FullPathMapping; + /// A map from a suffix (one or components of a path) to a canonical path. + llvm::StringMap SuffixHeaderMapping; + /// Maximum number of path components stored in a key of SuffixHeaderMapping. + /// Used to reduce the number of lookups into SuffixHeaderMapping. + int MaxSuffixComponents = 0; + /// A map from fully qualified symbol names to header names. llvm::StringMap SymbolMapping; - // Guards Regex matching as it's not thread-safe. - mutable std::mutex RegexMutex; }; /// Returns a CommentHandler that parses pragma comment on include files to From 3ec7cea6337677271ce49abd34f718416d0fda48 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Wed, 22 Aug 2018 13:58:25 +0000 Subject: [PATCH 078/686] [clang-tidy] Abseil: faster strsplit delimiter check This check is an abseil specific check that checks for code using single character string literals as delimiters and transforms the code into characters. The check was developed internally and has been running at google, this is just a move to open source the check. It was originally written by @sbenza. Patch by Deanna Garcia! git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340411 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/AbseilTidyModule.cpp | 3 + clang-tidy/abseil/CMakeLists.txt | 1 + .../abseil/FasterStrsplitDelimiterCheck.cpp | 131 ++++++++++++++++++ .../abseil/FasterStrsplitDelimiterCheck.h | 36 +++++ docs/ReleaseNotes.rst | 6 + .../abseil-faster-strsplit-delimiter.rst | 41 ++++++ docs/clang-tidy/checks/list.rst | 1 + .../abseil-faster-strsplit-delimiter.cpp | 99 +++++++++++++ 8 files changed, 318 insertions(+) create mode 100644 clang-tidy/abseil/FasterStrsplitDelimiterCheck.cpp create mode 100644 clang-tidy/abseil/FasterStrsplitDelimiterCheck.h create mode 100644 docs/clang-tidy/checks/abseil-faster-strsplit-delimiter.rst create mode 100644 test/clang-tidy/abseil-faster-strsplit-delimiter.cpp diff --git a/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tidy/abseil/AbseilTidyModule.cpp index 061fc7c4d..c4f27a492 100644 --- a/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tidy/abseil/AbseilTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "DurationDivisionCheck.h" +#include "FasterStrsplitDelimiterCheck.h" #include "StringFindStartswithCheck.h" namespace clang { @@ -22,6 +23,8 @@ class AbseilModule : public ClangTidyModule { void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck( "abseil-duration-division"); + CheckFactories.registerCheck( + "abseil-faster-strsplit-delimiter"); CheckFactories.registerCheck( "abseil-string-find-startswith"); } diff --git a/clang-tidy/abseil/CMakeLists.txt b/clang-tidy/abseil/CMakeLists.txt index a2478fd42..0a2e84b63 100644 --- a/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tidy/abseil/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyAbseilModule AbseilTidyModule.cpp DurationDivisionCheck.cpp + FasterStrsplitDelimiterCheck.cpp StringFindStartswithCheck.cpp LINK_LIBS diff --git a/clang-tidy/abseil/FasterStrsplitDelimiterCheck.cpp b/clang-tidy/abseil/FasterStrsplitDelimiterCheck.cpp new file mode 100644 index 000000000..35d99ac18 --- /dev/null +++ b/clang-tidy/abseil/FasterStrsplitDelimiterCheck.cpp @@ -0,0 +1,131 @@ +//===--- FasterStrsplitDelimiterCheck.cpp - clang-tidy---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FasterStrsplitDelimiterCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +namespace { + +AST_MATCHER(StringLiteral, lengthIsOne) { return Node.getLength() == 1; } + +::internal::Matcher +constructExprWithArg(llvm::StringRef ClassName, + const ::internal::Matcher &Arg) { + auto ConstrExpr = cxxConstructExpr(hasType(recordDecl(hasName(ClassName))), + hasArgument(0, ignoringParenCasts(Arg))); + + return anyOf(ConstrExpr, cxxBindTemporaryExpr(has(ConstrExpr))); +} + +::internal::Matcher +copyConstructExprWithArg(llvm::StringRef ClassName, + const ::internal::Matcher &Arg) { + return constructExprWithArg(ClassName, constructExprWithArg(ClassName, Arg)); +} + +llvm::Optional makeCharacterLiteral(const StringLiteral *Literal) { + std::string Result; + { + llvm::raw_string_ostream Stream(Result); + Literal->outputString(Stream); + } + + // Special case: If the string contains a single quote, we just need to return + // a character of the single quote. This is a special case because we need to + // escape it in the character literal. + if (Result == R"("'")") + return std::string(R"('\'')"); + + assert(Result.size() == 3 || (Result.size() == 4 && Result.substr(0, 2) == "\"\\")); + + // Now replace the " with '. + auto Pos = Result.find_first_of('"'); + if (Pos == Result.npos) + return llvm::None; + Result[Pos] = '\''; + Pos = Result.find_last_of('"'); + if (Pos == Result.npos) + return llvm::None; + Result[Pos] = '\''; + return Result; +} + +} // anonymous namespace + +void FasterStrsplitDelimiterCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + // Binds to one character string literals. + const auto SingleChar = + expr(ignoringParenCasts(stringLiteral(lengthIsOne()).bind("Literal"))); + + // Binds to a string_view (either absl or std) that was passed by value and + // contructed from string literal. + auto StringViewArg = + copyConstructExprWithArg("::absl::string_view", SingleChar); + + auto ByAnyCharArg = + expr(copyConstructExprWithArg("::absl::ByAnyChar", StringViewArg)) + .bind("ByAnyChar"); + + // Find uses of absl::StrSplit(..., "x") and absl::StrSplit(..., + // absl::ByAnyChar("x")) to transform them into absl::StrSplit(..., 'x'). + Finder->addMatcher(callExpr(callee(functionDecl(hasName("::absl::StrSplit"))), + hasArgument(1, anyOf(ByAnyCharArg, SingleChar)), + unless(isInTemplateInstantiation())) + .bind("StrSplit"), + this); + + // Find uses of absl::MaxSplits("x", N) and + // absl::MaxSplits(absl::ByAnyChar("x"), N) to transform them into + // absl::MaxSplits('x', N). + Finder->addMatcher( + callExpr( + callee(functionDecl(hasName("::absl::MaxSplits"))), + hasArgument(0, anyOf(ByAnyCharArg, ignoringParenCasts(SingleChar))), + unless(isInTemplateInstantiation())), + this); +} + +void FasterStrsplitDelimiterCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *Literal = Result.Nodes.getNodeAs("Literal"); + + if (Literal->getBeginLoc().isMacroID() || Literal->getEndLoc().isMacroID()) + return; + + llvm::Optional Replacement = makeCharacterLiteral(Literal); + if (!Replacement) + return; + SourceRange Range = Literal->getSourceRange(); + + if (const auto *ByAnyChar = Result.Nodes.getNodeAs("ByAnyChar")) + Range = ByAnyChar->getSourceRange(); + + diag( + Literal->getBeginLoc(), + "%select{absl::StrSplit()|absl::MaxSplits()}0 called with a string " + "literal " + "consisting of a single character; consider using the character overload") + << (Result.Nodes.getNodeAs("StrSplit") ? 0 : 1) + << FixItHint::CreateReplacement(CharSourceRange::getTokenRange(Range), + *Replacement); +} + +} // namespace abseil +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/abseil/FasterStrsplitDelimiterCheck.h b/clang-tidy/abseil/FasterStrsplitDelimiterCheck.h new file mode 100644 index 000000000..17e231cb1 --- /dev/null +++ b/clang-tidy/abseil/FasterStrsplitDelimiterCheck.h @@ -0,0 +1,36 @@ +//===--- FasterStrsplitDelimiterCheck.h - clang-tidy-------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_FASTERSTRSPLITDELIMITERCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_FASTERSTRSPLITDELIMITERCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace abseil { + +/// Finds instances of absl::StrSplit() or absl::MaxSplits() where the delimiter +/// is a single character string literal and replaces it with a character. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-faster-strsplit-delimiter.html +class FasterStrsplitDelimiterCheck : public ClangTidyCheck { +public: + FasterStrsplitDelimiterCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_FASTERSTRSPLITDELIMITERCHECK_H diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index cea98b334..3d9389d82 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -64,6 +64,12 @@ Improvements to clang-tidy floating-point context, and recommends the use of a function that returns a floating-point value. +- New :doc:`abseil-faster-strsplit-delimiter + ` check. + + Finds instances of ``absl::StrSplit()`` or ``absl::MaxSplits()`` where the + delimiter is a single character string literal and replaces with a character. + - New :doc:`readability-magic-numbers ` check. diff --git a/docs/clang-tidy/checks/abseil-faster-strsplit-delimiter.rst b/docs/clang-tidy/checks/abseil-faster-strsplit-delimiter.rst new file mode 100644 index 000000000..fe9115652 --- /dev/null +++ b/docs/clang-tidy/checks/abseil-faster-strsplit-delimiter.rst @@ -0,0 +1,41 @@ +.. title:: clang-tidy - abseil-faster-strsplit-delimiter + +abseil-faster-strsplit-delimiter +================================ + +Finds instances of ``absl::StrSplit()`` or ``absl::MaxSplits()`` where the +delimiter is a single character string literal and replaces with a character. +The check will offer a suggestion to change the string literal into a character. +It will also catch code using ``absl::ByAnyChar()`` for just a single character +and will transform that into a single character as well. + +These changes will give the same result, but using characters rather than +single character string literals is more efficient and readable. + +Examples: + +.. code-block:: c++ + + // Original - the argument is a string literal. + for (auto piece : absl::StrSplit(str, "B")) { + + // Suggested - the argument is a character, which causes the more efficient + // overload of absl::StrSplit() to be used. + for (auto piece : absl::StrSplit(str, 'B')) { + + + // Original - the argument is a string literal inside absl::ByAnyChar call. + for (auto piece : absl::StrSplit(str, absl::ByAnyChar("B"))) { + + // Suggested - the argument is a character, which causes the more efficient + // overload of absl::StrSplit() to be used and we do not need absl::ByAnyChar + // anymore. + for (auto piece : absl::StrSplit(str, 'B')) { + + + // Original - the argument is a string literal inside absl::MaxSplits call. + for (auto piece : absl::StrSplit(str, absl::MaxSplits("B", 1))) { + + // Suggested - the argument is a character, which causes the more efficient + // overload of absl::StrSplit() to be used. + for (auto piece : absl::StrSplit(str, absl::MaxSplits('B', 1))) { diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index 696905b50..3ecb17c25 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -5,6 +5,7 @@ Clang-Tidy Checks .. toctree:: abseil-duration-division + abseil-faster-strsplit-delimiter abseil-string-find-startswith android-cloexec-accept android-cloexec-accept4 diff --git a/test/clang-tidy/abseil-faster-strsplit-delimiter.cpp b/test/clang-tidy/abseil-faster-strsplit-delimiter.cpp new file mode 100644 index 000000000..5895e45a9 --- /dev/null +++ b/test/clang-tidy/abseil-faster-strsplit-delimiter.cpp @@ -0,0 +1,99 @@ +// RUN: %check_clang_tidy %s abseil-faster-strsplit-delimiter %t + +namespace absl { + +class string_view { + public: + string_view(); + string_view(const char *); +}; + +namespace strings_internal { +struct Splitter {}; +struct MaxSplitsImpl { + MaxSplitsImpl(); + ~MaxSplitsImpl(); +}; +} //namespace strings_internal + +template +strings_internal::Splitter StrSplit(absl::string_view, Delim) { + return {}; +} +template +strings_internal::Splitter StrSplit(absl::string_view, Delim, Pred) { + return {}; +} + +class ByAnyChar { + public: + explicit ByAnyChar(absl::string_view); + ~ByAnyChar(); +}; + +template +strings_internal::MaxSplitsImpl MaxSplits(Delim, int) { + return {}; +} + +} //namespace absl + +void SplitDelimiters() { + absl::StrSplit("ABC", "A"); + // CHECK-MESSAGES: [[@LINE-1]]:25: warning: absl::StrSplit() called with a string literal consisting of a single character; consider using the character overload [abseil-faster-strsplit-delimiter] + // CHECK-FIXES: absl::StrSplit("ABC", 'A'); + + absl::StrSplit("ABC", absl::ByAnyChar("\n")); + // CHECK-MESSAGES: [[@LINE-1]]:41: warning: absl::StrSplit() + // CHECK-FIXES: absl::StrSplit("ABC", '\n'); + + // Works with predicate + absl::StrSplit("ABC", "A", [](absl::string_view) { return true; }); + // CHECK-MESSAGES: [[@LINE-1]]:25: warning: absl::StrSplit() + // CHECK-FIXES: absl::StrSplit("ABC", 'A', [](absl::string_view) { return true; }); + + // Doesn't do anything with other strings lenghts. + absl::StrSplit("ABC", "AB"); + absl::StrSplit("ABC", absl::ByAnyChar("")); + absl::StrSplit("ABC", absl::ByAnyChar(" \t")); + + // Escapes a single quote in the resulting character literal. + absl::StrSplit("ABC", "'"); + // CHECK-MESSAGES: [[@LINE-1]]:25: warning: absl::StrSplit() + // CHECK-FIXES: absl::StrSplit("ABC", '\''); + + absl::StrSplit("ABC", "\""); + // CHECK-MESSAGES: [[@LINE-1]]:25: warning: absl::StrSplit() + // CHECK-FIXES: absl::StrSplit("ABC", '\"'); + + absl::StrSplit("ABC", absl::MaxSplits("\t", 1)); + // CHECK-MESSAGES: [[@LINE-1]]:41: warning: absl::MaxSplits() + // CHECK-FIXES: absl::StrSplit("ABC", absl::MaxSplits('\t', 1)); + + auto delim = absl::MaxSplits(absl::ByAnyChar(" "), 1); + // CHECK-MESSAGES: [[@LINE-1]]:48: warning: absl::MaxSplits() + // CHECK-FIXES: auto delim = absl::MaxSplits(' ', 1); +} + +#define MACRO(str) absl::StrSplit("ABC", str) + +void Macro() { + MACRO("A"); +} + +template +void FunctionTemplate() { + // This one should not warn because ByAnyChar is a dependent type. + absl::StrSplit("TTT", T("A")); + + // This one will warn, but we are checking that we get a correct warning only + // once. + absl::StrSplit("TTT", "A"); + // CHECK-MESSAGES: [[@LINE-1]]:25: warning: absl::StrSplit() + // CHECK-FIXES: absl::StrSplit("TTT", 'A'); +} + +void FunctionTemplateCaller() { + FunctionTemplate(); + FunctionTemplate(); +} From ef9c2c07b9cd183e9b9a9944ef6a8be8f7884429 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Wed, 22 Aug 2018 14:03:30 +0000 Subject: [PATCH 079/686] [clang-tidy] Add Abseil prefix to documentation Summary: Adds the Abseil prefix to the list of prefixes in the documentation Patch by Deanna Garcia! Reviewers: aaron.ballman, hokein Reviewed By: hokein Subscribers: xazax.hun, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D51100 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340412 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/clang-tidy/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/clang-tidy/index.rst b/docs/clang-tidy/index.rst index 0ed350c7b..3b7443c2d 100644 --- a/docs/clang-tidy/index.rst +++ b/docs/clang-tidy/index.rst @@ -55,6 +55,7 @@ There are currently the following groups of checks: ====================== ========================================================= Name prefix Description ====================== ========================================================= +``abseil-`` Checks related to Abseil library. ``android-`` Checks related to Android. ``boost-`` Checks related to Boost library. ``bugprone-`` Checks that target bugprone code constructs. From 32f6cc55ee957a8896b0fee56c005919b52fc059 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Wed, 22 Aug 2018 20:30:06 +0000 Subject: [PATCH 080/686] [clangd] send diagnostic categories only when 'categorySupport' capability was given by the client After r339738 Clangd started sending categories with each diagnostic, but that broke the eglot client. This commit puts the categories behind a capability to fix that breakage. Differential Revision: https://reviews.llvm.org/D51077 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340449 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 4 +- clangd/Diagnostics.h | 6 +++ clangd/Protocol.cpp | 1 + clangd/Protocol.h | 4 ++ .../compile-commands-path-in-initialize.test | 2 - test/clangd/compile-commands-path.test | 3 -- test/clangd/diagnostic-category.test | 43 +++++++++++++++++++ test/clangd/diagnostics.test | 1 - .../did-change-configuration-params.test | 1 - test/clangd/execute-command.test | 1 - test/clangd/extra-flags.test | 2 - test/clangd/fixits-embed-in-diagnostic.test | 1 - test/clangd/fixits.test | 1 - 13 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 test/clangd/diagnostic-category.test diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index 85e14e884..0a0c1ead0 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -84,6 +84,8 @@ void ClangdLSPServer::onInitialize(InitializeParams &Params) { Params.capabilities.textDocument.completion.completionItem.snippetSupport; DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.textDocument.publishDiagnostics.clangdFixSupport; + DiagOpts.SendDiagnosticCategory = + Params.capabilities.textDocument.publishDiagnostics.categorySupport; if (Params.capabilities.workspace && Params.capabilities.workspace->symbol && Params.capabilities.workspace->symbol->symbolKind) { @@ -506,7 +508,7 @@ void ClangdLSPServer::onDiagnosticsReady(PathRef File, } LSPDiag["clangd_fixes"] = std::move(ClangdFixes); } - if (!Diag.category.empty()) + if (DiagOpts.SendDiagnosticCategory && !Diag.category.empty()) LSPDiag["category"] = Diag.category; DiagnosticsJSON.push_back(std::move(LSPDiag)); diff --git a/clangd/Diagnostics.h b/clangd/Diagnostics.h index 89612c907..0cdaeac16 100644 --- a/clangd/Diagnostics.h +++ b/clangd/Diagnostics.h @@ -27,6 +27,12 @@ struct ClangdDiagnosticOptions { /// If true, Clangd uses an LSP extension to embed the fixes with the /// diagnostics that are sent to the client. bool EmbedFixesInDiagnostics = false; + + /// If true, Clangd uses an LSP extension to send the diagnostic's + /// category to the client. The category typically describes the compilation + /// stage during which the issue was produced, e.g. "Semantic Issue" or "Parse + /// Issue". + bool SendDiagnosticCategory = false; }; /// Contains basic information about a diagnostic. diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp index 698bb0489..3f56056e4 100644 --- a/clangd/Protocol.cpp +++ b/clangd/Protocol.cpp @@ -184,6 +184,7 @@ bool fromJSON(const llvm::json::Value &Params, if (!O) return false; O.map("clangdFixSupport", R.clangdFixSupport); + O.map("categorySupport", R.categorySupport); return true; } diff --git a/clangd/Protocol.h b/clangd/Protocol.h index 1de5ced82..fc6a3a8c9 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -255,6 +255,10 @@ struct PublishDiagnosticsClientCapabilities { /// Whether the client accepts diagnostics with fixes attached using the /// "clangd_fixes" extension. bool clangdFixSupport = false; + + /// Whether the client accepts diagnostics with category attached to it + /// using the "category" extension. + bool categorySupport = false; }; bool fromJSON(const llvm::json::Value &, PublishDiagnosticsClientCapabilities &); diff --git a/test/clangd/compile-commands-path-in-initialize.test b/test/clangd/compile-commands-path-in-initialize.test index 17b4333ae..b34c59525 100644 --- a/test/clangd/compile-commands-path-in-initialize.test +++ b/test/clangd/compile-commands-path-in-initialize.test @@ -23,7 +23,6 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "category": "#pragma message Directive", # CHECK-NEXT: "message": "MACRO is one", --- {"jsonrpc":"2.0","id":0,"method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabasePath":"INPUT_DIR/build-2"}}} @@ -31,7 +30,6 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "category": "#pragma message Directive", # CHECK-NEXT: "message": "MACRO is two", --- {"jsonrpc":"2.0","id":10000,"method":"shutdown"} diff --git a/test/clangd/compile-commands-path.test b/test/clangd/compile-commands-path.test index 693101ccd..f25d002f9 100644 --- a/test/clangd/compile-commands-path.test +++ b/test/clangd/compile-commands-path.test @@ -23,7 +23,6 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "category": "#pragma message Directive", # CHECK-NEXT: "message": "MACRO is not defined", --- {"jsonrpc":"2.0","id":0,"method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabasePath":"INPUT_DIR/build-1"}}} @@ -31,7 +30,6 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "category": "#pragma message Directive", # CHECK-NEXT: "message": "MACRO is one", --- {"jsonrpc":"2.0","id":0,"method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabasePath":"INPUT_DIR/build-2"}}} @@ -39,7 +37,6 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "category": "#pragma message Directive", # CHECK-NEXT: "message": "MACRO is two", --- {"jsonrpc":"2.0","id":10000,"method":"shutdown"} diff --git a/test/clangd/diagnostic-category.test b/test/clangd/diagnostic-category.test new file mode 100644 index 000000000..440afbb1e --- /dev/null +++ b/test/clangd/diagnostic-category.test @@ -0,0 +1,43 @@ +# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"publishDiagnostics":{"categorySupport":true}}},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"struct Point {}; union Point p;"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "category": "Semantic Issue", +# CHECK-NEXT: "message": "Use of 'Point' with tag type that does not match previous declaration\n\nfoo.c:1:8: note: previous use is here", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 22, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 17, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 1 +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "message": "Previous use is here\n\nfoo.c:1:18: error: use of 'Point' with tag type that does not match previous declaration", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 12, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 7, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 3 +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c" +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","id":4,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/diagnostics.test b/test/clangd/diagnostics.test index 8a9feab3a..a191c0822 100644 --- a/test/clangd/diagnostics.test +++ b/test/clangd/diagnostics.test @@ -6,7 +6,6 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "message": "Return type of 'main' is not 'int'", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { diff --git a/test/clangd/did-change-configuration-params.test b/test/clangd/did-change-configuration-params.test index d1caf7148..51b4a8747 100644 --- a/test/clangd/did-change-configuration-params.test +++ b/test/clangd/did-change-configuration-params.test @@ -24,7 +24,6 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "message": "Variable 'i' is uninitialized when used here", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { diff --git a/test/clangd/execute-command.test b/test/clangd/execute-command.test index b907f0a9b..492006fdf 100644 --- a/test/clangd/execute-command.test +++ b/test/clangd/execute-command.test @@ -6,7 +6,6 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { diff --git a/test/clangd/extra-flags.test b/test/clangd/extra-flags.test index 8ba3c502e..a7e0ca528 100644 --- a/test/clangd/extra-flags.test +++ b/test/clangd/extra-flags.test @@ -6,7 +6,6 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "message": "Variable 'i' is uninitialized when used here", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { @@ -29,7 +28,6 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "message": "Variable 'i' is uninitialized when used here", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { diff --git a/test/clangd/fixits-embed-in-diagnostic.test b/test/clangd/fixits-embed-in-diagnostic.test index 4d1039521..cd68ea81d 100644 --- a/test/clangd/fixits-embed-in-diagnostic.test +++ b/test/clangd/fixits-embed-in-diagnostic.test @@ -6,7 +6,6 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "clangd_fixes": [ # CHECK-NEXT: { # CHECK-NEXT: "edit": { diff --git a/test/clangd/fixits.test b/test/clangd/fixits.test index f99a5a9ba..ce74d1c8b 100644 --- a/test/clangd/fixits.test +++ b/test/clangd/fixits.test @@ -6,7 +6,6 @@ # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { -# CHECK-NEXT: "category": "Semantic Issue", # CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses", # CHECK-NEXT: "range": { # CHECK-NEXT: "end": { From ffae27862637acc1eb3b1e6e74d2fb3e101029e5 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Thu, 23 Aug 2018 10:25:07 +0000 Subject: [PATCH 081/686] [clangd] Increase the timeouts in TUScheduler tests to 10 seconds. Some of them timeout on our buildbots in certain configurations. The timeouts are there to avoid hanging indefinitely on deadlocks, so the exact number we put there does not matter. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340523 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/TUSchedulerTests.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/unittests/clangd/TUSchedulerTests.cpp b/unittests/clangd/TUSchedulerTests.cpp index 2842d5086..d51500d0e 100644 --- a/unittests/clangd/TUSchedulerTests.cpp +++ b/unittests/clangd/TUSchedulerTests.cpp @@ -278,7 +278,7 @@ TEST_F(TUSchedulerTests, EvictedAST) { // one that the cache will evict. S.update(Foo, getInputs(Foo, SourceContents), WantDiagnostics::Yes, [&BuiltASTCounter](std::vector Diags) { ++BuiltASTCounter; }); - ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(1))); + ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); ASSERT_EQ(BuiltASTCounter.load(), 1); // Build two more files. Since we can retain only 2 ASTs, these should be the @@ -287,7 +287,7 @@ TEST_F(TUSchedulerTests, EvictedAST) { [&BuiltASTCounter](std::vector Diags) { ++BuiltASTCounter; }); S.update(Baz, getInputs(Baz, SourceContents), WantDiagnostics::Yes, [&BuiltASTCounter](std::vector Diags) { ++BuiltASTCounter; }); - ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(1))); + ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); ASSERT_EQ(BuiltASTCounter.load(), 3); // Check only the last two ASTs are retained. @@ -296,7 +296,7 @@ TEST_F(TUSchedulerTests, EvictedAST) { // Access the old file again. S.update(Foo, getInputs(Foo, OtherSourceContents), WantDiagnostics::Yes, [&BuiltASTCounter](std::vector Diags) { ++BuiltASTCounter; }); - ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(1))); + ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); ASSERT_EQ(BuiltASTCounter.load(), 4); // Check the AST for foo.cpp is retained now and one of the others got @@ -333,13 +333,13 @@ TEST_F(TUSchedulerTests, EmptyPreamble) { 0u); }); // Wait for the preamble is being built. - ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(1))); + ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); // Update the file which results in an empty preamble. S.update(Foo, getInputs(Foo, WithEmptyPreamble), WantDiagnostics::Auto, [](std::vector) {}); // Wait for the preamble is being built. - ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(1))); + ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); S.runWithPreamble("getEmptyPreamble", Foo, [&](llvm::Expected Preamble) { // We expect to get an empty preamble. @@ -409,7 +409,7 @@ TEST_F(TUSchedulerTests, NoopOnEmptyChanges) { Updated = false; S.update(Source, std::move(Inputs), WantDiagnostics::Yes, [&Updated](std::vector) { Updated = true; }); - bool UpdateFinished = S.blockUntilIdle(timeoutSeconds(1)); + bool UpdateFinished = S.blockUntilIdle(timeoutSeconds(10)); if (!UpdateFinished) ADD_FAILURE() << "Updated has not finished in one second. Threading bug?"; return Updated; @@ -454,14 +454,14 @@ TEST_F(TUSchedulerTests, NoChangeDiags) { // Make sure the AST was actually built. cantFail(std::move(IA)); }); - ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(1))); + ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); // Even though the inputs didn't change and AST can be reused, we need to // report the diagnostics, as they were not reported previously. std::atomic SeenDiags(false); S.update(FooCpp, getInputs(FooCpp, Contents), WantDiagnostics::Auto, [&](std::vector) { SeenDiags = true; }); - ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(1))); + ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); ASSERT_TRUE(SeenDiags); // Subsequent request does not get any diagnostics callback because the same @@ -469,7 +469,7 @@ TEST_F(TUSchedulerTests, NoChangeDiags) { S.update( FooCpp, getInputs(FooCpp, Contents), WantDiagnostics::Auto, [&](std::vector) { ADD_FAILURE() << "Should not be called."; }); - ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(1))); + ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); } } // namespace From 203f435e7956f246fc77cb714ef9d0c45ffe939d Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Thu, 23 Aug 2018 12:19:39 +0000 Subject: [PATCH 082/686] [clangd] Move function argument snippet disable mechanism from LSP rendering to internal clangd reprensentation. Summary: We were handling the EnableFunctionArgSnippets only when we are producing LSP response. Move that code into CompletionItem generation so that internal clients can benefit from that as well. Reviewers: ilya-biryukov, ioeric, hokein Reviewed By: ilya-biryukov Subscribers: MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D51102 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340527 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 31 +++++++------ unittests/clangd/CodeCompleteTests.cpp | 64 +++++++++----------------- 2 files changed, 38 insertions(+), 57 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index df438b483..2fe63a760 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -269,7 +269,8 @@ struct CodeCompletionBuilder { CodeCompletionString *SemaCCS, const IncludeInserter &Includes, StringRef FileName, const CodeCompleteOptions &Opts) - : ASTCtx(ASTCtx), ExtractDocumentation(Opts.IncludeComments) { + : ASTCtx(ASTCtx), ExtractDocumentation(Opts.IncludeComments), + EnableFunctionArgSnippets(Opts.EnableFunctionArgSnippets) { add(C, SemaCCS); if (C.SemaResult) { Completion.Origin |= SymbolOrigin::AST; @@ -385,10 +386,17 @@ struct CodeCompletionBuilder { } std::string summarizeSnippet() const { - if (auto *Snippet = onlyValue<&BundledEntry::SnippetSuffix>()) - return *Snippet; - // All bundles are function calls. - return "(${0})"; + auto *Snippet = onlyValue<&BundledEntry::SnippetSuffix>(); + if (!Snippet) + // All bundles are function calls. + return "($0)"; + if (!Snippet->empty() && !EnableFunctionArgSnippets && + ((Completion.Kind == CompletionItemKind::Function) || + (Completion.Kind == CompletionItemKind::Method)) && + (Snippet->front() == '(') && (Snippet->back() == ')')) + // Check whether function has any parameters or not. + return Snippet->size() > 2 ? "($0)" : "()"; + return *Snippet; } std::string summarizeSignature() const { @@ -402,6 +410,7 @@ struct CodeCompletionBuilder { CodeCompletion Completion; SmallVector Bundled; bool ExtractDocumentation; + bool EnableFunctionArgSnippets; }; // Determine the symbol ID for a Sema code completion result, if possible. @@ -1413,16 +1422,8 @@ CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const { LSP.additionalTextEdits.push_back(FixIt); } } - if (Opts.EnableSnippets && !SnippetSuffix.empty()) { - if (!Opts.EnableFunctionArgSnippets && - ((Kind == CompletionItemKind::Function) || - (Kind == CompletionItemKind::Method)) && - (SnippetSuffix.front() == '(') && (SnippetSuffix.back() == ')')) - // Check whether function has any parameters or not. - LSP.textEdit->newText += SnippetSuffix.size() > 2 ? "(${0})" : "()"; - else - LSP.textEdit->newText += SnippetSuffix; - } + if (Opts.EnableSnippets) + LSP.textEdit->newText += SnippetSuffix; // FIXME(kadircet): Do not even fill insertText after making sure textEdit is // compatible with most of the editors. diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 3fce86626..32ec23211 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1140,7 +1140,7 @@ TEST(CompletionTest, OverloadBundling) { // For now we just return one of the doc strings arbitrarily. EXPECT_THAT(A.Documentation, AnyOf(HasSubstr("Overload with int"), HasSubstr("Overload with bool"))); - EXPECT_EQ(A.SnippetSuffix, "(${0})"); + EXPECT_EQ(A.SnippetSuffix, "($0)"); } TEST(CompletionTest, DocumentationFromChangedFileCrash) { @@ -1648,55 +1648,35 @@ TEST(SignatureHelpTest, IndexDocumentation) { SigDoc("Doc from sema")))); } -TEST(CompletionTest, RenderWithSnippetsForFunctionArgsDisabled) { +TEST(CompletionTest, CompletionFunctionArgsDisabled) { CodeCompleteOptions Opts; - Opts.EnableFunctionArgSnippets = true; - { - CodeCompletion C; - C.RequiredQualifier = "Foo::"; - C.Name = "x"; - C.SnippetSuffix = "()"; - - auto R = C.render(Opts); - EXPECT_EQ(R.textEdit->newText, "Foo::x"); - EXPECT_EQ(R.insertTextFormat, InsertTextFormat::PlainText); - } - Opts.EnableSnippets = true; Opts.EnableFunctionArgSnippets = false; + const std::string Header = + R"cpp( + void xfoo(); + void xfoo(int x, int y); + void xbar(); + void f() { + )cpp"; { - CodeCompletion C; - C.RequiredQualifier = "Foo::"; - C.Name = "x"; - C.SnippetSuffix = ""; - - auto R = C.render(Opts); - EXPECT_EQ(R.textEdit->newText, "Foo::x"); - EXPECT_EQ(R.insertTextFormat, InsertTextFormat::Snippet); + auto Results = completions(Header + "\nxfo^", {}, Opts); + EXPECT_THAT( + Results.Completions, + UnorderedElementsAre(AllOf(Named("xfoo"), SnippetSuffix("()")), + AllOf(Named("xfoo"), SnippetSuffix("($0)")))); } - { - CodeCompletion C; - C.RequiredQualifier = "Foo::"; - C.Name = "x"; - C.SnippetSuffix = "()"; - C.Kind = CompletionItemKind::Method; - - auto R = C.render(Opts); - EXPECT_EQ(R.textEdit->newText, "Foo::x()"); - EXPECT_EQ(R.insertTextFormat, InsertTextFormat::Snippet); + auto Results = completions(Header + "\nxba^", {}, Opts); + EXPECT_THAT(Results.Completions, UnorderedElementsAre(AllOf( + Named("xbar"), SnippetSuffix("()")))); } - { - CodeCompletion C; - C.RequiredQualifier = "Foo::"; - C.Name = "x"; - C.SnippetSuffix = "(${0:bool})"; - C.Kind = CompletionItemKind::Function; - - auto R = C.render(Opts); - EXPECT_EQ(R.textEdit->newText, "Foo::x(${0})"); - EXPECT_EQ(R.insertTextFormat, InsertTextFormat::Snippet); + Opts.BundleOverloads = true; + auto Results = completions(Header + "\nxfo^", {}, Opts); + EXPECT_THAT( + Results.Completions, + UnorderedElementsAre(AllOf(Named("xfoo"), SnippetSuffix("($0)")))); } } From dfe83d13cfe853c39e0ea17188fc3bc4b2a15e2c Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Thu, 23 Aug 2018 13:14:50 +0000 Subject: [PATCH 083/686] [clangd] Suggest code-completions for overriding base class virtual methods. Summary: Whenever a code-completion is triggered within a class/struct/union looks at base classes and figures out non-overriden virtual functions. Than suggests completions for those. Reviewers: ilya-biryukov, hokein, ioeric Reviewed By: hokein Subscribers: MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50898 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340530 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 109 +++++++++++++++++++++++-- unittests/clangd/CodeCompleteTests.cpp | 25 ++++++ 2 files changed, 125 insertions(+), 9 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 2fe63a760..254c22be3 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -188,6 +188,55 @@ static llvm::Expected toHeaderFile(StringRef Header, return HeaderFile{std::move(*Resolved), /*Verbatim=*/false}; } +// First traverses all method definitions inside current class/struct/union +// definition. Than traverses base classes to find virtual methods that haven't +// been overriden within current context. +// FIXME(kadircet): Currently we cannot see declarations below completion point. +// It is because Sema gets run only upto completion point. Need to find a +// solution to run it for the whole class/struct/union definition. +static std::vector +getNonOverridenMethodCompletionResults(const DeclContext *DC, Sema *S) { + const auto *CR = llvm::dyn_cast(DC); + // If not inside a class/struct/union return empty. + if (!CR) + return {}; + // First store overrides within current class. + // These are stored by name to make querying fast in the later step. + llvm::StringMap> Overrides; + for (auto *Method : CR->methods()) { + if (!Method->isVirtual()) + continue; + Overrides[Method->getName()].push_back(Method); + } + + std::vector Results; + for (const auto &Base : CR->bases()) { + const auto *BR = Base.getType().getTypePtr()->getAsCXXRecordDecl(); + if (!BR) + continue; + for (auto *Method : BR->methods()) { + if (!Method->isVirtual()) + continue; + const auto it = Overrides.find(Method->getName()); + bool IsOverriden = false; + if (it != Overrides.end()) { + for (auto *MD : it->second) { + // If the method in current body is not an overload of this virtual + // function, that it overrides this one. + if (!S->IsOverload(MD, Method, false)) { + IsOverriden = true; + break; + } + } + } + if (!IsOverriden) + Results.emplace_back(Method, 0); + } + } + + return Results; +} + /// A code completion result, in clang-native form. /// It may be promoted to a CompletionItem if it's among the top-ranked results. struct CompletionCandidate { @@ -196,6 +245,9 @@ struct CompletionCandidate { const CodeCompletionResult *SemaResult = nullptr; const Symbol *IndexResult = nullptr; + // States whether this item is an override suggestion. + bool IsOverride = false; + // Returns a token identifying the overload set this is part of. // 0 indicates it's not part of any overload set. size_t overloadSet() const { @@ -352,6 +404,8 @@ struct CodeCompletionBuilder { Completion.Documentation = getDocComment(ASTCtx, *C.SemaResult, /*CommentsFromHeader=*/false); } + if (C.IsOverride) + S.OverrideSuffix = true; } CodeCompletion build() { @@ -359,6 +413,12 @@ struct CodeCompletionBuilder { Completion.Signature = summarizeSignature(); Completion.SnippetSuffix = summarizeSnippet(); Completion.BundleSize = Bundled.size(); + if (summarizeOverride()) { + Completion.Name = Completion.ReturnType + ' ' + + std::move(Completion.Name) + + std::move(Completion.Signature) + " override"; + Completion.Signature.clear(); + } return std::move(Completion); } @@ -367,6 +427,7 @@ struct CodeCompletionBuilder { std::string SnippetSuffix; std::string Signature; std::string ReturnType; + bool OverrideSuffix; }; // If all BundledEntrys have the same value for a property, return it. @@ -379,6 +440,14 @@ struct CodeCompletionBuilder { return &(B->*Member); } + template const bool *onlyValue() const { + auto B = Bundled.begin(), E = Bundled.end(); + for (auto I = B + 1; I != E; ++I) + if (I->*Member != B->*Member) + return nullptr; + return &(B->*Member); + } + std::string summarizeReturnType() const { if (auto *RT = onlyValue<&BundledEntry::ReturnType>()) return *RT; @@ -406,6 +475,12 @@ struct CodeCompletionBuilder { return "(…)"; } + bool summarizeOverride() const { + if (auto *OverrideSuffix = onlyValue<&BundledEntry::OverrideSuffix>()) + return *OverrideSuffix; + return false; + } + ASTContext &ASTCtx; CodeCompletion Completion; SmallVector Bundled; @@ -1181,10 +1256,14 @@ class CodeCompleteFlow { auto IndexResults = (Opts.Index && allowIndex(Recorder->CCContext)) ? queryIndex() : SymbolSlab(); - // Merge Sema and Index results, score them, and pick the winners. - auto Top = mergeResults(Recorder->Results, IndexResults); - // Convert the results to final form, assembling the expensive strings. + // Merge Sema, Index and Override results, score them, and pick the + // winners. + const auto Overrides = getNonOverridenMethodCompletionResults( + Recorder->CCSema->CurContext, Recorder->CCSema); + auto Top = mergeResults(Recorder->Results, IndexResults, Overrides); CodeCompleteResult Output; + + // Convert the results to final form, assembling the expensive strings. for (auto &C : Top) { Output.Completions.push_back(toCodeCompletion(C.first)); Output.Completions.back().Score = C.second; @@ -1192,6 +1271,7 @@ class CodeCompleteFlow { } Output.HasMore = Incomplete; Output.Context = Recorder->CCContext.getKind(); + return Output; } @@ -1218,20 +1298,24 @@ class CodeCompleteFlow { return std::move(ResultsBuilder).build(); } - // Merges Sema and Index results where possible, to form CompletionCandidates. - // Groups overloads if desired, to form CompletionCandidate::Bundles. - // The bundles are scored and top results are returned, best to worst. + // Merges Sema, Index and Override results where possible, to form + // CompletionCandidates. Groups overloads if desired, to form + // CompletionCandidate::Bundles. The bundles are scored and top results are + // returned, best to worst. std::vector - mergeResults(const std::vector &SemaResults, - const SymbolSlab &IndexResults) { + mergeResults(const std::vector &SemaResults, + const SymbolSlab &IndexResults, + const std::vector &OverrideResults) { trace::Span Tracer("Merge and score results"); std::vector Bundles; llvm::DenseMap BundleLookup; auto AddToBundles = [&](const CodeCompletionResult *SemaResult, - const Symbol *IndexResult) { + const Symbol *IndexResult, + bool IsOverride = false) { CompletionCandidate C; C.SemaResult = SemaResult; C.IndexResult = IndexResult; + C.IsOverride = IsOverride; C.Name = IndexResult ? IndexResult->Name : Recorder->getName(*SemaResult); if (auto OverloadSet = Opts.BundleOverloads ? C.overloadSet() : 0) { auto Ret = BundleLookup.try_emplace(OverloadSet, Bundles.size()); @@ -1258,6 +1342,13 @@ class CodeCompleteFlow { // Emit all Sema results, merging them with Index results if possible. for (auto &SemaResult : Recorder->Results) AddToBundles(&SemaResult, CorrespondingIndexResult(SemaResult)); + // Handle OverrideResults the same way we deal with SemaResults. Since these + // results use the same structs as a SemaResult it is safe to do that, but + // we need to make sure we dont' duplicate things in future if Sema starts + // to provide them as well. + for (auto &OverrideResult : OverrideResults) + AddToBundles(&OverrideResult, CorrespondingIndexResult(OverrideResult), + true); // Now emit any Index-only results. for (const auto &IndexResult : IndexResults) { if (UsedIndexResults.count(&IndexResult)) diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 32ec23211..8f7f85377 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1680,6 +1680,31 @@ TEST(CompletionTest, CompletionFunctionArgsDisabled) { } } +TEST(CompletionTest, SuggestOverrides) { + constexpr const char *const Text(R"cpp( + class A { + public: + virtual void vfunc(bool param); + virtual void vfunc(bool param, int p); + void func(bool param); + }; + class B : public A { + virtual void ttt(bool param) const; + void vfunc(bool param, int p) override; + }; + class C : public B { + public: + void vfunc(bool param) override; + ^ + }; + )cpp"); + const auto Results = completions(Text); + EXPECT_THAT(Results.Completions, + AllOf(Contains(Labeled("void vfunc(bool param, int p) override")), + Contains(Labeled("void ttt(bool param) const override")), + Not(Contains(Labeled("void vfunc(bool param) override"))))); +} + } // namespace } // namespace clangd } // namespace clang From 7cf3952bdf23e216c940723bdc15105605bdcb1b Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Thu, 23 Aug 2018 15:55:27 +0000 Subject: [PATCH 084/686] [clangd] Check for include overlapping looks for only the line now. Summary: Currently we match an include only if we are inside filename, with this patch we will match whenever we are on the starting line of the include. Reviewers: ilya-biryukov, hokein, ioeric Reviewed By: ilya-biryukov Subscribers: MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D51163 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340539 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/XRefs.cpp | 2 +- unittests/clangd/XRefsTests.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index a79a8d119..97fee3af4 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -211,7 +211,7 @@ std::vector findDefinitions(ParsedAST &AST, Position Pos, std::vector Result; // Handle goto definition for #include. for (auto &Inc : AST.getIncludeStructure().MainFileIncludes) { - if (!Inc.Resolved.empty() && Inc.R.contains(Pos)) + if (!Inc.Resolved.empty() && Inc.R.start.line == Pos.line) Result.push_back(Location{URIForFile{Inc.Resolved}, {}}); } if (!Result.empty()) diff --git a/unittests/clangd/XRefsTests.cpp b/unittests/clangd/XRefsTests.cpp index 2558cdefa..47af72561 100644 --- a/unittests/clangd/XRefsTests.cpp +++ b/unittests/clangd/XRefsTests.cpp @@ -1014,11 +1014,13 @@ TEST(GoToInclude, All) { Locations = runFindDefinitions(Server, FooCpp, SourceAnnotations.point("5")); ASSERT_TRUE(bool(Locations)) << "findDefinitions returned an error"; - EXPECT_THAT(*Locations, IsEmpty()); + EXPECT_THAT(*Locations, + ElementsAre(Location{FooHUri, HeaderAnnotations.range()})); Locations = runFindDefinitions(Server, FooCpp, SourceAnnotations.point("7")); ASSERT_TRUE(bool(Locations)) << "findDefinitions returned an error"; - EXPECT_THAT(*Locations, IsEmpty()); + EXPECT_THAT(*Locations, + ElementsAre(Location{FooHUri, HeaderAnnotations.range()})); } TEST(GoToDefinition, WithPreamble) { From 24b7f217d16ee70813283c1562066a7639f6a892 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Fri, 24 Aug 2018 09:03:54 +0000 Subject: [PATCH 085/686] [clangd] Allow to merge symbols on-the-fly in global-symbol-builder Summary: The new mode avoids serializing and deserializing YAML. This results in better performance and less memory usage. Reduce phase is now almost instant. The default is to use the old mode going through YAML serialization to allow migrating MapReduce clients that require the old mode to operate properly. After we migrate the clients, we can switch the default to the new mode. Reviewers: hokein, ioeric, kbobyrev, sammccall Reviewed By: ioeric Subscribers: MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51155 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340600 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../GlobalSymbolBuilderMain.cpp | 143 +++++++++++++----- 1 file changed, 108 insertions(+), 35 deletions(-) diff --git a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp b/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp index a9f1f98c7..2437839bd 100644 --- a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp +++ b/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp @@ -49,9 +49,33 @@ static llvm::cl::opt AssumedHeaderDir( "not given, such headers will have relative paths."), llvm::cl::init("")); +static llvm::cl::opt MergeOnTheFly( + "merge-on-the-fly", + llvm::cl::desc( + "Merges symbols for each processed translation unit as soon " + "they become available. This results in a smaller memory " + "usage and an almost instant reduce stage. Optimal for running as a " + "standalone tool, but cannot be used with multi-process executors like " + "MapReduce."), + llvm::cl::init(true), llvm::cl::Hidden); + +/// Responsible for aggregating symbols from each processed file and producing +/// the final results. All methods in this class must be thread-safe, +/// 'consumeSymbols' may be called from multiple threads. +class SymbolsConsumer { +public: + virtual ~SymbolsConsumer() = default; + + /// Consume a SymbolSlab build for a file. + virtual void consumeSymbols(SymbolSlab Symbols) = 0; + /// Produce a resulting symbol slab, by combining occurrences of the same + /// symbols across translation units. + virtual SymbolSlab mergeResults() = 0; +}; + class SymbolIndexActionFactory : public tooling::FrontendActionFactory { public: - SymbolIndexActionFactory(tooling::ExecutionContext *Ctx) : Ctx(Ctx) {} + SymbolIndexActionFactory(SymbolsConsumer &Consumer) : Consumer(Consumer) {} clang::FrontendAction *create() override { // Wraps the index action and reports collected symbols to the execution @@ -61,10 +85,10 @@ class SymbolIndexActionFactory : public tooling::FrontendActionFactory { WrappedIndexAction(std::shared_ptr C, std::unique_ptr Includes, const index::IndexingOptions &Opts, - tooling::ExecutionContext *Ctx) + SymbolsConsumer &Consumer) : WrapperFrontendAction( index::createIndexingAction(C, Opts, nullptr)), - Ctx(Ctx), Collector(C), Includes(std::move(Includes)), + Consumer(Consumer), Collector(C), Includes(std::move(Includes)), PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {} std::unique_ptr @@ -91,14 +115,11 @@ class SymbolIndexActionFactory : public tooling::FrontendActionFactory { return; } - auto Symbols = Collector->takeSymbols(); - for (const auto &Sym : Symbols) { - Ctx->reportResult(Sym.ID.str(), SymbolToYAML(Sym)); - } + Consumer.consumeSymbols(Collector->takeSymbols()); } private: - tooling::ExecutionContext *Ctx; + SymbolsConsumer &Consumer; std::shared_ptr Collector; std::unique_ptr Includes; std::unique_ptr PragmaHandler; @@ -118,30 +139,72 @@ class SymbolIndexActionFactory : public tooling::FrontendActionFactory { CollectorOpts.Includes = Includes.get(); return new WrappedIndexAction( std::make_shared(std::move(CollectorOpts)), - std::move(Includes), IndexOpts, Ctx); + std::move(Includes), IndexOpts, Consumer); } - tooling::ExecutionContext *Ctx; + SymbolsConsumer &Consumer; }; -// Combine occurrences of the same symbol across translation units. -SymbolSlab mergeSymbols(tooling::ToolResults *Results) { - SymbolSlab::Builder UniqueSymbols; - llvm::BumpPtrAllocator Arena; - Symbol::Details Scratch; - Results->forEachResult([&](llvm::StringRef Key, llvm::StringRef Value) { - Arena.Reset(); - llvm::yaml::Input Yin(Value, &Arena); - auto Sym = clang::clangd::SymbolFromYAML(Yin, Arena); - clang::clangd::SymbolID ID; - Key >> ID; - if (const auto *Existing = UniqueSymbols.find(ID)) - UniqueSymbols.insert(mergeSymbol(*Existing, Sym, &Scratch)); - else - UniqueSymbols.insert(Sym); - }); - return std::move(UniqueSymbols).build(); -} +/// Stashes per-file results inside ExecutionContext, merges all of them at the +/// end. Useful for running on MapReduce infrastructure to avoid keeping symbols +/// from multiple files in memory. +class ToolExecutorConsumer : public SymbolsConsumer { +public: + ToolExecutorConsumer(ToolExecutor &Executor) : Executor(Executor) {} + + void consumeSymbols(SymbolSlab Symbols) override { + for (const auto &Sym : Symbols) + Executor.getExecutionContext()->reportResult(Sym.ID.str(), + SymbolToYAML(Sym)); + } + + SymbolSlab mergeResults() override { + SymbolSlab::Builder UniqueSymbols; + llvm::BumpPtrAllocator Arena; + Symbol::Details Scratch; + Executor.getToolResults()->forEachResult( + [&](llvm::StringRef Key, llvm::StringRef Value) { + Arena.Reset(); + llvm::yaml::Input Yin(Value, &Arena); + auto Sym = clang::clangd::SymbolFromYAML(Yin, Arena); + clang::clangd::SymbolID ID; + Key >> ID; + if (const auto *Existing = UniqueSymbols.find(ID)) + UniqueSymbols.insert(mergeSymbol(*Existing, Sym, &Scratch)); + else + UniqueSymbols.insert(Sym); + }); + return std::move(UniqueSymbols).build(); + } + +private: + ToolExecutor &Executor; +}; + +/// Merges symbols for each translation unit as soon as the file is processed. +/// Optimal choice for standalone tools. +class OnTheFlyConsumer : public SymbolsConsumer { +public: + void consumeSymbols(SymbolSlab Symbols) override { + std::lock_guard Lock(Mut); + for (auto &&Sym : Symbols) { + Symbol::Details Scratch; + if (const auto *Existing = Result.find(Sym.ID)) + Result.insert(mergeSymbol(*Existing, Sym, &Scratch)); + else + Result.insert(Sym); + } + } + + SymbolSlab mergeResults() override { + std::lock_guard Lock(Mut); + return std::move(Result).build(); + } + +private: + std::mutex Mut; + SymbolSlab::Builder Result; +}; } // namespace } // namespace clangd @@ -181,18 +244,28 @@ int main(int argc, const char **argv) { return 1; } + if (clang::clangd::MergeOnTheFly && !Executor->get()->isSingleProcess()) { + llvm::errs() + << "Found multi-process executor, forcing the use of intermediate YAML " + "serialization instead of the on-the-fly merge.\n"; + clang::clangd::MergeOnTheFly = false; + } + + std::unique_ptr Consumer; + if (clang::clangd::MergeOnTheFly) + Consumer = llvm::make_unique(); + else + Consumer = + llvm::make_unique(**Executor); + // Map phase: emit symbols found in each translation unit. auto Err = Executor->get()->execute( - llvm::make_unique( - Executor->get()->getExecutionContext())); + llvm::make_unique(*Consumer)); if (Err) { llvm::errs() << llvm::toString(std::move(Err)) << "\n"; } - - // Reduce phase: combine symbols using the ID as a key. - auto UniqueSymbols = - clang::clangd::mergeSymbols(Executor->get()->getToolResults()); - + // Reduce phase: combine symbols with the same IDs. + auto UniqueSymbols = Consumer->mergeResults(); // Output phase: emit YAML for result symbols. SymbolsToYAML(UniqueSymbols, llvm::outs()); return 0; From 29c8c28beaa0d894c039bf76843f70a5bd61fc99 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Fri, 24 Aug 2018 09:12:54 +0000 Subject: [PATCH 086/686] [clangd] Log memory usage of DexIndex and MemIndex This patch prints information about built index size estimation to verbose logs. This is useful for optimizing memory usage of DexIndex and comparisons with MemIndex. Reviewed by: sammccall Differential Revision: https://reviews.llvm.org/D51154 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340601 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/FileIndex.cpp | 4 ++++ clangd/index/FileIndex.h | 3 +++ clangd/index/Index.h | 6 ++++++ clangd/index/MemIndex.cpp | 8 ++++++++ clangd/index/MemIndex.h | 3 +++ clangd/index/Merge.cpp | 4 ++++ clangd/index/dex/DexIndex.cpp | 17 +++++++++++++++++ clangd/index/dex/DexIndex.h | 3 +++ unittests/clangd/CodeCompleteTests.cpp | 4 ++++ 9 files changed, 52 insertions(+) diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 3d8e17bfb..48d05959d 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -118,5 +118,9 @@ void FileIndex::findOccurrences( log("findOccurrences is not implemented."); } +size_t FileIndex::estimateMemoryUsage() const { + return Index.estimateMemoryUsage(); +} + } // namespace clangd } // namespace clang diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index 6f24f9d2c..58fad2f62 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -81,6 +81,9 @@ class FileIndex : public SymbolIndex { void findOccurrences(const OccurrencesRequest &Req, llvm::function_ref Callback) const override; + + size_t estimateMemoryUsage() const override; + private: FileSymbols FSymbols; MemIndex Index; diff --git a/clangd/index/Index.h b/clangd/index/Index.h index eac4c5e7d..95b081ace 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -385,6 +385,12 @@ class SymbolIndex { virtual void findOccurrences( const OccurrencesRequest &Req, llvm::function_ref Callback) const = 0; + + /// Returns estimated size of index (in bytes). + // FIXME(kbobyrev): Currently, this only returns the size of index itself + // excluding the size of actual symbol slab index refers to. We should include + // both. + virtual size_t estimateMemoryUsage() const = 0; }; } // namespace clangd diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index db11244d8..19a64ad95 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -26,6 +26,9 @@ void MemIndex::build(std::shared_ptr> Syms) { Index = std::move(TempIndex); Symbols = std::move(Syms); // Relase old symbols. } + + vlog("Built MemIndex with estimated memory usage {0} bytes.", + estimateMemoryUsage()); } std::unique_ptr MemIndex::build(SymbolSlab Slab) { @@ -98,5 +101,10 @@ getSymbolsFromSlab(SymbolSlab Slab) { &Snap->Pointers); } +size_t MemIndex::estimateMemoryUsage() const { + std::lock_guard Lock(Mutex); + return Index.getMemorySize(); +} + } // namespace clangd } // namespace clang diff --git a/clangd/index/MemIndex.h b/clangd/index/MemIndex.h index 8b12f8c79..feca87af5 100644 --- a/clangd/index/MemIndex.h +++ b/clangd/index/MemIndex.h @@ -39,7 +39,10 @@ class MemIndex : public SymbolIndex { llvm::function_ref Callback) const override; + size_t estimateMemoryUsage() const override; + private: + std::shared_ptr> Symbols; // Index is a set of symbols that are deduplicated by symbol IDs. // FIXME: build smarter index structure. diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index 4834bfba6..84928ca1a 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -84,6 +84,10 @@ class MergedIndex : public SymbolIndex { log("findOccurrences is not implemented."); } + size_t estimateMemoryUsage() const override { + return Dynamic->estimateMemoryUsage() + Static->estimateMemoryUsage(); + } + private: const SymbolIndex *Dynamic, *Static; }; diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/DexIndex.cpp index 534f51e0c..9280cc8fd 100644 --- a/clangd/index/dex/DexIndex.cpp +++ b/clangd/index/dex/DexIndex.cpp @@ -67,6 +67,9 @@ void DexIndex::build(std::shared_ptr> Syms) { InvertedIndex = std::move(TempInvertedIndex); SymbolQuality = std::move(TempSymbolQuality); } + + vlog("Built DexIndex with estimated memory usage {0} bytes.", + estimateMemoryUsage()); } std::unique_ptr DexIndex::build(SymbolSlab Slab) { @@ -171,6 +174,20 @@ void DexIndex::findOccurrences( log("findOccurrences is not implemented."); } +size_t DexIndex::estimateMemoryUsage() const { + std::lock_guard Lock(Mutex); + + size_t Bytes = + LookupTable.size() * sizeof(std::pair); + Bytes += SymbolQuality.size() * sizeof(std::pair); + Bytes += InvertedIndex.size() * sizeof(Token); + + for (const auto &P : InvertedIndex) { + Bytes += P.second.size() * sizeof(DocID); + } + return Bytes; +} + } // namespace dex } // namespace clangd } // namespace clang diff --git a/clangd/index/dex/DexIndex.h b/clangd/index/dex/DexIndex.h index 4485150b1..d9b2cd143 100644 --- a/clangd/index/dex/DexIndex.h +++ b/clangd/index/dex/DexIndex.h @@ -57,7 +57,10 @@ class DexIndex : public SymbolIndex { llvm::function_ref Callback) const override; + size_t estimateMemoryUsage() const override; + private: + mutable std::mutex Mutex; std::shared_ptr> Symbols /*GUARDED_BY(Mutex)*/; diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 8f7f85377..3f855844a 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -923,6 +923,10 @@ class IndexRequestCollector : public SymbolIndex { llvm::function_ref Callback) const override {} + // This is incorrect, but IndexRequestCollector is not an actual index and it + // isn't used in production code. + size_t estimateMemoryUsage() const override { return 0; } + const std::vector allRequests() const { return Requests; } private: From 052f22f5e94eae1e01d24349684b11f6763907eb Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Fri, 24 Aug 2018 11:23:56 +0000 Subject: [PATCH 087/686] [clangd] Speculative code completion index request before Sema is run. Summary: For index-based code completion, send an asynchronous speculative index request, based on the index request for the last code completion on the same file and the filter text typed before the cursor, before sema code completion is invoked. This can reduce the code completion latency (by roughly latency of sema code completion) if the speculative request is the same as the one generated for the ongoing code completion from sema. As a sequence of code completions often have the same scopes and proximity paths etc, this should be effective for a number of code completions. Trace with speculative index request:{F6997544} Reviewers: hokein, ilya-biryukov Reviewed By: ilya-biryukov Subscribers: javed.absar, jfb, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D50962 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340604 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 36 +++++++-- clangd/ClangdServer.h | 7 ++ clangd/CodeComplete.cpp | 107 ++++++++++++++++++++++--- clangd/CodeComplete.h | 43 +++++++++- clangd/TUScheduler.h | 13 +++ clangd/index/Index.h | 9 +++ clangd/tool/ClangdMain.cpp | 1 + unittests/clangd/CodeCompleteTests.cpp | 77 +++++++++++++++++- 8 files changed, 274 insertions(+), 19 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 0a3a26c82..788b58294 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -12,6 +12,7 @@ #include "FindSymbols.h" #include "Headers.h" #include "SourceCode.h" +#include "Trace.h" #include "XRefs.h" #include "index/Merge.h" #include "clang/Format/Format.h" @@ -22,6 +23,7 @@ #include "clang/Tooling/Refactoring/RefactoringResultConsumer.h" #include "clang/Tooling/Refactoring/Rename/RenamingAction.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Errc.h" @@ -29,6 +31,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include +#include using namespace clang; using namespace clang::clangd; @@ -181,21 +184,44 @@ void ClangdServer::codeComplete(PathRef File, Position Pos, // Copy PCHs to avoid accessing this->PCHs concurrently std::shared_ptr PCHs = this->PCHs; auto FS = FSProvider.getFileSystem(); - auto Task = [PCHs, Pos, FS, - CodeCompleteOpts](Path File, Callback CB, - llvm::Expected IP) { + + auto Task = [PCHs, Pos, FS, CodeCompleteOpts, + this](Path File, Callback CB, + llvm::Expected IP) { if (!IP) return CB(IP.takeError()); auto PreambleData = IP->Preamble; + llvm::Optional SpecFuzzyFind; + if (CodeCompleteOpts.Index && CodeCompleteOpts.SpeculativeIndexRequest) { + SpecFuzzyFind.emplace(); + { + std::lock_guard Lock(CachedCompletionFuzzyFindRequestMutex); + SpecFuzzyFind->CachedReq = CachedCompletionFuzzyFindRequestByFile[File]; + } + } + // FIXME(ibiryukov): even if Preamble is non-null, we may want to check // both the old and the new version in case only one of them matches. CodeCompleteResult Result = clangd::codeComplete( File, IP->Command, PreambleData ? &PreambleData->Preamble : nullptr, PreambleData ? PreambleData->Includes : IncludeStructure(), - IP->Contents, Pos, FS, PCHs, CodeCompleteOpts); - CB(std::move(Result)); + IP->Contents, Pos, FS, PCHs, CodeCompleteOpts, + SpecFuzzyFind ? SpecFuzzyFind.getPointer() : nullptr); + { + clang::clangd::trace::Span Tracer("Completion results callback"); + CB(std::move(Result)); + } + if (SpecFuzzyFind && SpecFuzzyFind->NewReq.hasValue()) { + std::lock_guard Lock(CachedCompletionFuzzyFindRequestMutex); + CachedCompletionFuzzyFindRequestByFile[File] = + SpecFuzzyFind->NewReq.getValue(); + } + // SpecFuzzyFind is only destroyed after speculative fuzzy find finishes. + // We don't want `codeComplete` to wait for the async call if it doesn't use + // the result (e.g. non-index completion, speculation fails), so that `CB` + // is called as soon as results are available. }; WorkScheduler.runWithPreamble("CodeComplete", File, diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h index 561355381..8bb5fdd65 100644 --- a/clangd/ClangdServer.h +++ b/clangd/ClangdServer.h @@ -18,6 +18,7 @@ #include "Protocol.h" #include "TUScheduler.h" #include "index/FileIndex.h" +#include "index/Index.h" #include "clang/Tooling/CompilationDatabase.h" #include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" @@ -228,6 +229,12 @@ class ClangdServer { // If present, a merged view of DynamicIdx and an external index. Read via // Index. std::unique_ptr MergedIndex; + + // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex) + llvm::StringMap> + CachedCompletionFuzzyFindRequestByFile; + mutable std::mutex CachedCompletionFuzzyFindRequestMutex; + // If set, this represents the workspace path. llvm::Optional RootPath; std::shared_ptr PCHs; diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 254c22be3..65c415fba 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -29,6 +29,7 @@ #include "Logger.h" #include "Quality.h" #include "SourceCode.h" +#include "TUScheduler.h" #include "Trace.h" #include "URI.h" #include "index/Index.h" @@ -42,6 +43,8 @@ #include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Sema/Sema.h" #include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/ScopedPrinter.h" @@ -1077,6 +1080,32 @@ bool allowIndex(CodeCompletionContext &CC) { llvm_unreachable("invalid NestedNameSpecifier kind"); } +std::future startAsyncFuzzyFind(const SymbolIndex &Index, + const FuzzyFindRequest &Req) { + return runAsync([&Index, Req]() { + trace::Span Tracer("Async fuzzyFind"); + SymbolSlab::Builder Syms; + Index.fuzzyFind(Req, [&Syms](const Symbol &Sym) { Syms.insert(Sym); }); + return std::move(Syms).build(); + }); +} + +// Creates a `FuzzyFindRequest` based on the cached index request from the +// last completion, if any, and the speculated completion filter text in the +// source code. +llvm::Optional speculativeFuzzyFindRequestForCompletion( + FuzzyFindRequest CachedReq, PathRef File, StringRef Content, Position Pos) { + auto Filter = speculateCompletionFilter(Content, Pos); + if (!Filter) { + elog("Failed to speculate filter text for code completion at Pos " + "{0}:{1}: {2}", + Pos.line, Pos.character, Filter.takeError()); + return llvm::None; + } + CachedReq.Query = *Filter; + return CachedReq; +} + } // namespace clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const { @@ -1131,7 +1160,9 @@ clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const { class CodeCompleteFlow { PathRef FileName; IncludeStructure Includes; // Complete once the compiler runs. + SpeculativeFuzzyFind *SpecFuzzyFind; // Can be nullptr. const CodeCompleteOptions &Opts; + // Sema takes ownership of Recorder. Recorder is valid until Sema cleanup. CompletionRecorder *Recorder = nullptr; int NSema = 0, NIndex = 0, NBoth = 0; // Counters for logging. @@ -1142,15 +1173,29 @@ class CodeCompleteFlow { // This is available after Sema has run. llvm::Optional Inserter; // Available during runWithSema. llvm::Optional FileProximity; // Initialized once Sema runs. + /// Speculative request based on the cached request and the filter text before + /// the cursor. + /// Initialized right before sema run. This is only set if `SpecFuzzyFind` is + /// set and contains a cached request. + llvm::Optional SpecReq; public: // A CodeCompleteFlow object is only useful for calling run() exactly once. CodeCompleteFlow(PathRef FileName, const IncludeStructure &Includes, + SpeculativeFuzzyFind *SpecFuzzyFind, const CodeCompleteOptions &Opts) - : FileName(FileName), Includes(Includes), Opts(Opts) {} + : FileName(FileName), Includes(Includes), SpecFuzzyFind(SpecFuzzyFind), + Opts(Opts) {} CodeCompleteResult run(const SemaCompleteInput &SemaCCInput) && { trace::Span Tracer("CodeCompleteFlow"); + if (Opts.Index && SpecFuzzyFind && SpecFuzzyFind->CachedReq.hasValue()) { + assert(!SpecFuzzyFind->Result.valid()); + if ((SpecReq = speculativeFuzzyFindRequestForCompletion( + *SpecFuzzyFind->CachedReq, SemaCCInput.FileName, + SemaCCInput.Contents, SemaCCInput.Pos))) + SpecFuzzyFind->Result = startAsyncFuzzyFind(*Opts.Index, *SpecReq); + } // We run Sema code completion first. It builds an AST and calculates: // - completion results based on the AST. @@ -1205,6 +1250,7 @@ class CodeCompleteFlow { }); Recorder = RecorderOwner.get(); + semaCodeComplete(std::move(RecorderOwner), Opts.getClangCompleteOpts(), SemaCCInput, &Includes); @@ -1256,6 +1302,7 @@ class CodeCompleteFlow { auto IndexResults = (Opts.Index && allowIndex(Recorder->CCContext)) ? queryIndex() : SymbolSlab(); + trace::Span Tracer("Populate CodeCompleteResult"); // Merge Sema, Index and Override results, score them, and pick the // winners. const auto Overrides = getNonOverridenMethodCompletionResults( @@ -1279,7 +1326,6 @@ class CodeCompleteFlow { trace::Span Tracer("Query index"); SPAN_ATTACH(Tracer, "limit", int64_t(Opts.Limit)); - SymbolSlab::Builder ResultsBuilder; // Build the query. FuzzyFindRequest Req; if (Opts.Limit) @@ -1291,7 +1337,22 @@ class CodeCompleteFlow { Req.ProximityPaths.push_back(FileName); vlog("Code complete: fuzzyFind(\"{0}\", scopes=[{1}])", Req.Query, llvm::join(Req.Scopes.begin(), Req.Scopes.end(), ",")); + + if (SpecFuzzyFind) + SpecFuzzyFind->NewReq = Req; + if (SpecFuzzyFind && SpecFuzzyFind->Result.valid() && (*SpecReq == Req)) { + vlog("Code complete: speculative fuzzy request matches the actual index " + "request. Waiting for the speculative index results."); + SPAN_ATTACH(Tracer, "Speculative results", true); + + trace::Span WaitSpec("Wait speculative results"); + return SpecFuzzyFind->Result.get(); + } + + SPAN_ATTACH(Tracer, "Speculative results", false); + // Run the query against the index. + SymbolSlab::Builder ResultsBuilder; if (Opts.Index->fuzzyFind( Req, [&](const Symbol &Sym) { ResultsBuilder.insert(Sym); })) Incomplete = true; @@ -1437,15 +1498,39 @@ class CodeCompleteFlow { } }; -CodeCompleteResult codeComplete(PathRef FileName, - const tooling::CompileCommand &Command, - PrecompiledPreamble const *Preamble, - const IncludeStructure &PreambleInclusions, - StringRef Contents, Position Pos, - IntrusiveRefCntPtr VFS, - std::shared_ptr PCHs, - CodeCompleteOptions Opts) { - return CodeCompleteFlow(FileName, PreambleInclusions, Opts) +llvm::Expected +speculateCompletionFilter(llvm::StringRef Content, Position Pos) { + auto Offset = positionToOffset(Content, Pos); + if (!Offset) + return llvm::make_error( + "Failed to convert position to offset in content.", + llvm::inconvertibleErrorCode()); + if (*Offset == 0) + return ""; + + // Start from the character before the cursor. + int St = *Offset - 1; + // FIXME(ioeric): consider UTF characters? + auto IsValidIdentifierChar = [](char c) { + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || (c == '_')); + }; + size_t Len = 0; + for (; (St >= 0) && IsValidIdentifierChar(Content[St]); --St, ++Len) { + } + if (Len > 0) + St++; // Shift to the first valid character. + return Content.substr(St, Len); +} + +CodeCompleteResult +codeComplete(PathRef FileName, const tooling::CompileCommand &Command, + PrecompiledPreamble const *Preamble, + const IncludeStructure &PreambleInclusions, StringRef Contents, + Position Pos, IntrusiveRefCntPtr VFS, + std::shared_ptr PCHs, + CodeCompleteOptions Opts, SpeculativeFuzzyFind *SpecFuzzyFind) { + return CodeCompleteFlow(FileName, PreambleInclusions, SpecFuzzyFind, Opts) .run({FileName, Command, Preamble, Contents, Pos, VFS, PCHs}); } diff --git a/clangd/CodeComplete.h b/clangd/CodeComplete.h index 1d85a66ec..1785fb6a6 100644 --- a/clangd/CodeComplete.h +++ b/clangd/CodeComplete.h @@ -25,6 +25,10 @@ #include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Sema/CodeCompleteOptions.h" #include "clang/Tooling/CompilationDatabase.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include namespace clang { class NamedDecl; @@ -72,6 +76,16 @@ struct CodeCompleteOptions { /// Expose origins of completion items in the label (for debugging). bool ShowOrigins = false; + /// If set to true, this will send an asynchronous speculative index request, + /// based on the index request for the last code completion on the same file + /// and the filter text typed before the cursor, before sema code completion + /// is invoked. This can reduce the code completion latency (by roughly + /// latency of sema code completion) if the speculative request is the same as + /// the one generated for the ongoing code completion from sema. As a sequence + /// of code completions often have the same scopes and proximity paths etc, + /// this should be effective for a number of code completions. + bool SpeculativeIndexRequest = false; + // Populated internally by clangd, do not set. /// If `Index` is set, it is used to augment the code completion /// results. @@ -165,7 +179,27 @@ struct CodeCompleteResult { }; raw_ostream &operator<<(raw_ostream &, const CodeCompleteResult &); +/// A speculative and asynchronous fuzzy find index request (based on cached +/// request) that can be sent before parsing sema. This would reduce completion +/// latency if the speculation succeeds. +struct SpeculativeFuzzyFind { + /// A cached request from past code completions. + /// Set by caller of `codeComplete()`. + llvm::Optional CachedReq; + /// The actual request used by `codeComplete()`. + /// Set by `codeComplete()`. This can be used by callers to update cache. + llvm::Optional NewReq; + /// The result is consumed by `codeComplete()` if speculation succeeded. + /// NOTE: the destructor will wait for the async call to finish. + std::future Result; +}; + /// Get code completions at a specified \p Pos in \p FileName. +/// If \p SpecFuzzyFind is set, a speculative and asynchronous fuzzy find index +/// request (based on cached request) will be run before parsing sema. In case +/// the speculative result is used by code completion (e.g. speculation failed), +/// the speculative result is not consumed, and `SpecFuzzyFind` is only +/// destroyed when the async request finishes. CodeCompleteResult codeComplete(PathRef FileName, const tooling::CompileCommand &Command, PrecompiledPreamble const *Preamble, @@ -173,7 +207,8 @@ CodeCompleteResult codeComplete(PathRef FileName, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, - CodeCompleteOptions Opts); + CodeCompleteOptions Opts, + SpeculativeFuzzyFind *SpecFuzzyFind = nullptr); /// Get signature help at a specified \p Pos in \p FileName. SignatureHelp @@ -193,6 +228,12 @@ signatureHelp(PathRef FileName, const tooling::CompileCommand &Command, // like workspace/symbols or textDocument/definition, but are not used for code // completion. bool isIndexedForCodeCompletion(const NamedDecl &ND, ASTContext &ASTCtx); + +/// Retrives a speculative code completion filter text before the cursor. +/// Exposed for testing only. +llvm::Expected +speculateCompletionFilter(llvm::StringRef Content, Position Pos); + } // namespace clangd } // namespace clang diff --git a/clangd/TUScheduler.h b/clangd/TUScheduler.h index 27542c662..edb19ee19 100644 --- a/clangd/TUScheduler.h +++ b/clangd/TUScheduler.h @@ -14,6 +14,7 @@ #include "Function.h" #include "Threading.h" #include "llvm/ADT/StringMap.h" +#include namespace clang { namespace clangd { @@ -167,6 +168,18 @@ class TUScheduler { std::chrono::steady_clock::duration UpdateDebounce; }; +/// Runs \p Action asynchronously with a new std::thread. The context will be +/// propogated. +template +std::future runAsync(llvm::unique_function Action) { + return std::async(std::launch::async, + [](llvm::unique_function &&Action, Context Ctx) { + WithContext WithCtx(std::move(Ctx)); + return Action(); + }, + std::move(Action), Context::current().clone()); +} + } // namespace clangd } // namespace clang diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 95b081ace..c813ab829 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -20,6 +20,7 @@ #include "llvm/Support/StringSaver.h" #include #include +#include namespace clang { namespace clangd { @@ -343,6 +344,14 @@ struct FuzzyFindRequest { /// Contextually relevant files (e.g. the file we're code-completing in). /// Paths should be absolute. std::vector ProximityPaths; + + bool operator==(const FuzzyFindRequest &Req) const { + return std::tie(Query, Scopes, MaxCandidateCount, RestrictForCodeCompletion, + ProximityPaths) == + std::tie(Req.Query, Req.Scopes, Req.MaxCandidateCount, + Req.RestrictForCodeCompletion, Req.ProximityPaths); + } + bool operator!=(const FuzzyFindRequest &Req) const { return !(*this == Req); } }; struct LookupRequest { diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 0149caec8..39f218535 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -308,6 +308,7 @@ int main(int argc, char *argv[]) { CCOpts.IncludeIndicator.Insert.clear(); CCOpts.IncludeIndicator.NoInsert.clear(); } + CCOpts.SpeculativeIndexRequest = Opts.StaticIndex; // Initialize and run ClangdLSPServer. ClangdLSPServer LSPServer( diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 3f855844a..b92cf64a6 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -927,7 +927,11 @@ class IndexRequestCollector : public SymbolIndex { // isn't used in production code. size_t estimateMemoryUsage() const override { return 0; } - const std::vector allRequests() const { return Requests; } + const std::vector consumeRequests() const { + auto Reqs = std::move(Requests); + Requests = {}; + return Reqs; + } private: mutable std::vector Requests; @@ -938,7 +942,7 @@ std::vector captureIndexRequests(llvm::StringRef Code) { IndexRequestCollector Requests; Opts.Index = &Requests; completions(Code, {}, Opts); - return Requests.allRequests(); + return Requests.consumeRequests(); } TEST(CompletionTest, UnqualifiedIdQuery) { @@ -1709,6 +1713,75 @@ TEST(CompletionTest, SuggestOverrides) { Not(Contains(Labeled("void vfunc(bool param) override"))))); } +TEST(SpeculateCompletionFilter, Filters) { + Annotations F(R"cpp($bof^ + $bol^ + ab$ab^ + x.ab$dot^ + x.$dotempty^ + x::ab$scoped^ + x::$scopedempty^ + + )cpp"); + auto speculate = [&](StringRef PointName) { + auto Filter = speculateCompletionFilter(F.code(), F.point(PointName)); + assert(Filter); + return *Filter; + }; + EXPECT_EQ(speculate("bof"), ""); + EXPECT_EQ(speculate("bol"), ""); + EXPECT_EQ(speculate("ab"), "ab"); + EXPECT_EQ(speculate("dot"), "ab"); + EXPECT_EQ(speculate("dotempty"), ""); + EXPECT_EQ(speculate("scoped"), "ab"); + EXPECT_EQ(speculate("scopedempty"), ""); +} + +TEST(CompletionTest, EnableSpeculativeIndexRequest) { + MockFSProvider FS; + MockCompilationDatabase CDB; + IgnoreDiagnostics DiagConsumer; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + auto File = testPath("foo.cpp"); + Annotations Test(R"cpp( + namespace ns1 { int abc; } + namespace ns2 { int abc; } + void f() { ns1::ab$1^; ns1::ab$2^; } + void f() { ns2::ab$3^; } + )cpp"); + runAddDocument(Server, File, Test.code()); + clangd::CodeCompleteOptions Opts = {}; + + IndexRequestCollector Requests; + Opts.Index = &Requests; + Opts.SpeculativeIndexRequest = true; + + auto CompleteAtPoint = [&](StringRef P) { + cantFail(runCodeComplete(Server, File, Test.point(P), Opts)); + // Sleep for a while to make sure asynchronous call (if applicable) is also + // triggered before callback is invoked. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + }; + + CompleteAtPoint("1"); + auto Reqs1 = Requests.consumeRequests(); + ASSERT_EQ(Reqs1.size(), 1u); + EXPECT_THAT(Reqs1[0].Scopes, UnorderedElementsAre("ns1::")); + + CompleteAtPoint("2"); + auto Reqs2 = Requests.consumeRequests(); + // Speculation succeeded. Used speculative index result. + ASSERT_EQ(Reqs2.size(), 1u); + EXPECT_EQ(Reqs2[0], Reqs1[0]); + + CompleteAtPoint("3"); + // Speculation failed. Sent speculative index request and the new index + // request after sema. + auto Reqs3 = Requests.consumeRequests(); + ASSERT_EQ(Reqs3.size(), 2u); +} + } // namespace } // namespace clangd } // namespace clang From 66cc5c29ed866eecd80459ba92f54139cdbf9a8d Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Fri, 24 Aug 2018 11:25:43 +0000 Subject: [PATCH 088/686] [clangd] Implement LIMIT iterator This patch introduces LIMIT iterator, which is very important for improving the quality of search query. LIMIT iterators can be applied on top of BOOST iterators to prevent populating query request with a huge number of low-quality symbols. Reviewed by: sammccall Differential Revision: https://reviews.llvm.org/D51029 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340605 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/DexIndex.cpp | 4 +- clangd/index/dex/Iterator.cpp | 81 ++++++++++++++++++++++-------- clangd/index/dex/Iterator.h | 30 ++++++----- unittests/clangd/DexIndexTests.cpp | 46 ++++++++--------- 4 files changed, 101 insertions(+), 60 deletions(-) diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/DexIndex.cpp index 9280cc8fd..dd993a95c 100644 --- a/clangd/index/dex/DexIndex.cpp +++ b/clangd/index/dex/DexIndex.cpp @@ -128,10 +128,10 @@ bool DexIndex::fuzzyFind( // using 100x of the requested number might not be good in practice, e.g. // when the requested number of items is small. const unsigned ItemsToRetrieve = 100 * Req.MaxCandidateCount; + auto Root = createLimit(move(QueryIterator), ItemsToRetrieve); // FIXME(kbobyrev): Add boosting to the query and utilize retrieved // boosting scores. - std::vector> SymbolDocIDs = - consume(*QueryIterator, ItemsToRetrieve); + std::vector> SymbolDocIDs = consume(*Root); // Retrieve top Req.MaxCandidateCount items. std::priority_queue> Top; diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index e128e1dd6..5190217e7 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -46,7 +46,7 @@ class DocumentIterator : public Iterator { return *Index; } - float consume(DocID ID) override { return DEFAULT_BOOST_SCORE; } + float consume() override { return DEFAULT_BOOST_SCORE; } private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { @@ -105,16 +105,12 @@ class AndIterator : public Iterator { DocID peek() const override { return Children.front()->peek(); } - // If not exhausted and points to the given item, consume() returns the - // product of Children->consume(ID). Otherwise, DEFAULT_BOOST_SCORE is - // returned. - float consume(DocID ID) override { - if (reachedEnd() || peek() != ID) - return DEFAULT_BOOST_SCORE; + float consume() override { + assert(!reachedEnd() && "AndIterator can't consume() at the end."); return std::accumulate( begin(Children), end(Children), DEFAULT_BOOST_SCORE, [&](float Current, const std::unique_ptr &Child) { - return Current * Child->consume(ID); + return Current * Child->consume(); }); } @@ -226,15 +222,16 @@ class OrIterator : public Iterator { // Returns the maximum boosting score among all Children when iterator is not // exhausted and points to the given ID, DEFAULT_BOOST_SCORE otherwise. - float consume(DocID ID) override { - if (reachedEnd() || peek() != ID) - return DEFAULT_BOOST_SCORE; + float consume() override { + assert(!reachedEnd() && + "OrIterator can't consume() after it reached the end."); + const DocID ID = peek(); return std::accumulate( begin(Children), end(Children), DEFAULT_BOOST_SCORE, - [&](float Current, const std::unique_ptr &Child) { + [&](float Boost, const std::unique_ptr &Child) { return (!Child->reachedEnd() && Child->peek() == ID) - ? std::max(Current, Child->consume(ID)) - : Current; + ? std::max(Boost, Child->consume()) + : Boost; }); } @@ -278,7 +275,7 @@ class TrueIterator : public Iterator { return Index; } - float consume(DocID) override { return DEFAULT_BOOST_SCORE; } + float consume() override { return DEFAULT_BOOST_SCORE; } private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { @@ -306,7 +303,7 @@ class BoostIterator : public Iterator { DocID peek() const override { return Child->peek(); } - float consume(DocID ID) override { return Child->consume(ID) * Factor; } + float consume() override { return Child->consume() * Factor; } private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { @@ -318,15 +315,50 @@ class BoostIterator : public Iterator { float Factor; }; +/// This iterator limits the number of items retrieved from the child iterator +/// on top of the query tree. To ensure that query tree with LIMIT iterators +/// inside works correctly, users have to call Root->consume(Root->peek()) each +/// time item is retrieved at the root of query tree. +class LimitIterator : public Iterator { +public: + LimitIterator(std::unique_ptr Child, size_t Limit) + : Child(move(Child)), Limit(Limit), ItemsLeft(Limit) {} + + bool reachedEnd() const override { + return ItemsLeft == 0 || Child->reachedEnd(); + } + + void advance() override { Child->advance(); } + + void advanceTo(DocID ID) override { Child->advanceTo(ID); } + + DocID peek() const override { return Child->peek(); } + + /// Decreases the limit in case the element consumed at top of the query tree + /// comes from the underlying iterator. + float consume() override { + assert(!reachedEnd() && "LimitIterator can't consume at the end."); + --ItemsLeft; + return Child->consume(); + } + +private: + llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { + OS << "(LIMIT " << Limit << '(' << ItemsLeft << ") " << *Child << ')'; + return OS; + } + + std::unique_ptr Child; + size_t Limit; + size_t ItemsLeft; +}; + } // end namespace -std::vector> consume(Iterator &It, size_t Limit) { +std::vector> consume(Iterator &It) { std::vector> Result; - for (size_t Retrieved = 0; !It.reachedEnd() && Retrieved < Limit; - It.advance(), ++Retrieved) { - DocID Document = It.peek(); - Result.push_back(std::make_pair(Document, It.consume(Document))); - } + for (; !It.reachedEnd(); It.advance()) + Result.push_back(std::make_pair(It.peek(), It.consume())); return Result; } @@ -353,6 +385,11 @@ std::unique_ptr createBoost(std::unique_ptr Child, return llvm::make_unique(move(Child), Factor); } +std::unique_ptr createLimit(std::unique_ptr Child, + size_t Size) { + return llvm::make_unique(move(Child), Size); +} + } // namespace dex } // namespace clangd } // namespace clang diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 68e7a6f62..84a1a16e2 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -87,13 +87,14 @@ class Iterator { /// /// Note: reachedEnd() must be false. virtual DocID peek() const = 0; - /// Retrieves boosting score. Query tree root should pass Root->peek() to this - /// function, the parameter is needed to propagate through the tree. Given ID - /// should be compared against BOOST iterator peek()s: some of the iterators - /// would not point to the item which was propagated to the top of the query - /// tree (e.g. if these iterators are branches of OR iterator) and hence - /// shouldn't apply any boosting to the consumed item. - virtual float consume(DocID ID) = 0; + /// Informs the iterator that the current document was consumed, and returns + /// its boost. + /// + /// Note: If this iterator has any child iterators that contain the document, + /// consume() should be called on those and their boosts incorporated. + /// consume() must *not* be called on children that don't contain the current + /// doc. + virtual float consume() = 0; virtual ~Iterator() {} @@ -118,17 +119,15 @@ class Iterator { virtual llvm::raw_ostream &dump(llvm::raw_ostream &OS) const = 0; }; -/// Advances the iterator until it is either exhausted or the number of -/// requested items is reached. Returns pairs of document IDs with the -/// corresponding boosting score. +/// Advances the iterator until it is exhausted. Returns pairs of document IDs +/// with the corresponding boosting score. /// /// Boosting can be seen as a compromise between retrieving too many items and /// calculating finals score for each of them (which might be very expensive) /// and not retrieving enough items so that items with very high final score /// would not be processed. Boosting score is a computationally efficient way /// to acquire preliminary scores of requested items. -std::vector> -consume(Iterator &It, size_t Limit = std::numeric_limits::max()); +std::vector> consume(Iterator &It); /// Returns a document iterator over given PostingList. /// @@ -165,6 +164,13 @@ std::unique_ptr createTrue(DocID Size); std::unique_ptr createBoost(std::unique_ptr Child, float Factor); +/// Returns LIMIT iterator, which yields up to N elements of its child iterator. +/// Elements only count towards the limit if they are part of the final result +/// set. Therefore the following iterator (AND (2) (LIMIT (1 2) 1)) yields (2), +/// not (). +std::unique_ptr createLimit(std::unique_ptr Child, + size_t Limit); + /// This allows createAnd(create(...), create(...)) syntax. template std::unique_ptr createAnd(Args... args) { std::vector> Children; diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index ca20e5a8b..eb8c1e0c9 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -29,9 +29,8 @@ namespace clangd { namespace dex { namespace { -std::vector -consumeIDs(Iterator &It, size_t Limit = std::numeric_limits::max()) { - auto IDAndScore = consume(It, Limit); +std::vector consumeIDs(Iterator &It) { + auto IDAndScore = consume(It); std::vector IDs(IDAndScore.size()); for (size_t I = 0; I < IDAndScore.size(); ++I) IDs[I] = IDAndScore[I].first; @@ -234,13 +233,13 @@ TEST(DexIndexIterators, QueryTree) { Root->advanceTo(1); Root->advanceTo(0); EXPECT_EQ(Root->peek(), 1U); - auto ElementBoost = Root->consume(Root->peek()); + auto ElementBoost = Root->consume(); EXPECT_THAT(ElementBoost, 6); Root->advance(); EXPECT_EQ(Root->peek(), 5U); Root->advanceTo(5); EXPECT_EQ(Root->peek(), 5U); - ElementBoost = Root->consume(Root->peek()); + ElementBoost = Root->consume(); EXPECT_THAT(ElementBoost, 8); Root->advanceTo(9000); EXPECT_TRUE(Root->reachedEnd()); @@ -265,24 +264,23 @@ TEST(DexIndexIterators, StringRepresentation) { } TEST(DexIndexIterators, Limit) { - const PostingList L0 = {4, 7, 8, 20, 42, 100}; - const PostingList L1 = {1, 3, 5, 8, 9}; - const PostingList L2 = {1, 5, 7, 9}; - const PostingList L3 = {0, 5}; - const PostingList L4 = {0, 1, 5}; - const PostingList L5; + const PostingList L0 = {3, 6, 7, 20, 42, 100}; + const PostingList L1 = {1, 3, 5, 6, 7, 30, 100}; + const PostingList L2 = {0, 3, 5, 7, 8, 100}; - auto DocIterator = create(L0); - EXPECT_THAT(consumeIDs(*DocIterator, 42), ElementsAre(4, 7, 8, 20, 42, 100)); + auto DocIterator = createLimit(create(L0), 42); + EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(3, 6, 7, 20, 42, 100)); - DocIterator = create(L0); - EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(4, 7, 8, 20, 42, 100)); + DocIterator = createLimit(create(L0), 3); + EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(3, 6, 7)); - DocIterator = create(L0); - EXPECT_THAT(consumeIDs(*DocIterator, 3), ElementsAre(4, 7, 8)); + DocIterator = createLimit(create(L0), 0); + EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre()); - DocIterator = create(L0); - EXPECT_THAT(consumeIDs(*DocIterator, 0), ElementsAre()); + auto AndIterator = + createAnd(createLimit(createTrue(9000), 343), createLimit(create(L0), 2), + createLimit(create(L1), 3), createLimit(create(L2), 42)); + EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(3, 7)); } TEST(DexIndexIterators, True) { @@ -301,7 +299,7 @@ TEST(DexIndexIterators, True) { TEST(DexIndexIterators, Boost) { auto BoostIterator = createBoost(createTrue(5U), 42U); EXPECT_FALSE(BoostIterator->reachedEnd()); - auto ElementBoost = BoostIterator->consume(BoostIterator->peek()); + auto ElementBoost = BoostIterator->consume(); EXPECT_THAT(ElementBoost, 42U); const PostingList L0 = {2, 4}; @@ -309,20 +307,20 @@ TEST(DexIndexIterators, Boost) { auto Root = createOr(createTrue(5U), createBoost(create(L0), 2U), createBoost(create(L1), 3U)); - ElementBoost = Root->consume(Root->peek()); + ElementBoost = Root->consume(); EXPECT_THAT(ElementBoost, Iterator::DEFAULT_BOOST_SCORE); Root->advance(); EXPECT_THAT(Root->peek(), 1U); - ElementBoost = Root->consume(Root->peek()); + ElementBoost = Root->consume(); EXPECT_THAT(ElementBoost, 3); Root->advance(); EXPECT_THAT(Root->peek(), 2U); - ElementBoost = Root->consume(Root->peek()); + ElementBoost = Root->consume(); EXPECT_THAT(ElementBoost, 2); Root->advanceTo(4); - ElementBoost = Root->consume(Root->peek()); + ElementBoost = Root->consume(); EXPECT_THAT(ElementBoost, 3); } From b910a10000c189f59c1c0084765f32b20e8f0bb2 Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Fri, 24 Aug 2018 13:09:41 +0000 Subject: [PATCH 089/686] [clangd] Initial cancellation mechanism for LSP requests. Reviewers: ilya-biryukov, ioeric, hokein Reviewed By: ilya-biryukov Subscribers: mgorny, ioeric, MaskRay, jkorous, arphaman, jfb, cfe-commits Differential Revision: https://reviews.llvm.org/D50502 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340607 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 1 + clangd/Cancellation.cpp | 34 ++++++ clangd/Cancellation.h | 142 +++++++++++++++++++++++++ clangd/ClangdLSPServer.cpp | 95 +++++++++++++---- clangd/ClangdLSPServer.h | 17 ++- clangd/ClangdServer.cpp | 13 ++- clangd/ClangdServer.h | 7 +- clangd/JSONRPCDispatcher.cpp | 19 +++- clangd/JSONRPCDispatcher.h | 8 +- clangd/Protocol.cpp | 33 ++++++ clangd/Protocol.h | 14 +++ clangd/ProtocolHandlers.cpp | 1 + clangd/ProtocolHandlers.h | 1 + unittests/clangd/CMakeLists.txt | 1 + unittests/clangd/CancellationTests.cpp | 74 +++++++++++++ 15 files changed, 431 insertions(+), 29 deletions(-) create mode 100644 clangd/Cancellation.cpp create mode 100644 clangd/Cancellation.h create mode 100644 unittests/clangd/CancellationTests.cpp diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index a3c3a6d7a..ef332baf7 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -9,6 +9,7 @@ endif() add_clang_library(clangDaemon AST.cpp + Cancellation.cpp ClangdLSPServer.cpp ClangdServer.cpp ClangdUnit.cpp diff --git a/clangd/Cancellation.cpp b/clangd/Cancellation.cpp new file mode 100644 index 000000000..a74097583 --- /dev/null +++ b/clangd/Cancellation.cpp @@ -0,0 +1,34 @@ +//===--- Cancellation.cpp -----------------------------------------*-C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Cancellation.h" +#include + +namespace clang { +namespace clangd { + +namespace { +static Key TaskKey; +} // namespace + +char CancelledError::ID = 0; + +const Task &getCurrentTask() { + const auto TH = Context::current().getExisting(TaskKey); + assert(TH && "Fetched a nullptr for TaskHandle from context."); + return *TH; +} + +Context setCurrentTask(ConstTaskHandle TH) { + assert(TH && "Trying to stash a nullptr as TaskHandle into context."); + return Context::current().derive(TaskKey, std::move(TH)); +} + +} // namespace clangd +} // namespace clang diff --git a/clangd/Cancellation.h b/clangd/Cancellation.h new file mode 100644 index 000000000..d7868e756 --- /dev/null +++ b/clangd/Cancellation.h @@ -0,0 +1,142 @@ +//===--- Cancellation.h -------------------------------------------*-C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Cancellation mechanism for async tasks. Roughly all the clients of this code +// can be classified into three categories: +// 1. The code that creates and schedules async tasks, e.g. TUScheduler. +// 2. The callers of the async method that can cancel some of the running tasks, +// e.g. `ClangdLSPServer` +// 3. The code running inside the async task itself, i.e. code completion or +// find definition implementation that run clang, etc. +// +// For (1), the guideline is to accept a callback for the result of async +// operation and return a `TaskHandle` to allow cancelling the request. +// +// TaskHandle someAsyncMethod(Runnable T, +// function)> Callback) { +// auto TH = Task::createHandle(); +// WithContext ContextWithCancellationToken(TH); +// auto run = [](){ +// Callback(T()); +// } +// // Start run() in a new async thread, and make sure to propagate Context. +// return TH; +// } +// +// The callers of async methods (2) can issue cancellations and should be +// prepared to handle `TaskCancelledError` result: +// +// void Caller() { +// // You should store this handle if you wanna cancel the task later on. +// TaskHandle TH = someAsyncMethod(Task, [](llvm::Expected R) { +// if(/*check for task cancellation error*/) +// // Handle the error +// // Do other things on R. +// }); +// // To cancel the task: +// sleep(5); +// TH->cancel(); +// } +// +// The worker code itself (3) should check for cancellations using +// `Task::isCancelled` that can be retrieved via `getCurrentTask()`. +// +// llvm::Expected AsyncTask() { +// // You can either store the read only TaskHandle by calling getCurrentTask +// // once and just use the variable everytime you want to check for +// // cancellation, or call isCancelled everytime. The former is more +// // efficient if you are going to have multiple checks. +// const auto T = getCurrentTask(); +// // DO SMTHNG... +// if(T.isCancelled()) { +// // Task has been cancelled, lets get out. +// return llvm::makeError(); +// } +// // DO SOME MORE THING... +// if(T.isCancelled()) { +// // Task has been cancelled, lets get out. +// return llvm::makeError(); +// } +// return ResultType(...); +// } +// If the operation was cancelled before task could run to completion, it should +// propagate the TaskCancelledError as a result. + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CANCELLATION_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CANCELLATION_H + +#include "Context.h" +#include "llvm/Support/Error.h" +#include +#include +#include + +namespace clang { +namespace clangd { + +/// Enables signalling a cancellation on an async task or checking for +/// cancellation. It is thread-safe to trigger cancellation from multiple +/// threads or check for cancellation. Task object for the currently running +/// task can be obtained via clangd::getCurrentTask(). +class Task { +public: + void cancel() { CT = true; } + /// If cancellation checks are rare, one could use the isCancelled() helper in + /// the namespace to simplify the code. However, if cancellation checks are + /// frequent, the guideline is first obtain the Task object for the currently + /// running task with getCurrentTask() and do cancel checks using it to avoid + /// extra lookups in the Context. + bool isCancelled() const { return CT; } + + /// Creates a task handle that can be used by an asyn task to check for + /// information that can change during it's runtime, like Cancellation. + static std::shared_ptr createHandle() { + return std::shared_ptr(new Task()); + } + + Task(const Task &) = delete; + Task &operator=(const Task &) = delete; + Task(Task &&) = delete; + Task &operator=(Task &&) = delete; + +private: + Task() : CT(false) {} + std::atomic CT; +}; +using ConstTaskHandle = std::shared_ptr; +using TaskHandle = std::shared_ptr; + +/// Fetches current task information from Context. TaskHandle must have been +/// stashed into context beforehand. +const Task &getCurrentTask(); + +/// Stashes current task information within the context. +LLVM_NODISCARD Context setCurrentTask(ConstTaskHandle TH); + +/// Checks whether the current task has been cancelled or not. +/// Consider storing the task handler returned by getCurrentTask and then +/// calling isCancelled through it. getCurrentTask is expensive since it does a +/// lookup in the context. +inline bool isCancelled() { return getCurrentTask().isCancelled(); } + +class CancelledError : public llvm::ErrorInfo { +public: + static char ID; + + void log(llvm::raw_ostream &OS) const override { + OS << "Task was cancelled."; + } + std::error_code convertToErrorCode() const override { + return std::make_error_code(std::errc::operation_canceled); + } +}; + +} // namespace clangd +} // namespace clang + +#endif diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index 0a0c1ead0..98c50d1fe 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -8,10 +8,12 @@ //===----------------------------------------------------------------------===// #include "ClangdLSPServer.h" +#include "Cancellation.h" #include "Diagnostics.h" #include "JSONRPCDispatcher.h" #include "SourceCode.h" #include "URI.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" @@ -69,6 +71,11 @@ SymbolKindBitset defaultSymbolKinds() { return Defaults; } +std::string NormalizeRequestID(const json::Value &ID) { + auto NormalizedID = parseNumberOrString(&ID); + assert(NormalizedID && "Was not able to parse request id."); + return std::move(*NormalizedID); +} } // namespace void ClangdLSPServer::onInitialize(InitializeParams &Params) { @@ -339,17 +346,21 @@ void ClangdLSPServer::onCodeAction(CodeActionParams &Params) { } void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) { - Server.codeComplete(Params.textDocument.uri.file(), Params.position, CCOpts, - [this](llvm::Expected List) { - if (!List) - return replyError(ErrorCode::InvalidParams, - llvm::toString(List.takeError())); - CompletionList LSPList; - LSPList.isIncomplete = List->HasMore; - for (const auto &R : List->Completions) - LSPList.items.push_back(R.render(CCOpts)); - reply(std::move(LSPList)); - }); + CreateSpaceForTaskHandle(); + TaskHandle TH = Server.codeComplete( + Params.textDocument.uri.file(), Params.position, CCOpts, + [this](llvm::Expected List) { + auto _ = llvm::make_scope_exit([this]() { CleanupTaskHandle(); }); + + if (!List) + return replyError(List.takeError()); + CompletionList LSPList; + LSPList.isIncomplete = List->HasMore; + for (const auto &R : List->Completions) + LSPList.items.push_back(R.render(CCOpts)); + return reply(std::move(LSPList)); + }); + StoreTaskHandle(std::move(TH)); } void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams &Params) { @@ -364,14 +375,14 @@ void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams &Params) { } void ClangdLSPServer::onGoToDefinition(TextDocumentPositionParams &Params) { - Server.findDefinitions( - Params.textDocument.uri.file(), Params.position, - [](llvm::Expected> Items) { - if (!Items) - return replyError(ErrorCode::InvalidParams, - llvm::toString(Items.takeError())); - reply(json::Array(*Items)); - }); + Server.findDefinitions(Params.textDocument.uri.file(), Params.position, + [](llvm::Expected> Items) { + if (!Items) + return replyError( + ErrorCode::InvalidParams, + llvm::toString(Items.takeError())); + reply(json::Array(*Items)); + }); } void ClangdLSPServer::onSwitchSourceHeader(TextDocumentIdentifier &Params) { @@ -606,3 +617,49 @@ GlobalCompilationDatabase &ClangdLSPServer::CompilationDB::getCDB() { return *CachingCDB; return *CDB; } + +void ClangdLSPServer::onCancelRequest(CancelParams &Params) { + std::lock_guard Lock(TaskHandlesMutex); + const auto &It = TaskHandles.find(Params.ID); + if (It == TaskHandles.end()) + return; + if (It->second) + It->second->cancel(); + TaskHandles.erase(It); +} + +void ClangdLSPServer::CleanupTaskHandle() { + const json::Value *ID = getRequestId(); + if (!ID) + return; + std::string NormalizedID = NormalizeRequestID(*ID); + std::lock_guard Lock(TaskHandlesMutex); + TaskHandles.erase(NormalizedID); +} + +void ClangdLSPServer::CreateSpaceForTaskHandle() { + const json::Value *ID = getRequestId(); + if (!ID) + return; + std::string NormalizedID = NormalizeRequestID(*ID); + std::lock_guard Lock(TaskHandlesMutex); + if (!TaskHandles.insert({NormalizedID, nullptr}).second) + elog("Creation of space for task handle: {0} failed.", NormalizedID); +} + +void ClangdLSPServer::StoreTaskHandle(TaskHandle TH) { + const json::Value *ID = getRequestId(); + if (!ID) + return; + std::string NormalizedID = NormalizeRequestID(*ID); + std::lock_guard Lock(TaskHandlesMutex); + auto It = TaskHandles.find(NormalizedID); + if (It == TaskHandles.end()) { + elog("CleanupTaskHandle called before store can happen for request:{0}.", + NormalizedID); + return; + } + if (It->second != nullptr) + elog("TaskHandle didn't get cleared for: {0}.", NormalizedID); + It->second = std::move(TH); +} diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h index 1d6252dad..0a714491a 100644 --- a/clangd/ClangdLSPServer.h +++ b/clangd/ClangdLSPServer.h @@ -75,6 +75,7 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { void onRename(RenameParams &Parames) override; void onHover(TextDocumentPositionParams &Params) override; void onChangeConfiguration(DidChangeConfigurationParams &Params) override; + void onCancelRequest(CancelParams &Params) override; std::vector getFixes(StringRef File, const clangd::Diagnostic &D); @@ -167,8 +168,22 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { // the worker thread that may otherwise run an async callback on partially // destructed instance of ClangdLSPServer. ClangdServer Server; -}; + // Holds task handles for running requets. Key of the map is a serialized + // request id. + llvm::StringMap TaskHandles; + std::mutex TaskHandlesMutex; + + // Following three functions are for managing TaskHandles map. They store or + // remove a task handle for the request-id stored in current Context. + // FIXME(kadircet): Wrap the following three functions in a RAII object to + // make sure these do not get misused. The object might be stored in the + // Context of the thread or moved around until a reply is generated for the + // request. + void CleanupTaskHandle(); + void CreateSpaceForTaskHandle(); + void StoreTaskHandle(TaskHandle TH); +}; } // namespace clangd } // namespace clang diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 788b58294..abd2d895b 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -8,6 +8,7 @@ //===-------------------------------------------------------------------===// #include "ClangdServer.h" +#include "Cancellation.h" #include "CodeComplete.h" #include "FindSymbols.h" #include "Headers.h" @@ -173,14 +174,16 @@ void ClangdServer::removeDocument(PathRef File) { WorkScheduler.remove(File); } -void ClangdServer::codeComplete(PathRef File, Position Pos, - const clangd::CodeCompleteOptions &Opts, - Callback CB) { +TaskHandle ClangdServer::codeComplete(PathRef File, Position Pos, + const clangd::CodeCompleteOptions &Opts, + Callback CB) { // Copy completion options for passing them to async task handler. auto CodeCompleteOpts = Opts; if (!CodeCompleteOpts.Index) // Respect overridden index. CodeCompleteOpts.Index = Index; + TaskHandle TH = Task::createHandle(); + WithContext ContextWithCancellation(setCurrentTask(TH)); // Copy PCHs to avoid accessing this->PCHs concurrently std::shared_ptr PCHs = this->PCHs; auto FS = FSProvider.getFileSystem(); @@ -188,6 +191,9 @@ void ClangdServer::codeComplete(PathRef File, Position Pos, auto Task = [PCHs, Pos, FS, CodeCompleteOpts, this](Path File, Callback CB, llvm::Expected IP) { + if (isCancelled()) + return CB(llvm::make_error()); + if (!IP) return CB(IP.takeError()); @@ -226,6 +232,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos, WorkScheduler.runWithPreamble("CodeComplete", File, Bind(Task, File.str(), std::move(CB))); + return TH; } void ClangdServer::signatureHelp(PathRef File, Position Pos, diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h index 8bb5fdd65..018bddd3b 100644 --- a/clangd/ClangdServer.h +++ b/clangd/ClangdServer.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H +#include "Cancellation.h" #include "ClangdUnit.h" #include "CodeComplete.h" #include "FSProvider.h" @@ -124,9 +125,9 @@ class ClangdServer { /// while returned future is not yet ready. /// A version of `codeComplete` that runs \p Callback on the processing thread /// when codeComplete results become available. - void codeComplete(PathRef File, Position Pos, - const clangd::CodeCompleteOptions &Opts, - Callback CB); + TaskHandle codeComplete(PathRef File, Position Pos, + const clangd::CodeCompleteOptions &Opts, + Callback CB); /// Provide signature help for \p File at \p Pos. This method should only be /// called for tracked files. diff --git a/clangd/JSONRPCDispatcher.cpp b/clangd/JSONRPCDispatcher.cpp index 2741c6650..9dea83ae8 100644 --- a/clangd/JSONRPCDispatcher.cpp +++ b/clangd/JSONRPCDispatcher.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "JSONRPCDispatcher.h" +#include "Cancellation.h" #include "ProtocolHandlers.h" #include "Trace.h" #include "llvm/ADT/SmallString.h" @@ -93,7 +94,7 @@ void JSONOutput::mirrorInput(const Twine &Message) { } void clangd::reply(json::Value &&Result) { - auto ID = Context::current().get(RequestID); + auto ID = getRequestId(); if (!ID) { elog("Attempted to reply to a notification!"); return; @@ -116,7 +117,7 @@ void clangd::replyError(ErrorCode Code, const llvm::StringRef &Message) { {"message", Message.str()}}; }); - if (auto ID = Context::current().get(RequestID)) { + if (auto ID = getRequestId()) { log("--> reply({0}) error: {1}", *ID, Message); Context::current() .getExisting(RequestOut) @@ -129,6 +130,16 @@ void clangd::replyError(ErrorCode Code, const llvm::StringRef &Message) { } } +void clangd::replyError(Error E) { + handleAllErrors(std::move(E), + [](const CancelledError &TCE) { + replyError(ErrorCode::RequestCancelled, TCE.message()); + }, + [](const ErrorInfoBase &EIB) { + replyError(ErrorCode::InvalidParams, EIB.message()); + }); +} + void clangd::call(StringRef Method, json::Value &&Params) { RequestSpan::attach([&](json::Object &Args) { Args["Call"] = json::Object{{"method", Method.str()}, {"params", Params}}; @@ -366,3 +377,7 @@ void clangd::runLanguageServerLoop(std::FILE *In, JSONOutput &Out, } } } + +const json::Value *clangd::getRequestId() { + return Context::current().get(RequestID); +} diff --git a/clangd/JSONRPCDispatcher.h b/clangd/JSONRPCDispatcher.h index e8c96fc92..fda37f86c 100644 --- a/clangd/JSONRPCDispatcher.h +++ b/clangd/JSONRPCDispatcher.h @@ -64,6 +64,13 @@ void reply(llvm::json::Value &&Result); /// Sends an error response to the client, and logs it. /// Current context must derive from JSONRPCDispatcher::Handler. void replyError(ErrorCode Code, const llvm::StringRef &Message); +/// Implements ErrorCode and message extraction from a given llvm::Error. It +/// fetches the related message from error's message method. If error doesn't +/// match any known errors, uses ErrorCode::InvalidParams for the error. +void replyError(llvm::Error E); +/// Returns the request-id of the current request. Should not be used directly +/// for replying to requests, use the above mentioned methods for that case. +const llvm::json::Value *getRequestId(); /// Sends a request to the client. /// Current context must derive from JSONRPCDispatcher::Handler. void call(llvm::StringRef Method, llvm::json::Value &&Params); @@ -110,7 +117,6 @@ enum JSONStreamStyle { void runLanguageServerLoop(std::FILE *In, JSONOutput &Out, JSONStreamStyle InputStyle, JSONRPCDispatcher &Dispatcher, bool &IsDone); - } // namespace clangd } // namespace clang diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp index 3f56056e4..609cfa621 100644 --- a/clangd/Protocol.cpp +++ b/clangd/Protocol.cpp @@ -616,5 +616,38 @@ bool fromJSON(const json::Value &Params, O.map("compilationDatabaseChanges", CCPC.compilationDatabaseChanges); } +json::Value toJSON(const CancelParams &CP) { + return json::Object{{"id", CP.ID}}; +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const CancelParams &CP) { + O << toJSON(CP); + return O; +} + +llvm::Optional parseNumberOrString(const json::Value *Params) { + if (!Params) + return llvm::None; + // ID is either a number or a string, check for both. + if(const auto AsString = Params->getAsString()) + return AsString->str(); + + if(const auto AsNumber = Params->getAsInteger()) + return itostr(AsNumber.getValue()); + + return llvm::None; +} + +bool fromJSON(const json::Value &Params, CancelParams &CP) { + const auto ParamsAsObject = Params.getAsObject(); + if (!ParamsAsObject) + return false; + if (auto Parsed = parseNumberOrString(ParamsAsObject->get("id"))) { + CP.ID = std::move(*Parsed); + return true; + } + return false; +} + } // namespace clangd } // namespace clang diff --git a/clangd/Protocol.h b/clangd/Protocol.h index fc6a3a8c9..d019e82db 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -871,6 +871,20 @@ struct DocumentHighlight { llvm::json::Value toJSON(const DocumentHighlight &DH); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DocumentHighlight &); +struct CancelParams { + /// The request id to cancel. + /// This can be either a number or string, if it is a number simply print it + /// out and always use a string. + std::string ID; +}; +llvm::json::Value toJSON(const CancelParams &); +llvm::raw_ostream &operator<<(llvm::raw_ostream &, const CancelParams &); +bool fromJSON(const llvm::json::Value &, CancelParams &); + +/// Param can be either of type string or number. Returns the result as a +/// string. +llvm::Optional parseNumberOrString(const llvm::json::Value *Param); + } // namespace clangd } // namespace clang diff --git a/clangd/ProtocolHandlers.cpp b/clangd/ProtocolHandlers.cpp index deb5b9d09..5cf370d44 100644 --- a/clangd/ProtocolHandlers.cpp +++ b/clangd/ProtocolHandlers.cpp @@ -75,4 +75,5 @@ void clangd::registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, Register("workspace/didChangeConfiguration", &ProtocolCallbacks::onChangeConfiguration); Register("workspace/symbol", &ProtocolCallbacks::onWorkspaceSymbol); + Register("$/cancelRequest", &ProtocolCallbacks::onCancelRequest); } diff --git a/clangd/ProtocolHandlers.h b/clangd/ProtocolHandlers.h index 63fd99dca..cfbac5252 100644 --- a/clangd/ProtocolHandlers.h +++ b/clangd/ProtocolHandlers.h @@ -55,6 +55,7 @@ class ProtocolCallbacks { virtual void onDocumentHighlight(TextDocumentPositionParams &Params) = 0; virtual void onHover(TextDocumentPositionParams &Params) = 0; virtual void onChangeConfiguration(DidChangeConfigurationParams &Params) = 0; + virtual void onCancelRequest(CancelParams &Params) = 0; }; void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, diff --git a/unittests/clangd/CMakeLists.txt b/unittests/clangd/CMakeLists.txt index ab0a21209..92f8b963f 100644 --- a/unittests/clangd/CMakeLists.txt +++ b/unittests/clangd/CMakeLists.txt @@ -10,6 +10,7 @@ include_directories( add_extra_unittest(ClangdTests Annotations.cpp + CancellationTests.cpp ClangdTests.cpp ClangdUnitTests.cpp CodeCompleteTests.cpp diff --git a/unittests/clangd/CancellationTests.cpp b/unittests/clangd/CancellationTests.cpp new file mode 100644 index 000000000..7afd1086a --- /dev/null +++ b/unittests/clangd/CancellationTests.cpp @@ -0,0 +1,74 @@ +#include "Cancellation.h" +#include "Context.h" +#include "Threading.h" +#include "llvm/Support/Error.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include +#include + +namespace clang { +namespace clangd { +namespace { + +TEST(CancellationTest, CancellationTest) { + TaskHandle TH = Task::createHandle(); + WithContext ContextWithCancellation(setCurrentTask(TH)); + EXPECT_FALSE(isCancelled()); + TH->cancel(); + EXPECT_TRUE(isCancelled()); +} + +TEST(CancellationTest, TaskTestHandleDiesContextLives) { + llvm::Optional ContextWithCancellation; + { + TaskHandle TH = Task::createHandle(); + ContextWithCancellation.emplace(setCurrentTask(TH)); + EXPECT_FALSE(isCancelled()); + TH->cancel(); + EXPECT_TRUE(isCancelled()); + } + EXPECT_TRUE(isCancelled()); +} + +TEST(CancellationTest, TaskContextDiesHandleLives) { + TaskHandle TH = Task::createHandle(); + { + WithContext ContextWithCancellation(setCurrentTask(TH)); + EXPECT_FALSE(isCancelled()); + TH->cancel(); + EXPECT_TRUE(isCancelled()); + } + // Still should be able to cancel without any problems. + TH->cancel(); +} + +TEST(CancellationTest, CancellationToken) { + TaskHandle TH = Task::createHandle(); + WithContext ContextWithCancellation(setCurrentTask(TH)); + const auto &CT = getCurrentTask(); + EXPECT_FALSE(CT.isCancelled()); + TH->cancel(); + EXPECT_TRUE(CT.isCancelled()); +} + +TEST(CancellationTest, AsynCancellationTest) { + std::atomic HasCancelled(false); + Notification Cancelled; + auto TaskToBeCancelled = [&](ConstTaskHandle CT) { + WithContext ContextGuard(setCurrentTask(std::move(CT))); + Cancelled.wait(); + HasCancelled = isCancelled(); + }; + TaskHandle TH = Task::createHandle(); + std::thread AsyncTask(TaskToBeCancelled, TH); + TH->cancel(); + Cancelled.notify(); + AsyncTask.join(); + + EXPECT_TRUE(HasCancelled); +} +} // namespace +} // namespace clangd +} // namespace clang From d116df6611114c11dc089fa4031f8bdb17bd154b Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Fri, 24 Aug 2018 16:43:46 +0000 Subject: [PATCH 090/686] [clang-doc] Fix memory leaks Adds a virtual destructor to the base Info class. Differential Revision: https://reviews.llvm.org/D51137 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340620 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/Representation.h | 2 ++ clang-doc/Serialize.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/clang-doc/Representation.h b/clang-doc/Representation.h index 9e88bd9c8..7a127c08d 100644 --- a/clang-doc/Representation.h +++ b/clang-doc/Representation.h @@ -162,6 +162,8 @@ struct Info { Info(const Info &Other) = delete; Info(Info &&Other) = default; + virtual ~Info() = default; + SymbolID USR = SymbolID(); // Unique identifier for the decl described by this Info. const InfoType IT = InfoType::IT_default; // InfoType of this particular Info. diff --git a/clang-doc/Serialize.cpp b/clang-doc/Serialize.cpp index 873a9bdd1..450822127 100644 --- a/clang-doc/Serialize.cpp +++ b/clang-doc/Serialize.cpp @@ -361,7 +361,7 @@ std::unique_ptr emitInfo(const FunctionDecl *D, const FullComment *FC, I->USR = Func.Namespace[0].USR; else I->USR = SymbolID(); - I->ChildFunctions.push_back(std::move(Func)); + I->ChildFunctions.emplace_back(std::move(Func)); return std::unique_ptr{std::move(I)}; } @@ -382,7 +382,7 @@ std::unique_ptr emitInfo(const CXXMethodDecl *D, const FullComment *FC, // Wrap in enclosing scope auto I = llvm::make_unique(); I->USR = ParentUSR; - I->ChildFunctions.push_back(std::move(Func)); + I->ChildFunctions.emplace_back(std::move(Func)); return std::unique_ptr{std::move(I)}; } @@ -402,13 +402,13 @@ std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, case InfoType::IT_namespace: { auto I = llvm::make_unique(); I->USR = Enum.Namespace[0].USR; - I->ChildEnums.push_back(std::move(Enum)); + I->ChildEnums.emplace_back(std::move(Enum)); return std::unique_ptr{std::move(I)}; } case InfoType::IT_record: { auto I = llvm::make_unique(); I->USR = Enum.Namespace[0].USR; - I->ChildEnums.push_back(std::move(Enum)); + I->ChildEnums.emplace_back(std::move(Enum)); return std::unique_ptr{std::move(I)}; } default: @@ -419,7 +419,7 @@ std::unique_ptr emitInfo(const EnumDecl *D, const FullComment *FC, // Put in global namespace auto I = llvm::make_unique(); I->USR = SymbolID(); - I->ChildEnums.push_back(std::move(Enum)); + I->ChildEnums.emplace_back(std::move(Enum)); return std::unique_ptr{std::move(I)}; } From 88c38e8b734ceb6287df7504c8cc63c6c85e233d Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 27 Aug 2018 09:47:50 +0000 Subject: [PATCH 091/686] [clangd] Use TRUE iterator instead of complete posting list Stop using `$$$` (empty) trigram and generating a posting list with all items. Since TRUE iterator is already implemented and correctly inserted when there are no real trigram posting lists, this is a valid transformation. Benchmarks show that this simple change allows ~30% speedup on dataset of real completion queries. Before ``` ------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------- DexAdHocQueries 5640321 ns 5640265 ns 120 DexRealQ 939835603 ns 939830296 ns 1 ``` After ``` ------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------- DexAdHocQueries 3452014 ns 3451987 ns 203 DexRealQ 667455912 ns 667455750 ns 1 ``` Reviewed by: ilya-biryukov Differential Revision: https://reviews.llvm.org/D51287 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340729 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Trigram.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/clangd/index/dex/Trigram.cpp b/clangd/index/dex/Trigram.cpp index af422eafe..25a14ffe4 100644 --- a/clangd/index/dex/Trigram.cpp +++ b/clangd/index/dex/Trigram.cpp @@ -67,10 +67,6 @@ std::vector generateIdentifierTrigrams(llvm::StringRef Identifier) { UniqueTrigrams.insert(Token(Token::Kind::Trigram, Chars)); }; - // FIXME(kbobyrev): Instead of producing empty trigram for each identifier, - // just use True Iterator on the query side when the query string is empty. - add({{END_MARKER, END_MARKER, END_MARKER}}); - if (TwoHeads.size() == 2) add({{TwoHeads.front(), TwoHeads.back(), END_MARKER}}); From 3035e562594c6acb99ddab3376c5cfc0a87c0156 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 27 Aug 2018 15:38:49 +0000 Subject: [PATCH 092/686] [docs] Mention clangd-dev in clangd documentation Since the clangd-dev is intended to be the place for clangd-related discussions, we should point new users to this mailing list while probably mentioning cfe-dev, too. Reviewed by: ioeric Differential Revision: https://reviews.llvm.org/D51293 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340749 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/clangd.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/clangd.rst b/docs/clangd.rst index a03f2c150..e2d18fd41 100644 --- a/docs/clangd.rst +++ b/docs/clangd.rst @@ -111,7 +111,10 @@ extension to the protocol. Getting Involved ================== -A good place for interested contributors is the `Clang developer mailing list +A good place for interested contributors is the `Clangd developer mailing list +`_. For discussions with the +broader community on topics not only related to Clangd, use +`Clang developer mailing list `_. If you're also interested in contributing patches to :program:`Clangd`, take a look at the `LLVM Developer Policy From 2957125ae6ca0106b3271450874e70c90e7f44c9 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 27 Aug 2018 17:26:43 +0000 Subject: [PATCH 093/686] Cleanup after rL340729 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340759 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/DexIndexTests.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index eb8c1e0c9..68ef1a130 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -335,35 +335,34 @@ trigramsAre(std::initializer_list Trigrams) { TEST(DexIndexTrigrams, IdentifierTrigrams) { EXPECT_THAT(generateIdentifierTrigrams("X86"), - trigramsAre({"x86", "x$$", "x8$", "$$$"})); + trigramsAre({"x86", "x$$", "x8$"})); - EXPECT_THAT(generateIdentifierTrigrams("nl"), - trigramsAre({"nl$", "n$$", "$$$"})); + EXPECT_THAT(generateIdentifierTrigrams("nl"), trigramsAre({"nl$", "n$$"})); - EXPECT_THAT(generateIdentifierTrigrams("n"), trigramsAre({"n$$", "$$$"})); + EXPECT_THAT(generateIdentifierTrigrams("n"), trigramsAre({"n$$"})); EXPECT_THAT(generateIdentifierTrigrams("clangd"), - trigramsAre({"c$$", "cl$", "cla", "lan", "ang", "ngd", "$$$"})); + trigramsAre({"c$$", "cl$", "cla", "lan", "ang", "ngd"})); EXPECT_THAT(generateIdentifierTrigrams("abc_def"), trigramsAre({"a$$", "abc", "abd", "ade", "bcd", "bde", "cde", - "def", "ab$", "ad$", "$$$"})); + "def", "ab$", "ad$"})); EXPECT_THAT(generateIdentifierTrigrams("a_b_c_d_e_"), trigramsAre({"a$$", "a_$", "a_b", "abc", "abd", "acd", "ace", - "bcd", "bce", "bde", "cde", "ab$", "$$$"})); + "bcd", "bce", "bde", "cde", "ab$"})); EXPECT_THAT(generateIdentifierTrigrams("unique_ptr"), trigramsAre({"u$$", "uni", "unp", "upt", "niq", "nip", "npt", "iqu", "iqp", "ipt", "que", "qup", "qpt", "uep", - "ept", "ptr", "un$", "up$", "$$$"})); + "ept", "ptr", "un$", "up$"})); - EXPECT_THAT(generateIdentifierTrigrams("TUDecl"), - trigramsAre({"t$$", "tud", "tde", "ude", "dec", "ecl", "tu$", - "td$", "$$$"})); + EXPECT_THAT( + generateIdentifierTrigrams("TUDecl"), + trigramsAre({"t$$", "tud", "tde", "ude", "dec", "ecl", "tu$", "td$"})); EXPECT_THAT(generateIdentifierTrigrams("IsOK"), - trigramsAre({"i$$", "iso", "iok", "sok", "is$", "io$", "$$$"})); + trigramsAre({"i$$", "iso", "iok", "sok", "is$", "io$"})); EXPECT_THAT( generateIdentifierTrigrams("abc_defGhij__klm"), @@ -372,7 +371,7 @@ TEST(DexIndexTrigrams, IdentifierTrigrams) { "cde", "cdg", "cdk", "cgh", "cgk", "def", "deg", "dek", "dgh", "dgk", "dkl", "efg", "efk", "egh", "egk", "ekl", "fgh", "fgk", "fkl", "ghi", "ghk", "gkl", "hij", "hik", - "hkl", "ijk", "ikl", "jkl", "klm", "ab$", "ad$", "$$$"})); + "hkl", "ijk", "ikl", "jkl", "klm", "ab$", "ad$"})); } TEST(DexIndexTrigrams, QueryTrigrams) { From fcec6dce83c8292f43d5d192a9d7945272fd5a92 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Tue, 28 Aug 2018 07:48:28 +0000 Subject: [PATCH 094/686] [clang-tidy] Abseil: no namepsace check This check ensures that users of Abseil do not open namespace absl in their code, as that violates our compatibility guidelines. AbseilMatcher.h written by Hugo Gonzalez. Patch by Deanna Garcia! git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340800 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/AbseilMatcher.h | 51 +++++++++++++++++++ clang-tidy/abseil/AbseilTidyModule.cpp | 2 + clang-tidy/abseil/CMakeLists.txt | 1 + clang-tidy/abseil/NoNamespaceCheck.cpp | 42 +++++++++++++++ clang-tidy/abseil/NoNamespaceCheck.h | 36 +++++++++++++ docs/ReleaseNotes.rst | 6 +++ .../clang-tidy/checks/abseil-no-namespace.rst | 21 ++++++++ docs/clang-tidy/checks/list.rst | 1 + test/clang-tidy/Inputs/absl/external-file.h | 1 + .../Inputs/absl/strings/internal-file.h | 1 + test/clang-tidy/abseil-no-namespace.cpp | 24 +++++++++ 11 files changed, 186 insertions(+) create mode 100644 clang-tidy/abseil/AbseilMatcher.h create mode 100644 clang-tidy/abseil/NoNamespaceCheck.cpp create mode 100644 clang-tidy/abseil/NoNamespaceCheck.h create mode 100644 docs/clang-tidy/checks/abseil-no-namespace.rst create mode 100644 test/clang-tidy/Inputs/absl/external-file.h create mode 100644 test/clang-tidy/Inputs/absl/strings/internal-file.h create mode 100644 test/clang-tidy/abseil-no-namespace.cpp diff --git a/clang-tidy/abseil/AbseilMatcher.h b/clang-tidy/abseil/AbseilMatcher.h new file mode 100644 index 000000000..ef08bb86b --- /dev/null +++ b/clang-tidy/abseil/AbseilMatcher.h @@ -0,0 +1,51 @@ +//===- AbseilMatcher.h - clang-tidy ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +namespace clang { +namespace ast_matchers { + +/// Matches AST nodes that were found within Abseil files. +/// +/// Example matches Y but not X +/// (matcher = cxxRecordDecl(isInAbseilFile()) +/// \code +/// #include "absl/strings/internal-file.h" +/// class X {}; +/// \endcode +/// absl/strings/internal-file.h: +/// \code +/// class Y {}; +/// \endcode +/// +/// Usable as: Matcher, Matcher, Matcher, +/// Matcher + +AST_POLYMORPHIC_MATCHER(isInAbseilFile, + AST_POLYMORPHIC_SUPPORTED_TYPES( + Decl, Stmt, TypeLoc, NestedNameSpecifierLoc)) { + auto &SourceManager = Finder->getASTContext().getSourceManager(); + SourceLocation Loc = Node.getBeginLoc(); + if (Loc.isInvalid()) + return false; + const FileEntry *FileEntry = + SourceManager.getFileEntryForID(SourceManager.getFileID(Loc)); + if (!FileEntry) + return false; + StringRef Filename = FileEntry->getName(); + llvm::Regex RE( + "absl/(algorithm|base|container|debugging|memory|meta|numeric|strings|" + "synchronization|time|types|utility)"); + return RE.match(Filename); +} + +} // namespace ast_matchers +} // namespace clang diff --git a/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tidy/abseil/AbseilTidyModule.cpp index c4f27a492..178a7a610 100644 --- a/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tidy/abseil/AbseilTidyModule.cpp @@ -12,6 +12,7 @@ #include "../ClangTidyModuleRegistry.h" #include "DurationDivisionCheck.h" #include "FasterStrsplitDelimiterCheck.h" +#include "NoNamespaceCheck.h" #include "StringFindStartswithCheck.h" namespace clang { @@ -25,6 +26,7 @@ class AbseilModule : public ClangTidyModule { "abseil-duration-division"); CheckFactories.registerCheck( "abseil-faster-strsplit-delimiter"); + CheckFactories.registerCheck("abseil-no-namespace"); CheckFactories.registerCheck( "abseil-string-find-startswith"); } diff --git a/clang-tidy/abseil/CMakeLists.txt b/clang-tidy/abseil/CMakeLists.txt index 0a2e84b63..601dceb12 100644 --- a/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tidy/abseil/CMakeLists.txt @@ -4,6 +4,7 @@ add_clang_library(clangTidyAbseilModule AbseilTidyModule.cpp DurationDivisionCheck.cpp FasterStrsplitDelimiterCheck.cpp + NoNamespaceCheck.cpp StringFindStartswithCheck.cpp LINK_LIBS diff --git a/clang-tidy/abseil/NoNamespaceCheck.cpp b/clang-tidy/abseil/NoNamespaceCheck.cpp new file mode 100644 index 000000000..fc9e6980d --- /dev/null +++ b/clang-tidy/abseil/NoNamespaceCheck.cpp @@ -0,0 +1,42 @@ +//===--- NoNamespaceCheck.cpp - clang-tidy---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NoNamespaceCheck.h" +#include "AbseilMatcher.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +void NoNamespaceCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + Finder->addMatcher( + namespaceDecl(hasName("::absl"), unless(isInAbseilFile())) + .bind("abslNamespace"), + this); +} + +void NoNamespaceCheck::check(const MatchFinder::MatchResult &Result) { + const auto *abslNamespaceDecl = + Result.Nodes.getNodeAs("abslNamespace"); + + diag(abslNamespaceDecl->getLocation(), + "namespace 'absl' is reserved for implementation of the Abseil library " + "and should not be opened in user code"); +} + +} // namespace abseil +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/abseil/NoNamespaceCheck.h b/clang-tidy/abseil/NoNamespaceCheck.h new file mode 100644 index 000000000..00686d165 --- /dev/null +++ b/clang-tidy/abseil/NoNamespaceCheck.h @@ -0,0 +1,36 @@ +//===--- NoNamespaceCheck.h - clang-tidy-------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_NONAMESPACECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_NONAMESPACECHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace abseil { + +/// This check ensures users don't open namespace absl, as that violates +/// Abseil's compatibility guidelines. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-no-namespace.html +class NoNamespaceCheck : public ClangTidyCheck { +public: + NoNamespaceCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_NONAMESPACECHECK_H diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index 3d9389d82..6f39c924e 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -70,6 +70,12 @@ Improvements to clang-tidy Finds instances of ``absl::StrSplit()`` or ``absl::MaxSplits()`` where the delimiter is a single character string literal and replaces with a character. +- New :doc:`abseil-no-namespace + ` check. + + Ensures code does not open ``namespace absl`` as that violates Abseil's + compatibility guidelines. + - New :doc:`readability-magic-numbers ` check. diff --git a/docs/clang-tidy/checks/abseil-no-namespace.rst b/docs/clang-tidy/checks/abseil-no-namespace.rst new file mode 100644 index 000000000..46e79cfc1 --- /dev/null +++ b/docs/clang-tidy/checks/abseil-no-namespace.rst @@ -0,0 +1,21 @@ +.. title:: clang-tidy - abseil-no-namespace + +abseil-no-namespace +=================== + +Ensures code does not open ``namespace absl`` as that violates Abseil's +compatibility guidelines. Code should not open ``namespace absl`` as that +conflicts with Abseil's compatibility guidelines and may result in breakage. + +Any code that uses: + +.. code-block:: c++ + + namespace absl { + ... + } + +will be prompted with a warning. + +See `the full Abseil compatibility guidelines `_ for more information. diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index 3ecb17c25..c52f030a6 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -6,6 +6,7 @@ Clang-Tidy Checks .. toctree:: abseil-duration-division abseil-faster-strsplit-delimiter + abseil-no-namespace abseil-string-find-startswith android-cloexec-accept android-cloexec-accept4 diff --git a/test/clang-tidy/Inputs/absl/external-file.h b/test/clang-tidy/Inputs/absl/external-file.h new file mode 100644 index 000000000..94f6687b5 --- /dev/null +++ b/test/clang-tidy/Inputs/absl/external-file.h @@ -0,0 +1 @@ +namespace absl {} diff --git a/test/clang-tidy/Inputs/absl/strings/internal-file.h b/test/clang-tidy/Inputs/absl/strings/internal-file.h new file mode 100644 index 000000000..94f6687b5 --- /dev/null +++ b/test/clang-tidy/Inputs/absl/strings/internal-file.h @@ -0,0 +1 @@ +namespace absl {} diff --git a/test/clang-tidy/abseil-no-namespace.cpp b/test/clang-tidy/abseil-no-namespace.cpp new file mode 100644 index 000000000..78821c373 --- /dev/null +++ b/test/clang-tidy/abseil-no-namespace.cpp @@ -0,0 +1,24 @@ +// RUN: %check_clang_tidy %s abseil-no-namespace %t -- -- -I %S/Inputs +// RUN: clang-tidy -checks='-*, abseil-no-namespace' -header-filter='.*' %s -- -I %S/Inputs 2>&1 | FileCheck %s + +/// Warning will not be triggered on internal Abseil code that is included. +#include "absl/strings/internal-file.h" +// CHECK-NOT: warning: + +/// Warning will be triggered on code that is not internal that is included. +#include "absl/external-file.h" +// CHECK: absl/external-file.h:1:11: warning: namespace 'absl' is reserved + +namespace absl {} +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: namespace 'absl' is reserved for implementation of the Abseil library and should not be opened in user code [abseil-no-namespace] + +namespace absl { +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: namespace 'absl' +namespace std { +int i = 5; +} +} + +// Things that shouldn't trigger the check +int i = 5; +namespace std {} From a13cba2c2e398523104049e6a2e9029ba9fcf6c0 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Tue, 28 Aug 2018 10:57:45 +0000 Subject: [PATCH 095/686] [clangd] Add some trace::Spans. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340815 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 1 + clangd/TUScheduler.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index abd2d895b..708a27682 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -495,6 +495,7 @@ void ClangdServer::consumeDiagnostics(PathRef File, DocVersion Version, } tooling::CompileCommand ClangdServer::getCompileCommand(PathRef File) { + trace::Span Span("GetCompileCommand"); llvm::Optional C = CDB.getCompileCommand(File); if (!C) // FIXME: Suppress diagnostics? Let the user know? C = CDB.getFallbackCommand(File); diff --git a/clangd/TUScheduler.cpp b/clangd/TUScheduler.cpp index e61a54d4f..8db8b2c32 100644 --- a/clangd/TUScheduler.cpp +++ b/clangd/TUScheduler.cpp @@ -417,6 +417,7 @@ void ASTWorker::update( // Note *AST can be still be null if buildAST fails. if (*AST) { OnUpdated((*AST)->getDiagnostics()); + trace::Span Span("Running main AST callback"); Callbacks.onMainAST(FileName, **AST); DiagsWereReported = true; } From 737bee028cc56419ca61cde3b2fa5b0dc2acfd6d Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Tue, 28 Aug 2018 11:04:07 +0000 Subject: [PATCH 096/686] [clangd] Remove unused parameter. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340816 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/XRefs.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index 97fee3af4..a4cbf549c 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -77,8 +77,7 @@ class DeclarationAndMacrosFinder : public index::IndexDataConsumer { Preprocessor &PP; public: - DeclarationAndMacrosFinder(raw_ostream &OS, - const SourceLocation &SearchedLocation, + DeclarationAndMacrosFinder(const SourceLocation &SearchedLocation, ASTContext &AST, Preprocessor &PP) : SearchedLocation(SearchedLocation), AST(AST), PP(PP) {} @@ -163,8 +162,8 @@ struct IdentifiedSymbol { }; IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos) { - auto DeclMacrosFinder = DeclarationAndMacrosFinder( - llvm::errs(), Pos, AST.getASTContext(), AST.getPreprocessor()); + auto DeclMacrosFinder = DeclarationAndMacrosFinder(Pos, AST.getASTContext(), + AST.getPreprocessor()); index::IndexingOptions IndexOpts; IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All; @@ -324,7 +323,7 @@ class DocumentHighlightsFinder : public index::IndexDataConsumer { const ASTContext &AST; public: - DocumentHighlightsFinder(raw_ostream &OS, ASTContext &AST, Preprocessor &PP, + DocumentHighlightsFinder(ASTContext &AST, Preprocessor &PP, std::vector &Decls) : Decls(Decls), AST(AST) {} std::vector takeHighlights() { @@ -389,7 +388,7 @@ std::vector findDocumentHighlights(ParsedAST &AST, std::vector SelectedDecls = Symbols.Decls; DocumentHighlightsFinder DocHighlightsFinder( - llvm::errs(), AST.getASTContext(), AST.getPreprocessor(), SelectedDecls); + AST.getASTContext(), AST.getPreprocessor(), SelectedDecls); index::IndexingOptions IndexOpts; IndexOpts.SystemSymbolFilter = From 43acc28e0453d45d17490e2c865fcd86ca5b3f6a Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 28 Aug 2018 13:15:50 +0000 Subject: [PATCH 097/686] [clangd] Use buffered llvm::errs() in the clangd binary. Summary: Unbuffered stream can cause significant (non-deterministic) latency for the logger. Reviewers: sammccall Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51349 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340822 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/tool/ClangdMain.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 39f218535..f22dd35e9 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -259,6 +259,9 @@ int main(int argc, char *argv[]) { if (Tracer) TracingSession.emplace(*Tracer); + // Use buffered stream to stderr (we still flush each log message). Unbuffered + // stream can cause significant (non-deterministic) latency for the logger. + llvm::errs().SetBuffered(); JSONOutput Out(llvm::outs(), llvm::errs(), LogLevel, InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, PrettyPrint); From 6a190314317a2f9b158c8080452317d684b33a45 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 28 Aug 2018 14:55:05 +0000 Subject: [PATCH 098/686] [clangd] Switch to Dex by default for the static index Dex is now mature enough to be used as the default static index. This patch performs the switch but introduces a hidden flag to allow users fallback to Mem in case something happens. Reviewed by: ioeric Differential Revision: https://reviews.llvm.org/D51352 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340828 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/tool/ClangdMain.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index f22dd35e9..52c76400f 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -29,10 +29,11 @@ using namespace clang; using namespace clang::clangd; +// FIXME: remove this option when Dex is stable enough. static llvm::cl::opt UseDex("use-dex-index", llvm::cl::desc("Use experimental Dex static index."), - llvm::cl::init(false), llvm::cl::Hidden); + llvm::cl::init(true), llvm::cl::Hidden); namespace { From d963991b7e6439437139638bfef2cb309402c09a Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Wed, 29 Aug 2018 11:17:31 +0000 Subject: [PATCH 099/686] Introduce the abseil-str-cat-append check. This flags uses of absl::StrCat when absl::StrAppend should be used instead. Patch by Hugo Gonzalez and Benjamin Kramer. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340915 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/AbseilTidyModule.cpp | 3 + clang-tidy/abseil/CMakeLists.txt | 1 + clang-tidy/abseil/StrCatAppendCheck.cpp | 102 ++++++++++++++ clang-tidy/abseil/StrCatAppendCheck.h | 36 +++++ docs/ReleaseNotes.rst | 6 + .../checks/abseil-str-cat-append.rst | 17 +++ docs/clang-tidy/checks/list.rst | 1 + test/clang-tidy/abseil-str-cat-append.cpp | 129 ++++++++++++++++++ 8 files changed, 295 insertions(+) create mode 100644 clang-tidy/abseil/StrCatAppendCheck.cpp create mode 100644 clang-tidy/abseil/StrCatAppendCheck.h create mode 100644 docs/clang-tidy/checks/abseil-str-cat-append.rst create mode 100644 test/clang-tidy/abseil-str-cat-append.cpp diff --git a/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tidy/abseil/AbseilTidyModule.cpp index 178a7a610..e70184bfb 100644 --- a/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tidy/abseil/AbseilTidyModule.cpp @@ -14,6 +14,7 @@ #include "FasterStrsplitDelimiterCheck.h" #include "NoNamespaceCheck.h" #include "StringFindStartswithCheck.h" +#include "StrCatAppendCheck.h" namespace clang { namespace tidy { @@ -29,6 +30,8 @@ class AbseilModule : public ClangTidyModule { CheckFactories.registerCheck("abseil-no-namespace"); CheckFactories.registerCheck( "abseil-string-find-startswith"); + CheckFactories.registerCheck( + "abseil-str-cat-append"); } }; diff --git a/clang-tidy/abseil/CMakeLists.txt b/clang-tidy/abseil/CMakeLists.txt index 601dceb12..97932013a 100644 --- a/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tidy/abseil/CMakeLists.txt @@ -6,6 +6,7 @@ add_clang_library(clangTidyAbseilModule FasterStrsplitDelimiterCheck.cpp NoNamespaceCheck.cpp StringFindStartswithCheck.cpp + StrCatAppendCheck.cpp LINK_LIBS clangAST diff --git a/clang-tidy/abseil/StrCatAppendCheck.cpp b/clang-tidy/abseil/StrCatAppendCheck.cpp new file mode 100644 index 000000000..25b9d17e8 --- /dev/null +++ b/clang-tidy/abseil/StrCatAppendCheck.cpp @@ -0,0 +1,102 @@ +//===--- StrCatAppendCheck.cpp - clang-tidy--------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "StrCatAppendCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +namespace { +// Skips any combination of temporary materialization, temporary binding and +// implicit casting. +AST_MATCHER_P(Stmt, IgnoringTemporaries, ast_matchers::internal::Matcher, + InnerMatcher) { + const Stmt *E = &Node; + while (true) { + if (const auto *MTE = dyn_cast(E)) + E = MTE->getTemporary(); + if (const auto *BTE = dyn_cast(E)) + E = BTE->getSubExpr(); + if (const auto *ICE = dyn_cast(E)) + E = ICE->getSubExpr(); + else + break; + } + + return InnerMatcher.matches(*E, Finder, Builder); +} + +} // namespace + +// TODO: str += StrCat(...) +// str.append(StrCat(...)) + +void StrCatAppendCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + const auto StrCat = functionDecl(hasName("::absl::StrCat")); + // The arguments of absl::StrCat are implicitly converted to AlphaNum. This + // matches to the arguments because of that behavior. + const auto AlphaNum = IgnoringTemporaries(cxxConstructExpr( + argumentCountIs(1), hasType(cxxRecordDecl(hasName("::absl::AlphaNum"))), + hasArgument(0, ignoringImpCasts(declRefExpr(to(equalsBoundNode("LHS")), + expr().bind("Arg0")))))); + + const auto HasAnotherReferenceToLhs = + callExpr(hasAnyArgument(expr(hasDescendant(declRefExpr( + to(equalsBoundNode("LHS")), unless(equalsBoundNode("Arg0"))))))); + + // Now look for calls to operator= with an object on the LHS and a call to + // StrCat on the RHS. The first argument of the StrCat call should be the same + // as the LHS. Ignore calls from template instantiations. + Finder->addMatcher( + cxxOperatorCallExpr( + unless(isInTemplateInstantiation()), hasOverloadedOperatorName("="), + hasArgument(0, declRefExpr(to(decl().bind("LHS")))), + hasArgument(1, IgnoringTemporaries( + callExpr(callee(StrCat), hasArgument(0, AlphaNum), + unless(HasAnotherReferenceToLhs)) + .bind("Call")))) + .bind("Op"), + this); +} + +void StrCatAppendCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Op = Result.Nodes.getNodeAs("Op"); + const auto *Call = Result.Nodes.getNodeAs("Call"); + assert(Op != nullptr && Call != nullptr && "Matcher does not work as expected"); + + // Handles the case 'x = absl::StrCat(x)', which has no effect. + if (Call->getNumArgs() == 1) { + diag(Op->getBeginLoc(), "call to 'absl::StrCat' has no effect"); + return; + } + + // Emit a warning and emit fixits to go from + // x = absl::StrCat(x, ...) + // to + // absl::StrAppend(&x, ...) + diag(Op->getBeginLoc(), + "call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a " + "string to avoid a performance penalty") + << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(Op->getBeginLoc(), + Call->getCallee()->getEndLoc()), + "StrAppend") + << FixItHint::CreateInsertion(Call->getArg(0)->getBeginLoc(), "&"); +} + +} // namespace abseil +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/abseil/StrCatAppendCheck.h b/clang-tidy/abseil/StrCatAppendCheck.h new file mode 100644 index 000000000..eaa750daf --- /dev/null +++ b/clang-tidy/abseil/StrCatAppendCheck.h @@ -0,0 +1,36 @@ +//===--- StrCatAppendCheck.h - clang-tidy------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRCATAPPENDCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRCATAPPENDCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace abseil { + +/// Flags uses of absl::StrCat to append to a string. Suggests absl::StrAppend +/// should be used instead. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-str-cat-append.html +class StrCatAppendCheck : public ClangTidyCheck { +public: + StrCatAppendCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRCATAPPENDCHECK_H diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index 6f39c924e..d1d7f4a1f 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -76,6 +76,12 @@ Improvements to clang-tidy Ensures code does not open ``namespace absl`` as that violates Abseil's compatibility guidelines. +- New :doc:`abseil-str-cat-append + ` check. + + Flags uses of ``absl::StrCat()`` to append to a ``std::string``. Suggests + ``absl::StrAppend()`` should be used instead. + - New :doc:`readability-magic-numbers ` check. diff --git a/docs/clang-tidy/checks/abseil-str-cat-append.rst b/docs/clang-tidy/checks/abseil-str-cat-append.rst new file mode 100644 index 000000000..7ab1069b5 --- /dev/null +++ b/docs/clang-tidy/checks/abseil-str-cat-append.rst @@ -0,0 +1,17 @@ +.. title:: clang-tidy - abseil-str-cat-append + +abseil-str-cat-append +===================== + +Flags uses of ``absl::StrCat()`` to append to a ``std::string``. Suggests +``absl::StrAppend()`` should be used instead. + +The extra calls cause unnecessary temporary strings to be constructed. Removing +them makes the code smaller and faster. + +.. code-block:: c++ + + a = absl::StrCat(a, b); // Use absl::StrAppend(&a, b) instead. + +Does not diagnose cases where ``abls::StrCat()`` is used as a template +argument for a functor. diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index c52f030a6..bde9285fb 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -8,6 +8,7 @@ Clang-Tidy Checks abseil-faster-strsplit-delimiter abseil-no-namespace abseil-string-find-startswith + abseil-str-cat-append android-cloexec-accept android-cloexec-accept4 android-cloexec-creat diff --git a/test/clang-tidy/abseil-str-cat-append.cpp b/test/clang-tidy/abseil-str-cat-append.cpp new file mode 100644 index 000000000..9a1273388 --- /dev/null +++ b/test/clang-tidy/abseil-str-cat-append.cpp @@ -0,0 +1,129 @@ +// RUN: %check_clang_tidy %s abseil-str-cat-append %t -- -- -I%S -std=c++11 + +typedef unsigned __INT16_TYPE__ char16; +typedef unsigned __INT32_TYPE__ char32; +typedef __SIZE_TYPE__ size; + +namespace std { +template +class allocator {}; +template +class char_traits {}; +template +struct basic_string { + typedef basic_string _Type; + basic_string(); + basic_string(const C* p, const A& a = A()); + + const C* c_str() const; + const C* data() const; + + _Type& append(const C* s); + _Type& append(const C* s, size n); + _Type& assign(const C* s); + _Type& assign(const C* s, size n); + + int compare(const _Type&) const; + int compare(const C* s) const; + int compare(size pos, size len, const _Type&) const; + int compare(size pos, size len, const C* s) const; + + size find(const _Type& str, size pos = 0) const; + size find(const C* s, size pos = 0) const; + size find(const C* s, size pos, size n) const; + + _Type& insert(size pos, const _Type& str); + _Type& insert(size pos, const C* s); + _Type& insert(size pos, const C* s, size n); + + _Type& operator+=(const _Type& str); + _Type& operator+=(const C* s); + _Type& operator=(const _Type& str); + _Type& operator=(const C* s); +}; + +typedef basic_string, std::allocator> string; +typedef basic_string, + std::allocator> + wstring; +typedef basic_string, std::allocator> + u16string; +typedef basic_string, std::allocator> + u32string; +} // namespace std + +std::string operator+(const std::string&, const std::string&); +std::string operator+(const std::string&, const char*); +std::string operator+(const char*, const std::string&); + +bool operator==(const std::string&, const std::string&); +bool operator==(const std::string&, const char*); +bool operator==(const char*, const std::string&); + +namespace llvm { +struct StringRef { + StringRef(const char* p); + StringRef(const std::string&); +}; +} // namespace llvm + +namespace absl { + +struct AlphaNum { + AlphaNum(int i); + AlphaNum(double f); + AlphaNum(const char* c_str); + AlphaNum(const std::string& str); + + private: + AlphaNum(const AlphaNum&); + AlphaNum& operator=(const AlphaNum&); +}; + +std::string StrCat(const AlphaNum& A); +std::string StrCat(const AlphaNum& A, const AlphaNum& B); + +template +void Foo(A& a) { + a = StrCat(a); +} + +void Bar() { + std::string A, B; + Foo(A); + + std::string C = StrCat(A); + A = StrCat(A); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: call to 'absl::StrCat' has no effect + A = StrCat(A, B); +// CHECK-MESSAGES: [[@LINE-1]]:3: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty +// CHECK-FIXES: {{^}} StrAppend(&A, B); + B = StrCat(A, B); + +#define M(X) X = StrCat(X, A) + M(B); +// CHECK-MESSAGES: [[@LINE-1]]:5: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty +// CHECK-FIXES: #define M(X) X = StrCat(X, A) +} + +void Regression_SelfAppend() { + std::string A; + A = StrCat(A, A); +} + +} // namespace absl + +void OutsideAbsl() { + std::string A, B; + A = absl::StrCat(A, B); +// CHECK-MESSAGES: [[@LINE-1]]:3: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty +// CHECK-FIXES: {{^}} StrAppend(&A, B); +} + +void OutisdeUsingAbsl() { + std::string A, B; + using absl::StrCat; + A = StrCat(A, B); +// CHECK-MESSAGES: [[@LINE-1]]:3: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty +// CHECK-FIXES: {{^}} StrAppend(&A, B); +} From da6b2a62038abc89bb4b1ee9bc1ad08aa1853933 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Wed, 29 Aug 2018 11:29:07 +0000 Subject: [PATCH 100/686] Introduce the abseil-redundant-strcat-calls check. This flags redundant calls to absl::StrCat where the result is being passed to another call to absl::StrCat or absl::StrAppend. Patch by Hugo Gonzalez and Samuel Benzaquen. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340918 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/AbseilTidyModule.cpp | 3 + clang-tidy/abseil/CMakeLists.txt | 1 + .../abseil/RedundantStrcatCallsCheck.cpp | 140 +++++++++++++ clang-tidy/abseil/RedundantStrcatCallsCheck.h | 39 ++++ docs/ReleaseNotes.rst | 6 + .../checks/abseil-redundant-strcat-calls.rst | 26 +++ docs/clang-tidy/checks/list.rst | 1 + .../abseil-redundant-strcat-calls.cpp | 188 ++++++++++++++++++ 8 files changed, 404 insertions(+) create mode 100644 clang-tidy/abseil/RedundantStrcatCallsCheck.cpp create mode 100644 clang-tidy/abseil/RedundantStrcatCallsCheck.h create mode 100644 docs/clang-tidy/checks/abseil-redundant-strcat-calls.rst create mode 100644 test/clang-tidy/abseil-redundant-strcat-calls.cpp diff --git a/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tidy/abseil/AbseilTidyModule.cpp index e70184bfb..e7e0a2bdc 100644 --- a/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tidy/abseil/AbseilTidyModule.cpp @@ -13,6 +13,7 @@ #include "DurationDivisionCheck.h" #include "FasterStrsplitDelimiterCheck.h" #include "NoNamespaceCheck.h" +#include "RedundantStrcatCallsCheck.h" #include "StringFindStartswithCheck.h" #include "StrCatAppendCheck.h" @@ -28,6 +29,8 @@ class AbseilModule : public ClangTidyModule { CheckFactories.registerCheck( "abseil-faster-strsplit-delimiter"); CheckFactories.registerCheck("abseil-no-namespace"); + CheckFactories.registerCheck( + "abseil-redundant-strcat-calls"); CheckFactories.registerCheck( "abseil-string-find-startswith"); CheckFactories.registerCheck( diff --git a/clang-tidy/abseil/CMakeLists.txt b/clang-tidy/abseil/CMakeLists.txt index 97932013a..f36c0d979 100644 --- a/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tidy/abseil/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_library(clangTidyAbseilModule DurationDivisionCheck.cpp FasterStrsplitDelimiterCheck.cpp NoNamespaceCheck.cpp + RedundantStrcatCallsCheck.cpp StringFindStartswithCheck.cpp StrCatAppendCheck.cpp diff --git a/clang-tidy/abseil/RedundantStrcatCallsCheck.cpp b/clang-tidy/abseil/RedundantStrcatCallsCheck.cpp new file mode 100644 index 000000000..19806685f --- /dev/null +++ b/clang-tidy/abseil/RedundantStrcatCallsCheck.cpp @@ -0,0 +1,140 @@ +//===--- RedundantStrcatCallsCheck.cpp - clang-tidy------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RedundantStrcatCallsCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +// TODO: Features to add to the check: +// - Make it work if num_args > 26. +// - Remove empty literal string arguments. +// - Collapse consecutive literal string arguments into one (remove the ,). +// - Replace StrCat(a + b) -> StrCat(a, b) if a or b are strings. +// - Make it work in macros if the outer and inner StrCats are both in the +// argument. + +void RedundantStrcatCallsCheck::registerMatchers(MatchFinder* Finder) { + if (!getLangOpts().CPlusPlus) + return; + const auto CallToStrcat = + callExpr(callee(functionDecl(hasName("::absl::StrCat")))); + const auto CallToStrappend = + callExpr(callee(functionDecl(hasName("::absl::StrAppend")))); + // Do not match StrCat() calls that are descendants of other StrCat calls. + // Those are handled on the ancestor call. + const auto CallToEither = callExpr( + callee(functionDecl(hasAnyName("::absl::StrCat", "::absl::StrAppend")))); + Finder->addMatcher( + callExpr(CallToStrcat, unless(hasAncestor(CallToEither))).bind("StrCat"), + this); + Finder->addMatcher(CallToStrappend.bind("StrAppend"), this); +} + +namespace { + +struct StrCatCheckResult { + int NumCalls = 0; + std::vector Hints; +}; + +void RemoveCallLeaveArgs(const CallExpr* Call, StrCatCheckResult* CheckResult) { + // Remove 'Foo(' + CheckResult->Hints.push_back( + FixItHint::CreateRemoval(CharSourceRange::getCharRange( + Call->getBeginLoc(), Call->getArg(0)->getBeginLoc()))); + // Remove the ')' + CheckResult->Hints.push_back( + FixItHint::CreateRemoval(CharSourceRange::getCharRange( + Call->getRParenLoc(), Call->getEndLoc().getLocWithOffset(1)))); +} + +const clang::CallExpr* ProcessArgument(const Expr* Arg, + const MatchFinder::MatchResult& Result, + StrCatCheckResult* CheckResult) { + const auto IsAlphanum = hasDeclaration(cxxMethodDecl(hasName("AlphaNum"))); + static const auto* const Strcat = new auto(hasName("::absl::StrCat")); + const auto IsStrcat = cxxBindTemporaryExpr( + has(callExpr(callee(functionDecl(*Strcat))).bind("StrCat"))); + if (const auto* SubStrcatCall = selectFirst( + "StrCat", + match(stmt(anyOf( + cxxConstructExpr(IsAlphanum, hasArgument(0, IsStrcat)), + IsStrcat)), + *Arg->IgnoreParenImpCasts(), *Result.Context))) { + RemoveCallLeaveArgs(SubStrcatCall, CheckResult); + return SubStrcatCall; + } + return nullptr; +} + +StrCatCheckResult ProcessCall(const CallExpr* RootCall, bool IsAppend, + const MatchFinder::MatchResult& Result) { + StrCatCheckResult CheckResult; + std::deque CallsToProcess = {RootCall}; + + while (!CallsToProcess.empty()) { + ++CheckResult.NumCalls; + + const CallExpr* CallExpr = CallsToProcess.front(); + CallsToProcess.pop_front(); + + int StartArg = CallExpr == RootCall && IsAppend; + for (const auto *Arg : CallExpr->arguments()) { + if (StartArg-- > 0) + continue; + if (const clang::CallExpr* Sub = + ProcessArgument(Arg, Result, &CheckResult)) { + CallsToProcess.push_back(Sub); + } + } + } + return CheckResult; +} +} // namespace + +void RedundantStrcatCallsCheck::check(const MatchFinder::MatchResult& Result) { + bool IsAppend; + + const CallExpr* RootCall; + if ((RootCall = Result.Nodes.getNodeAs("StrCat"))) + IsAppend = false; + else if ((RootCall = Result.Nodes.getNodeAs("StrAppend"))) + IsAppend = true; + else + return; + + if (RootCall->getBeginLoc().isMacroID()) { + // Ignore calls within macros. + // In many cases the outer StrCat part of the macro and the inner StrCat is + // a macro argument. Removing the inner StrCat() converts one macro + // argument into many. + return; + } + + const StrCatCheckResult CheckResult = + ProcessCall(RootCall, IsAppend, Result); + if (CheckResult.NumCalls == 1) { + // Just one call, so nothing to fix. + return; + } + + diag(RootCall->getBeginLoc(), + "multiple calls to 'absl::StrCat' can be flattened into a single call") + << CheckResult.Hints; +} + +} // namespace abseil +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/abseil/RedundantStrcatCallsCheck.h b/clang-tidy/abseil/RedundantStrcatCallsCheck.h new file mode 100644 index 000000000..ede035479 --- /dev/null +++ b/clang-tidy/abseil/RedundantStrcatCallsCheck.h @@ -0,0 +1,39 @@ +//===--- RedundantStrcatCallsCheck.h - clang-tidy----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_REDUNDANTSTRCATCALLSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_REDUNDANTSTRCATCALLSCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace abseil { + +/// Flags redundant calls to absl::StrCat when the result is being passed to +/// another call of absl::StrCat/absl::StrAppend. Also suggests a fix to +/// collapse the calls. +/// Example: +/// StrCat(1, StrCat(2, 3)) ==> StrCat(1, 2, 3) +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-redundant-strcat-calls.html +class RedundantStrcatCallsCheck : public ClangTidyCheck { +public: + RedundantStrcatCallsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_REDUNDANTSTRCATCALLSCHECK_H diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index d1d7f4a1f..1e3a74abe 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -76,6 +76,12 @@ Improvements to clang-tidy Ensures code does not open ``namespace absl`` as that violates Abseil's compatibility guidelines. +- New :doc:`abseil-redundant-strcat-calls + ` check. + + Suggests removal of unnecessary calls to ``absl::StrCat`` when the result is + being passed to another ``absl::StrCat`` or ``absl::StrAppend``. + - New :doc:`abseil-str-cat-append ` check. diff --git a/docs/clang-tidy/checks/abseil-redundant-strcat-calls.rst b/docs/clang-tidy/checks/abseil-redundant-strcat-calls.rst new file mode 100644 index 000000000..d342bf377 --- /dev/null +++ b/docs/clang-tidy/checks/abseil-redundant-strcat-calls.rst @@ -0,0 +1,26 @@ +.. title:: clang-tidy - abseil-redundant-strcat-calls + +abseil-redundant-strcat-calls +============================= + +Suggests removal of unnecessary calls to ``absl::StrCat`` when the result is +being passed to another call to ``absl::StrCat`` or ``absl::StrAppend``. + +The extra calls cause unnecessary temporary strings to be constructed. Removing +them makes the code smaller and faster. + +Examples: + +.. code-block:: c++ + + std::string s = absl::StrCat("A", absl::StrCat("B", absl::StrCat("C", "D"))); + //before + + std::string s = absl::StrCat("A", "B", "C", "D"); + //after + + absl::StrAppend(&s, absl::StrCat("E", "F", "G")); + //before + + absl::StrAppend(&s, "E", "F", "G"); + //after diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index bde9285fb..35deccd8e 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -7,6 +7,7 @@ Clang-Tidy Checks abseil-duration-division abseil-faster-strsplit-delimiter abseil-no-namespace + abseil-redundant-strcat-calls abseil-string-find-startswith abseil-str-cat-append android-cloexec-accept diff --git a/test/clang-tidy/abseil-redundant-strcat-calls.cpp b/test/clang-tidy/abseil-redundant-strcat-calls.cpp new file mode 100644 index 000000000..2d7237971 --- /dev/null +++ b/test/clang-tidy/abseil-redundant-strcat-calls.cpp @@ -0,0 +1,188 @@ +// RUN: %check_clang_tidy %s abseil-redundant-strcat-calls %t + +int strlen(const char *); + +// Here we mimic the hierarchy of ::string. +// We need to do so because we are matching on the fully qualified name of the +// methods. +struct __sso_string_base {}; +namespace __gnu_cxx { +template +class __versa_string { + public: + const char *c_str() const; + const char *data() const; + int size() const; + int capacity() const; + int length() const; + bool empty() const; + char &operator[](int); + void clear(); + void resize(int); + int compare(const __versa_string &) const; +}; +} // namespace __gnu_cxx + +namespace std { +template +class char_traits {}; +template +class allocator {}; +} // namespace std + +template , + typename C = std::allocator> +class basic_string : public __gnu_cxx::__versa_string { + public: + basic_string(); + basic_string(const basic_string &); + basic_string(const char *, C = C()); + basic_string(const char *, int, C = C()); + basic_string(const basic_string &, int, int, C = C()); + ~basic_string(); + + basic_string &operator+=(const basic_string &); +}; + +template +basic_string operator+(const basic_string &, + const basic_string &); +template +basic_string operator+(const basic_string &, const char *); + +typedef basic_string string; + +bool operator==(const string &, const string &); +bool operator==(const string &, const char *); +bool operator==(const char *, const string &); + +bool operator!=(const string &, const string &); +bool operator<(const string &, const string &); +bool operator>(const string &, const string &); +bool operator<=(const string &, const string &); +bool operator>=(const string &, const string &); + +namespace std { +template , + typename _Alloc = allocator<_CharT>> +class basic_string; + +template +class basic_string { + public: + basic_string(); + basic_string(const basic_string &); + basic_string(const char *, const _Alloc & = _Alloc()); + basic_string(const char *, int, const _Alloc & = _Alloc()); + basic_string(const basic_string &, int, int, const _Alloc & = _Alloc()); + ~basic_string(); + + basic_string &operator+=(const basic_string &); + + unsigned size() const; + unsigned length() const; + bool empty() const; +}; + +typedef basic_string string; +} // namespace std + +namespace absl { + +class string_view { + public: + typedef std::char_traits traits_type; + + string_view(); + string_view(const char *); + string_view(const string &); + string_view(const char *, int); + string_view(string_view, int); + + template + explicit operator ::basic_string() const; + + const char *data() const; + int size() const; + int length() const; +}; + +bool operator==(string_view A, string_view B); + +struct AlphaNum { + AlphaNum(int i); + AlphaNum(double f); + AlphaNum(const char *c_str); + AlphaNum(const string &str); + AlphaNum(const string_view &pc); + + private: + AlphaNum(const AlphaNum &); + AlphaNum &operator=(const AlphaNum &); +}; + +string StrCat(const AlphaNum &A); +string StrCat(const AlphaNum &A, const AlphaNum &B); +string StrCat(const AlphaNum &A, const AlphaNum &B, const AlphaNum &C); +string StrCat(const AlphaNum &A, const AlphaNum &B, const AlphaNum &C, + const AlphaNum &D); + +// Support 5 or more arguments +template +string StrCat(const AlphaNum &A, const AlphaNum &B, const AlphaNum &C, + const AlphaNum &D, const AlphaNum &E, const AV &... args); + +void StrAppend(string *Dest, const AlphaNum &A); +void StrAppend(string *Dest, const AlphaNum &A, const AlphaNum &B); +void StrAppend(string *Dest, const AlphaNum &A, const AlphaNum &B, + const AlphaNum &C); +void StrAppend(string *Dest, const AlphaNum &A, const AlphaNum &B, + const AlphaNum &C, const AlphaNum &D); + +// Support 5 or more arguments +template +void StrAppend(string *Dest, const AlphaNum &A, const AlphaNum &B, + const AlphaNum &C, const AlphaNum &D, const AlphaNum &E, + const AV &... args); + +} // namespace absl + +using absl::AlphaNum; +using absl::StrAppend; +using absl::StrCat; + +void Positives() { + string S = StrCat(1, StrCat("A", StrCat(1.1))); + // CHECK-MESSAGES: [[@LINE-1]]:14: warning: multiple calls to 'absl::StrCat' can be flattened into a single call + + S = StrCat(StrCat(StrCat(StrCat(StrCat(1))))); + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: multiple calls to 'absl::StrCat' can be flattened into a single call + + // TODO: should trigger. The issue here is that in the current + // implementation we ignore any StrCat with StrCat ancestors. Therefore + // inserting anything in between calls will disable triggering the deepest + // ones. + // s = StrCat(Identity(StrCat(StrCat(1, 2), StrCat(3, 4)))); + + StrAppend(&S, 001, StrCat(1, 2, "3"), StrCat("FOO")); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: multiple calls to 'absl::StrCat' can be flattened into a single call + + StrAppend(&S, 001, StrCat(StrCat(1, 2), "3"), StrCat("FOO")); + // CHECK-MESSAGES: [[@LINE-1]]:3: warning: multiple calls to 'absl::StrCat' can be flattened into a single call + + // Too many args. Ignore for now. + S = StrCat(1, 2, StrCat(3, 4, 5, 6, 7), 8, 9, 10, + StrCat(11, 12, 13, 14, 15, 16, 17, 18), 19, 20, 21, 22, 23, 24, 25, + 26, 27); + // CHECK-MESSAGES: :[[@LINE-3]]:7: warning: multiple calls to 'absl::StrCat' can be flattened into a single call + StrAppend(&S, StrCat(1, 2, 3, 4, 5), StrCat(6, 7, 8, 9, 10)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple calls to 'absl::StrCat' can be flattened into a single call +} + +void Negatives() { + // One arg. It is used for conversion. Ignore. + string S = StrCat(1); + +#define A_MACRO(x, y, z) StrCat(x, y, z) + S = A_MACRO(1, 2, StrCat("A", "B")); +} From ec9bc1797b8df8fc4f44eecd40f0861335617655 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Wed, 29 Aug 2018 14:23:15 +0000 Subject: [PATCH 101/686] [clang-tidy] Add abseil-no-internal-dependencies check Finds instances where the user depends on internal details and warns them against doing so. Should not be run on internal Abseil files or Abseil source code. Patch by hugoeg! Differential Revision: https://reviews.llvm.org/D50542 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@340928 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/AbseilTidyModule.cpp | 3 ++ clang-tidy/abseil/CMakeLists.txt | 1 + .../abseil/NoInternalDependenciesCheck.cpp | 48 +++++++++++++++++++ .../abseil/NoInternalDependenciesCheck.h | 36 ++++++++++++++ docs/ReleaseNotes.rst | 5 ++ .../abseil-no-internal-dependencies.rst | 24 ++++++++++ docs/clang-tidy/checks/list.rst | 1 + test/clang-tidy/Inputs/absl/external-file.h | 7 ++- .../Inputs/absl/strings/internal-file.h | 34 ++++++++++++- .../abseil-no-internal-dependencies.cpp | 39 +++++++++++++++ 10 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 clang-tidy/abseil/NoInternalDependenciesCheck.cpp create mode 100644 clang-tidy/abseil/NoInternalDependenciesCheck.h create mode 100644 docs/clang-tidy/checks/abseil-no-internal-dependencies.rst create mode 100644 test/clang-tidy/abseil-no-internal-dependencies.cpp diff --git a/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tidy/abseil/AbseilTidyModule.cpp index e7e0a2bdc..b3de5c079 100644 --- a/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tidy/abseil/AbseilTidyModule.cpp @@ -12,6 +12,7 @@ #include "../ClangTidyModuleRegistry.h" #include "DurationDivisionCheck.h" #include "FasterStrsplitDelimiterCheck.h" +#include "NoInternalDependenciesCheck.h" #include "NoNamespaceCheck.h" #include "RedundantStrcatCallsCheck.h" #include "StringFindStartswithCheck.h" @@ -28,6 +29,8 @@ class AbseilModule : public ClangTidyModule { "abseil-duration-division"); CheckFactories.registerCheck( "abseil-faster-strsplit-delimiter"); + CheckFactories.registerCheck( + "abseil-no-internal-dependencies"); CheckFactories.registerCheck("abseil-no-namespace"); CheckFactories.registerCheck( "abseil-redundant-strcat-calls"); diff --git a/clang-tidy/abseil/CMakeLists.txt b/clang-tidy/abseil/CMakeLists.txt index f36c0d979..e268b34bf 100644 --- a/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tidy/abseil/CMakeLists.txt @@ -4,6 +4,7 @@ add_clang_library(clangTidyAbseilModule AbseilTidyModule.cpp DurationDivisionCheck.cpp FasterStrsplitDelimiterCheck.cpp + NoInternalDependenciesCheck.cpp NoNamespaceCheck.cpp RedundantStrcatCallsCheck.cpp StringFindStartswithCheck.cpp diff --git a/clang-tidy/abseil/NoInternalDependenciesCheck.cpp b/clang-tidy/abseil/NoInternalDependenciesCheck.cpp new file mode 100644 index 000000000..cb77e95fa --- /dev/null +++ b/clang-tidy/abseil/NoInternalDependenciesCheck.cpp @@ -0,0 +1,48 @@ +//===--- NoInternalDependenciesCheck.cpp - clang-tidy----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NoInternalDependenciesCheck.h" +#include "AbseilMatcher.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +void NoInternalDependenciesCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + // TODO: refactor matcher to be configurable or just match on any internal + // access from outside the enclosing namespace. + + Finder->addMatcher( + nestedNameSpecifierLoc(loc(specifiesNamespace(namespaceDecl( + matchesName("internal"), + hasParent(namespaceDecl(hasName("absl")))))), + unless(isInAbseilFile())) + .bind("InternalDep"), + this); +} + +void NoInternalDependenciesCheck::check(const MatchFinder::MatchResult &Result) { + const auto *InternalDependency = + Result.Nodes.getNodeAs("InternalDep"); + + diag(InternalDependency->getBeginLoc(), + "do not reference any 'internal' namespaces; those implementation " + "details are reserved to Abseil"); +} + +} // namespace abseil +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/abseil/NoInternalDependenciesCheck.h b/clang-tidy/abseil/NoInternalDependenciesCheck.h new file mode 100644 index 000000000..7e659df59 --- /dev/null +++ b/clang-tidy/abseil/NoInternalDependenciesCheck.h @@ -0,0 +1,36 @@ +//===--- NoInternalDependenciesCheck.h - clang-tidy----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_NOINTERNALDEPSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_NOINTERNALDEPSCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace abseil { + +/// Finds instances where the user depends on internal details and warns them +/// against doing so. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-no-internal-dependencies.html +class NoInternalDependenciesCheck : public ClangTidyCheck { +public: + NoInternalDependenciesCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_NOINTERNALDEPSCHECK_H diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index 1e3a74abe..c660bc762 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -70,6 +70,11 @@ Improvements to clang-tidy Finds instances of ``absl::StrSplit()`` or ``absl::MaxSplits()`` where the delimiter is a single character string literal and replaces with a character. +- New :doc:`abseil-no-internal-dependencies + ` check. + + Gives a warning if code using Abseil depends on internal details. + - New :doc:`abseil-no-namespace ` check. diff --git a/docs/clang-tidy/checks/abseil-no-internal-dependencies.rst b/docs/clang-tidy/checks/abseil-no-internal-dependencies.rst new file mode 100644 index 000000000..bcf68ffd2 --- /dev/null +++ b/docs/clang-tidy/checks/abseil-no-internal-dependencies.rst @@ -0,0 +1,24 @@ +subl.. title:: clang-tidy - abseil-no-internal-dependencies + +abseil-no-internal-dependencies +=============================== + +Warns if code using Abseil depends on internal details. If something is in a +namespace that includes the word “internal”, code is not allowed to depend upon +it beaucse it’s an implementation detail. They cannot friend it, include it, +you mention it or refer to it in any way. Doing so violates Abseil's +compatibility guidelines and may result in breakage. See +https://abseil.io/about/compatibility for more information. + +The following cases will result in warnings: + +.. code-block:: c++ + + absl::strings_internal::foo(); + // warning triggered on this line + class foo { + friend struct absl::container_internal::faa; + // warning triggered on this line + }; + absl::memory_internal::MakeUniqueResult(); + // warning triggered on this line diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index 35deccd8e..600969fbd 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -6,6 +6,7 @@ Clang-Tidy Checks .. toctree:: abseil-duration-division abseil-faster-strsplit-delimiter + abseil-no-internal-dependencies abseil-no-namespace abseil-redundant-strcat-calls abseil-string-find-startswith diff --git a/test/clang-tidy/Inputs/absl/external-file.h b/test/clang-tidy/Inputs/absl/external-file.h index 94f6687b5..11e2e4aa9 100644 --- a/test/clang-tidy/Inputs/absl/external-file.h +++ b/test/clang-tidy/Inputs/absl/external-file.h @@ -1 +1,6 @@ -namespace absl {} +namespace absl { +namespace base_internal { +void InternalFunction() {} +} // namespace base_internal +} //namespace absl +void DirectAccess2() { absl::base_internal::InternalFunction(); } diff --git a/test/clang-tidy/Inputs/absl/strings/internal-file.h b/test/clang-tidy/Inputs/absl/strings/internal-file.h index 94f6687b5..6014278e2 100644 --- a/test/clang-tidy/Inputs/absl/strings/internal-file.h +++ b/test/clang-tidy/Inputs/absl/strings/internal-file.h @@ -1 +1,33 @@ -namespace absl {} +namespace std { +struct string { + string(const char *); + ~string(); +}; +} // namespace std + +namespace absl { +std::string StringsFunction(std::string s1) { return s1; } +class SomeContainer {}; +namespace strings_internal { +void InternalFunction() {} +template P InternalTemplateFunction(P a) {} +} // namespace strings_internal + +namespace container_internal { +struct InternalStruct {}; +} // namespace container_internal +} // namespace absl + +// should not trigger warnings because inside Abseil files +void DirectAcessInternal() { + absl::strings_internal::InternalFunction(); + absl::strings_internal::InternalTemplateFunction("a"); +} + +class FriendUsageInternal { + friend struct absl::container_internal::InternalStruct; +}; + +namespace absl { +void OpeningNamespaceInternally() { strings_internal::InternalFunction(); } +} // namespace absl diff --git a/test/clang-tidy/abseil-no-internal-dependencies.cpp b/test/clang-tidy/abseil-no-internal-dependencies.cpp new file mode 100644 index 000000000..bd56cb04d --- /dev/null +++ b/test/clang-tidy/abseil-no-internal-dependencies.cpp @@ -0,0 +1,39 @@ +// RUN: %check_clang_tidy %s abseil-no-internal-dependencies %t, -- -- -I %S/Inputs +// RUN: clang-tidy -checks='-*, abseil-no-internal-dependencies' -header-filter='.*' %s -- -I %S/Inputs 2>&1 | FileCheck %s + +#include "absl/strings/internal-file.h" +// CHECK-NOT: warning: + +#include "absl/external-file.h" +// CHECK: absl/external-file.h:6:24: warning: do not reference any 'internal' namespaces; those implementation details are reserved to Abseil [abseil-no-internal-dependencies] + +void DirectAcess() { + absl::strings_internal::InternalFunction(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not reference any 'internal' namespaces; those implementation details are reserved to Abseil + + absl::strings_internal::InternalTemplateFunction("a"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not reference any 'internal' namespaces; those implementation details are reserved to Abseil +} + +class FriendUsage { + friend struct absl::container_internal::InternalStruct; + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: do not reference any 'internal' namespaces; those implementation details are reserved to Abseil +}; + +namespace absl { +void OpeningNamespace() { + strings_internal::InternalFunction(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not reference any 'internal' namespaces; those implementation details are reserved to Abseil +} +} // namespace absl + +// should not trigger warnings +void CorrectUsage() { + std::string Str = absl::StringsFunction("a"); + absl::SomeContainer b; +} + +namespace absl { +SomeContainer b; +std::string Str = absl::StringsFunction("a"); +} // namespace absl From dcd3d67b77946922ff3b133d062798bf2f3e5786 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Thu, 30 Aug 2018 08:44:27 +0000 Subject: [PATCH 102/686] [clang-tidy] fix check_clang_tidy to forbid mixing of CHECK-NOTES and CHECK-MESSAGES Summary: The check_clang_tidy.py script would allow mixing of `CHECK-NOTES` and `CHECK-MESSAGES` but running `FileCheck` for that would implicitly fail, because `CHECK-NOTES` bails out if there is a warning. That means a clang-tidy test can not mix these constructs to check warnings with `CHECK-MESSAGES` and notes with `CHECK-NOTES`. The script gives now a clear error if that happens. Reviewers: alexfh, aaron.ballman, lebedev.ri, hokein Reviewed By: lebedev.ri Subscribers: xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D51381 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341039 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clang-tidy/check_clang_tidy.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/clang-tidy/check_clang_tidy.py b/test/clang-tidy/check_clang_tidy.py index dadb84d57..d8086b16e 100755 --- a/test/clang-tidy/check_clang_tidy.py +++ b/test/clang-tidy/check_clang_tidy.py @@ -98,6 +98,9 @@ def main(): sys.exit('%s, %s or %s not found in the input' % (check_fixes_prefix, check_messages_prefix, check_notes_prefix) ) + if has_check_notes and has_check_messages: + sys.exit('Please use either CHECK-NOTES or CHECK-MESSAGES but not both') + # Remove the contents of the CHECK lines to avoid CHECKs matching on # themselves. We need to keep the comments to preserve line numbers while # avoiding empty lines which could potentially trigger formatting-related From 6d9e9a9b510efe6f60fcdd96c34e905cfea424a6 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 30 Aug 2018 11:23:58 +0000 Subject: [PATCH 103/686] [clangd] Implement iterator cost This patch introduces iterator cost concept to improve the performance of Dex query iterators (mainly, AND iterator). Benchmarks show that the queries become ~10% faster. Before ``` ------------------------------------------------------- Benchmark Time CPU Iteration ------------------------------------------------------- DexAdHocQueries 5883074 ns 5883018 ns 117 DexRealQ 959904457 ns 959898507 ns 1 ``` After ``` ------------------------------------------------------- Benchmark Time CPU Iteration ------------------------------------------------------- DexAdHocQueries 5238403 ns 5238361 ns 130 DexRealQ 873275207 ns 873269453 ns 1 ``` Reviewed by: sammccall Differential Revision: https://reviews.llvm.org/D51310 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341057 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.cpp | 38 +++++++++++++++++++++++++++--- clangd/index/dex/Iterator.h | 2 ++ unittests/clangd/DexIndexTests.cpp | 4 ++-- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index 5190217e7..f9c7c7dbf 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -48,6 +48,8 @@ class DocumentIterator : public Iterator { float consume() override { return DEFAULT_BOOST_SCORE; } + size_t estimateSize() const override { return Documents.size(); } + private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << '['; @@ -85,6 +87,18 @@ class AndIterator : public Iterator { assert(!Children.empty() && "AndIterator should have at least one child."); // Establish invariants. sync(); + // When children are sorted by the estimateSize(), sync() calls are more + // effective. Each sync() starts with the first child and makes sure all + // children point to the same element. If any child is "above" the previous + // ones, the algorithm resets and and advances the children to the next + // highest element starting from the front. When child iterators in the + // beginning have smaller estimated size, the sync() will have less restarts + // and become more effective. + std::sort(begin(Children), end(Children), + [](const std::unique_ptr &LHS, + const std::unique_ptr &RHS) { + return LHS->estimateSize() < RHS->estimateSize(); + }); } bool reachedEnd() const override { return ReachedEnd; } @@ -114,6 +128,10 @@ class AndIterator : public Iterator { }); } + size_t estimateSize() const override { + return Children.front()->estimateSize(); + } + private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << "(& "; @@ -146,9 +164,6 @@ class AndIterator : public Iterator { return; // If any child goes beyond given ID (i.e. ID is not the common item), // all children should be advanced to the next common item. - // FIXME(kbobyrev): This is not a very optimized version; after costs - // are introduced, cycle should break whenever ID exceeds current one - // and cheapest children should be advanced over again. if (Child->peek() > SyncID) { SyncID = Child->peek(); NeedsAdvance = true; @@ -178,6 +193,7 @@ class OrIterator : public Iterator { OrIterator(std::vector> AllChildren) : Children(std::move(AllChildren)) { assert(Children.size() > 0 && "Or Iterator must have at least one child."); + std::sort(begin(Children), end(Children)); } /// Returns true if all children are exhausted. @@ -235,6 +251,14 @@ class OrIterator : public Iterator { }); } + size_t estimateSize() const override { + return std::accumulate( + begin(Children), end(Children), Children.front()->estimateSize(), + [&](size_t Current, const std::unique_ptr &Child) { + return std::max(Current, Child->estimateSize()); + }); + } + private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << "(| "; @@ -277,6 +301,8 @@ class TrueIterator : public Iterator { float consume() override { return DEFAULT_BOOST_SCORE; } + size_t estimateSize() const override { return Size; } + private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << "(TRUE {" << Index << "} out of " << Size << ")"; @@ -305,6 +331,8 @@ class BoostIterator : public Iterator { float consume() override { return Child->consume() * Factor; } + size_t estimateSize() const override { return Child->estimateSize(); } + private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << "(BOOST " << Factor << ' ' << *Child << ')'; @@ -342,6 +370,10 @@ class LimitIterator : public Iterator { return Child->consume(); } + size_t estimateSize() const override { + return std::min(Child->estimateSize(), Limit); + } + private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << "(LIMIT " << Limit << '(' << ItemsLeft << ") " << *Child << ')'; diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 84a1a16e2..06adaefaf 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -95,6 +95,8 @@ class Iterator { /// consume() must *not* be called on children that don't contain the current /// doc. virtual float consume() = 0; + /// Returns an estimate of advance() calls before the iterator is exhausted. + virtual size_t estimateSize() const = 0; virtual ~Iterator() {} diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index 68ef1a130..78c0dfc4d 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -259,8 +259,8 @@ TEST(DexIndexIterators, StringRepresentation) { createOr(create(L3), create(L4), create(L5))); EXPECT_EQ(llvm::to_string(*Nested), - "(& (& [{1}, 3, 5, 8, 9, END] [{1}, 5, 7, 9, END]) (| [0, {5}, " - "END] [0, {1}, 5, END] [{END}]))"); + "(& (| [{END}] [0, {5}, END] [0, {1}, 5, END]) (& [{1}, 5, 7, 9, " + "END] [{1}, 3, 5, 8, 9, END]))"); } TEST(DexIndexIterators, Limit) { From dd0396c0911c7029283edcd040159375f2b89206 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 30 Aug 2018 12:29:36 +0000 Subject: [PATCH 104/686] [clangd] Fix tests after rL341057 Since OR iterator children are not longer sorted by the estimated size, string representation should be different. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341060 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/DexIndexTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index 78c0dfc4d..a66df13b0 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -259,7 +259,7 @@ TEST(DexIndexIterators, StringRepresentation) { createOr(create(L3), create(L4), create(L5))); EXPECT_EQ(llvm::to_string(*Nested), - "(& (| [{END}] [0, {5}, END] [0, {1}, 5, END]) (& [{1}, 5, 7, 9, " + "(& (| [0, {5}, END] [0, {1}, 5, END] [{END}]) (& [{1}, 5, 7, 9, " "END] [{1}, 3, 5, 8, 9, END]))"); } From 182a647b02b81cf0a31eafba7b73fa756928795f Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 30 Aug 2018 12:42:19 +0000 Subject: [PATCH 105/686] [clang-tidy] Use simple string matching instead of Regex Instead of parsing and compiling the `llvm::Regex` each time, it's faster to use basic string matching for filename prefix check. Reviewed by: hokein Differential Revision: https://reviews.llvm.org/D51360 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341061 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/AbseilMatcher.h | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/clang-tidy/abseil/AbseilMatcher.h b/clang-tidy/abseil/AbseilMatcher.h index ef08bb86b..17b3517e1 100644 --- a/clang-tidy/abseil/AbseilMatcher.h +++ b/clang-tidy/abseil/AbseilMatcher.h @@ -6,9 +6,10 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// + #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include namespace clang { namespace ast_matchers { @@ -28,10 +29,9 @@ namespace ast_matchers { /// /// Usable as: Matcher, Matcher, Matcher, /// Matcher - -AST_POLYMORPHIC_MATCHER(isInAbseilFile, - AST_POLYMORPHIC_SUPPORTED_TYPES( - Decl, Stmt, TypeLoc, NestedNameSpecifierLoc)) { +AST_POLYMORPHIC_MATCHER( + isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, + NestedNameSpecifierLoc)) { auto &SourceManager = Finder->getASTContext().getSourceManager(); SourceLocation Loc = Node.getBeginLoc(); if (Loc.isInvalid()) @@ -40,11 +40,21 @@ AST_POLYMORPHIC_MATCHER(isInAbseilFile, SourceManager.getFileEntryForID(SourceManager.getFileID(Loc)); if (!FileEntry) return false; - StringRef Filename = FileEntry->getName(); - llvm::Regex RE( - "absl/(algorithm|base|container|debugging|memory|meta|numeric|strings|" - "synchronization|time|types|utility)"); - return RE.match(Filename); + // Determine whether filepath contains "absl/[absl-library]" substring, where + // [absl-library] is AbseilLibraries list entry. + StringRef Path = FileEntry->getName(); + const static llvm::SmallString<5> AbslPrefix("absl/"); + size_t PrefixPosition = Path.find(AbslPrefix); + if (PrefixPosition == StringRef::npos) + return false; + Path = Path.drop_front(PrefixPosition + AbslPrefix.size()); + static const char *AbseilLibraries[] = { + "algorithm", "base", "container", "debugging", + "memory", "meta", "numeric", "strings", + "synchronization", "time", "types", "utility"}; + return std::any_of( + std::begin(AbseilLibraries), std::end(AbseilLibraries), + [&](const char *Library) { return Path.startswith(Library); }); } } // namespace ast_matchers From 30ec2b89b405d2ebb93b012aa8588280ff6bf17b Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Thu, 30 Aug 2018 13:14:31 +0000 Subject: [PATCH 106/686] [clangd] Report position of opening paren in singature help Summary: Only accessible via the C++ API at the moment. Reviewers: sammccall Reviewed By: sammccall Subscribers: ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51437 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341065 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 12 +++++- clangd/Protocol.h | 7 +++ unittests/clangd/CodeCompleteTests.cpp | 60 ++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 65c415fba..e355a8332 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -794,7 +794,17 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, OverloadCandidate *Candidates, - unsigned NumCandidates) override { + unsigned NumCandidates, + SourceLocation OpenParLoc) override { + assert(!OpenParLoc.isInvalid()); + SourceManager &SrcMgr = S.getSourceManager(); + OpenParLoc = SrcMgr.getFileLoc(OpenParLoc); + if (SrcMgr.isInMainFile(OpenParLoc)) + SigHelp.argListStart = sourceLocToPosition(SrcMgr, OpenParLoc); + else + elog("Location oustide main file in signature help: {0}", + OpenParLoc.printToString(SrcMgr)); + std::vector ScoredSignatures; SigHelp.signatures.reserve(NumCandidates); ScoredSignatures.reserve(NumCandidates); diff --git a/clangd/Protocol.h b/clangd/Protocol.h index d019e82db..2e291bd11 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -828,6 +828,13 @@ struct SignatureHelp { /// The active parameter of the active signature. int activeParameter = 0; + + /// Position of the start of the argument list, including opening paren. e.g. + /// foo("first arg", "second arg", + /// ^-argListStart ^-cursor + /// This is a clangd-specific extension, it is only available via C++ API and + /// not currently serialized for the LSP. + Position argListStart; }; llvm::json::Value toJSON(const SignatureHelp &); diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index b92cf64a6..cb0e9a3e5 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -825,8 +825,7 @@ TEST(CompletionTest, IgnoreCompleteInExcludedPPBranchWithRecoveryContext) { EXPECT_TRUE(Results.Completions.empty()); } - -SignatureHelp signatures(StringRef Text, +SignatureHelp signatures(StringRef Text, Position Point, std::vector IndexSymbols = {}) { std::unique_ptr Index; if (!IndexSymbols.empty()) @@ -840,9 +839,14 @@ SignatureHelp signatures(StringRef Text, ClangdServer Server(CDB, FS, DiagConsumer, Opts); auto File = testPath("foo.cpp"); + runAddDocument(Server, File, Text); + return cantFail(runSignatureHelp(Server, File, Point)); +} + +SignatureHelp signatures(StringRef Text, + std::vector IndexSymbols = {}) { Annotations Test(Text); - runAddDocument(Server, File, Test.code()); - return cantFail(runSignatureHelp(Server, File, Test.point())); + return signatures(Test.code(), Test.point(), std::move(IndexSymbols)); } MATCHER_P(ParamsAre, P, "") { @@ -907,6 +911,54 @@ TEST(SignatureHelpTest, ActiveArg) { EXPECT_EQ(1, Results.activeParameter); } +TEST(SignatureHelpTest, OpeningParen) { + llvm::StringLiteral Tests[] = {// Recursive function call. + R"cpp( + int foo(int a, int b, int c); + int main() { + foo(foo $p^( foo(10, 10, 10), ^ ))); + })cpp", + // Functional type cast. + R"cpp( + struct Foo { + Foo(int a, int b, int c); + }; + int main() { + Foo $p^( 10, ^ ); + })cpp", + // New expression. + R"cpp( + struct Foo { + Foo(int a, int b, int c); + }; + int main() { + new Foo $p^( 10, ^ ); + })cpp", + // Macro expansion. + R"cpp( + int foo(int a, int b, int c); + #define FOO foo( + + int main() { + // Macro expansions. + $p^FOO 10, ^ ); + })cpp", + // Macro arguments. + R"cpp( + int foo(int a, int b, int c); + int main() { + #define ID(X) X + ID(foo $p^( foo(10), ^ )) + })cpp"}; + + for (auto Test : Tests) { + Annotations Code(Test); + EXPECT_EQ(signatures(Code.code(), Code.point()).argListStart, + Code.point("p")) + << "Test source:" << Test; + } +} + class IndexRequestCollector : public SymbolIndex { public: bool From 2f01a7deae6e2d2f3bf20e36473b9046bcdba845 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 30 Aug 2018 13:30:34 +0000 Subject: [PATCH 107/686] [clangd] Remove UB introduced in rL341057 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341066 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index f9c7c7dbf..bf4274a4f 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -193,7 +193,6 @@ class OrIterator : public Iterator { OrIterator(std::vector> AllChildren) : Children(std::move(AllChildren)) { assert(Children.size() > 0 && "Or Iterator must have at least one child."); - std::sort(begin(Children), end(Children)); } /// Returns true if all children are exhausted. From 3439719ec494893ca8a2ddbef678f7142938e791 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 30 Aug 2018 15:07:34 +0000 Subject: [PATCH 108/686] [clangd] Run SignatureHelp using an up-to-date preamble, waiting if needed. Summary: After code completion inserts a header, running signature help using the old preamble will usually fail. So we add support for consistent preamble reads. Reviewers: ilya-biryukov Subscribers: javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51438 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341076 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 9 ++- clangd/TUScheduler.cpp | 66 ++++++++++++++++++--- clangd/TUScheduler.h | 31 ++++++---- unittests/clangd/TUSchedulerTests.cpp | 85 +++++++++++++++++++++++---- 4 files changed, 158 insertions(+), 33 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 708a27682..6bdd082cd 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -230,7 +230,8 @@ TaskHandle ClangdServer::codeComplete(PathRef File, Position Pos, // is called as soon as results are available. }; - WorkScheduler.runWithPreamble("CodeComplete", File, + // We use a potentially-stale preamble because latency is critical here. + WorkScheduler.runWithPreamble("CodeComplete", File, TUScheduler::Stale, Bind(Task, File.str(), std::move(CB))); return TH; } @@ -252,7 +253,11 @@ void ClangdServer::signatureHelp(PathRef File, Position Pos, IP->Contents, Pos, FS, PCHs, Index)); }; - WorkScheduler.runWithPreamble("SignatureHelp", File, + // Unlike code completion, we wait for an up-to-date preamble here. + // Signature help is often triggered after code completion. If the code + // completion inserted a header to make the symbol available, then using + // the old preamble would yield useless results. + WorkScheduler.runWithPreamble("SignatureHelp", File, TUScheduler::Consistent, Bind(Action, File.str(), std::move(CB))); } diff --git a/clangd/TUScheduler.cpp b/clangd/TUScheduler.cpp index 8db8b2c32..48dc445d1 100644 --- a/clangd/TUScheduler.cpp +++ b/clangd/TUScheduler.cpp @@ -183,6 +183,10 @@ class ASTWorker { bool blockUntilIdle(Deadline Timeout) const; std::shared_ptr getPossiblyStalePreamble() const; + /// Obtain a preamble reflecting all updates so far. Threadsafe. + /// It may be delivered immediately, or later on the worker thread. + void getCurrentPreamble( + llvm::unique_function)>); /// Wait for the first build of preamble to finish. Preamble itself can be /// accessed via getPossibleStalePreamble(). Note that this function will /// return after an unsuccessful build of the preamble too, i.e. result of @@ -464,6 +468,34 @@ ASTWorker::getPossiblyStalePreamble() const { return LastBuiltPreamble; } +void ASTWorker::getCurrentPreamble( + llvm::unique_function)> Callback) { + // We could just call startTask() to throw the read on the queue, knowing + // it will run after any updates. But we know this task is cheap, so to + // improve latency we cheat: insert it on the queue after the last update. + std::unique_lock Lock(Mutex); + auto LastUpdate = + std::find_if(Requests.rbegin(), Requests.rend(), + [](const Request &R) { return R.UpdateType.hasValue(); }); + // If there were no writes in the queue, the preamble is ready now. + if (LastUpdate == Requests.rend()) { + Lock.unlock(); + return Callback(getPossiblyStalePreamble()); + } + assert(!RunSync && "Running synchronously, but queue is non-empty!"); + Requests.insert(LastUpdate.base(), + Request{Bind( + [this](decltype(Callback) Callback) { + Callback(getPossiblyStalePreamble()); + }, + std::move(Callback)), + "GetPreamble", steady_clock::now(), + Context::current().clone(), + /*UpdateType=*/llvm::None}); + Lock.unlock(); + RequestsCV.notify_all(); +} + void ASTWorker::waitForFirstPreamble() const { PreambleWasBuilt.wait(); } @@ -711,7 +743,7 @@ void TUScheduler::runWithAST( } void TUScheduler::runWithPreamble( - llvm::StringRef Name, PathRef File, + llvm::StringRef Name, PathRef File, PreambleConsistency Consistency, llvm::unique_function)> Action) { auto It = Files.find(File); if (It == Files.end()) { @@ -731,22 +763,40 @@ void TUScheduler::runWithPreamble( return; } + // Future is populated if the task needs a specific preamble. + std::future> ConsistentPreamble; + if (Consistency == Consistent) { + std::promise> Promise; + ConsistentPreamble = Promise.get_future(); + It->second->Worker->getCurrentPreamble(Bind( + [](decltype(Promise) Promise, + std::shared_ptr Preamble) { + Promise.set_value(std::move(Preamble)); + }, + std::move(Promise))); + } + std::shared_ptr Worker = It->second->Worker.lock(); auto Task = [Worker, this](std::string Name, std::string File, std::string Contents, tooling::CompileCommand Command, Context Ctx, + decltype(ConsistentPreamble) ConsistentPreamble, decltype(Action) Action) mutable { - // We don't want to be running preamble actions before the preamble was - // built for the first time. This avoids extra work of processing the - // preamble headers in parallel multiple times. - Worker->waitForFirstPreamble(); + std::shared_ptr Preamble; + if (ConsistentPreamble.valid()) { + Preamble = ConsistentPreamble.get(); + } else { + // We don't want to be running preamble actions before the preamble was + // built for the first time. This avoids extra work of processing the + // preamble headers in parallel multiple times. + Worker->waitForFirstPreamble(); + Preamble = Worker->getPossiblyStalePreamble(); + } std::lock_guard BarrierLock(Barrier); WithContext Guard(std::move(Ctx)); trace::Span Tracer(Name); SPAN_ATTACH(Tracer, "file", File); - std::shared_ptr Preamble = - Worker->getPossiblyStalePreamble(); Action(InputsAndPreamble{Contents, Command, Preamble.get()}); }; @@ -755,7 +805,7 @@ void TUScheduler::runWithPreamble( Bind(Task, std::string(Name), std::string(File), It->second->Contents, It->second->Command, Context::current().derive(kFileBeingProcessed, File), - std::move(Action))); + std::move(ConsistentPreamble), std::move(Action))); } std::vector> diff --git a/clangd/TUScheduler.h b/clangd/TUScheduler.h index edb19ee19..3ef17c6b6 100644 --- a/clangd/TUScheduler.h +++ b/clangd/TUScheduler.h @@ -119,19 +119,28 @@ class TUScheduler { void runWithAST(llvm::StringRef Name, PathRef File, Callback Action); - /// Schedule an async read of the Preamble. - /// The preamble may be stale, generated from an older version of the file. - /// Reading from locations in the preamble may cause the files to be re-read. - /// This gives callers two options: - /// - validate that the preamble is still valid, and only use it in this case - /// - accept that preamble contents may be outdated, and try to avoid reading - /// source code from headers. + /// Controls whether preamble reads wait for the preamble to be up-to-date. + enum PreambleConsistency { + /// The preamble is generated from the current version of the file. + /// If the content was recently updated, we will wait until we have a + /// preamble that reflects that update. + /// This is the slowest option, and may be delayed by other tasks. + Consistent, + /// The preamble may be generated from an older version of the file. + /// Reading from locations in the preamble may cause files to be re-read. + /// This gives callers two options: + /// - validate that the preamble is still valid, and only use it if so + /// - accept that the preamble contents may be outdated, and try to avoid + /// reading source code from headers. + /// This is the fastest option, usually a preamble is available immediately. + Stale, + }; + /// Schedule an async read of the preamble. /// If there's no preamble yet (because the file was just opened), we'll wait - /// for it to build. The preamble may still be null if it fails to build or is - /// empty. - /// If an error occurs during processing, it is forwarded to the \p Action - /// callback. + /// for it to build. The result may be null if it fails to build or is empty. + /// If an error occurs, it is forwarded to the \p Action callback. void runWithPreamble(llvm::StringRef Name, PathRef File, + PreambleConsistency Consistency, Callback Action); /// Wait until there are no scheduled or running tasks. diff --git a/unittests/clangd/TUSchedulerTests.cpp b/unittests/clangd/TUSchedulerTests.cpp index d51500d0e..5222d795c 100644 --- a/unittests/clangd/TUSchedulerTests.cpp +++ b/unittests/clangd/TUSchedulerTests.cpp @@ -22,6 +22,7 @@ namespace { using ::testing::_; using ::testing::AnyOf; using ::testing::Each; +using ::testing::ElementsAre; using ::testing::Pair; using ::testing::Pointee; using ::testing::UnorderedElementsAre; @@ -63,7 +64,7 @@ TEST_F(TUSchedulerTests, MissingFiles) { ASSERT_FALSE(bool(AST)); ignoreError(AST.takeError()); }); - S.runWithPreamble("", Missing, + S.runWithPreamble("", Missing, TUScheduler::Stale, [&](llvm::Expected Preamble) { ASSERT_FALSE(bool(Preamble)); ignoreError(Preamble.takeError()); @@ -75,9 +76,10 @@ TEST_F(TUSchedulerTests, MissingFiles) { S.runWithAST("", Added, [&](llvm::Expected AST) { EXPECT_TRUE(bool(AST)); }); - S.runWithPreamble("", Added, [&](llvm::Expected Preamble) { - EXPECT_TRUE(bool(Preamble)); - }); + S.runWithPreamble("", Added, TUScheduler::Stale, + [&](llvm::Expected Preamble) { + EXPECT_TRUE(bool(Preamble)); + }); S.remove(Added); // Assert that all operations fail after removing the file. @@ -85,10 +87,11 @@ TEST_F(TUSchedulerTests, MissingFiles) { ASSERT_FALSE(bool(AST)); ignoreError(AST.takeError()); }); - S.runWithPreamble("", Added, [&](llvm::Expected Preamble) { - ASSERT_FALSE(bool(Preamble)); - ignoreError(Preamble.takeError()); - }); + S.runWithPreamble("", Added, TUScheduler::Stale, + [&](llvm::Expected Preamble) { + ASSERT_FALSE(bool(Preamble)); + ignoreError(Preamble.takeError()); + }); // remove() shouldn't crash on missing files. S.remove(Added); } @@ -148,6 +151,64 @@ TEST_F(TUSchedulerTests, Debounce) { EXPECT_EQ(2, CallbackCount); } +static std::vector includes(const PreambleData *Preamble) { + std::vector Result; + if (Preamble) + for (const auto &Inclusion : Preamble->Includes.MainFileIncludes) + Result.push_back(Inclusion.Written); + return Result; +} + +TEST_F(TUSchedulerTests, PreambleConsistency) { + std::atomic CallbackCount(0); + { + Notification InconsistentReadDone; // Must live longest. + TUScheduler S( + getDefaultAsyncThreadsCount(), /*StorePreamblesInMemory=*/true, + noopParsingCallbacks(), + /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), + ASTRetentionPolicy()); + auto Path = testPath("foo.cpp"); + // Schedule two updates (A, B) and two preamble reads (stale, consistent). + // The stale read should see A, and the consistent read should see B. + // (We recognize the preambles by their included files). + S.update(Path, getInputs(Path, "#include "), WantDiagnostics::Yes, + [&](std::vector Diags) { + // This callback runs in between the two preamble updates. + + // This blocks update B, preventing it from winning the race + // against the stale read. + // If the first read was instead consistent, this would deadlock. + InconsistentReadDone.wait(); + // This delays update B, preventing it from winning a race + // against the consistent read. The consistent read sees B + // only because it waits for it. + // If the second read was stale, it would usually see A. + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + }); + S.update(Path, getInputs(Path, "#include "), WantDiagnostics::Yes, + [&](std::vector Diags) {}); + + S.runWithPreamble("StaleRead", Path, TUScheduler::Stale, + [&](llvm::Expected Pre) { + ASSERT_TRUE(bool(Pre)); + assert(bool(Pre)); + EXPECT_THAT(includes(Pre->Preamble), + ElementsAre("")); + InconsistentReadDone.notify(); + ++CallbackCount; + }); + S.runWithPreamble("ConsistentRead", Path, TUScheduler::Consistent, + [&](llvm::Expected Pre) { + ASSERT_TRUE(bool(Pre)); + EXPECT_THAT(includes(Pre->Preamble), + ElementsAre("")); + ++CallbackCount; + }); + } + EXPECT_EQ(2, CallbackCount); +} + TEST_F(TUSchedulerTests, ManyUpdates) { const int FilesCount = 3; const int UpdatesPerFile = 10; @@ -229,7 +290,7 @@ TEST_F(TUSchedulerTests, ManyUpdates) { { WithContextValue WithNonce(NonceKey, ++Nonce); S.runWithPreamble( - "CheckPreamble", File, + "CheckPreamble", File, TUScheduler::Stale, [File, Inputs, Nonce, &Mut, &TotalPreambleReads]( llvm::Expected Preamble) { EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce)); @@ -324,7 +385,7 @@ TEST_F(TUSchedulerTests, EmptyPreamble) { auto WithEmptyPreamble = R"cpp(int main() {})cpp"; S.update(Foo, getInputs(Foo, WithPreamble), WantDiagnostics::Auto, [](std::vector) {}); - S.runWithPreamble("getNonEmptyPreamble", Foo, + S.runWithPreamble("getNonEmptyPreamble", Foo, TUScheduler::Stale, [&](llvm::Expected Preamble) { // We expect to get a non-empty preamble. EXPECT_GT(cantFail(std::move(Preamble)) @@ -340,7 +401,7 @@ TEST_F(TUSchedulerTests, EmptyPreamble) { [](std::vector) {}); // Wait for the preamble is being built. ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); - S.runWithPreamble("getEmptyPreamble", Foo, + S.runWithPreamble("getEmptyPreamble", Foo, TUScheduler::Stale, [&](llvm::Expected Preamble) { // We expect to get an empty preamble. EXPECT_EQ(cantFail(std::move(Preamble)) @@ -372,7 +433,7 @@ TEST_F(TUSchedulerTests, RunWaitsForPreamble) { [](std::vector) {}); for (int I = 0; I < ReadsToSchedule; ++I) { S.runWithPreamble( - "test", Foo, + "test", Foo, TUScheduler::Stale, [I, &PreamblesMut, &Preambles](llvm::Expected IP) { std::lock_guard Lock(PreamblesMut); Preambles[I] = cantFail(std::move(IP)).Preamble; From 91fceebce9aeb8133b074b6795b371f0ca9abf50 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 30 Aug 2018 22:10:13 +0000 Subject: [PATCH 109/686] Remove LIT_SITE_CFG_IN_FOOTER, clang-tools-extra It's always replaced with the same (short) static string, so just put that there directly. No intended behavior change. https://reviews.llvm.org/D51357 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341130 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/lit.site.cfg.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index 948e469cf..bea2dbfb1 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -23,7 +23,8 @@ except KeyError: key, = e.args lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) -@LIT_SITE_CFG_IN_FOOTER@ +import lit +lit.llvm.initialize(lit_config, config) # Let the main config do the real work. lit_config.load_config(config, "@CLANG_TOOLS_SOURCE_DIR@/test/lit.cfg") From 47da6ac290982fcb0f75c43a1248c9cf4a5b9701 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Thu, 30 Aug 2018 23:25:38 +0000 Subject: [PATCH 110/686] Extract runCommandsInFile method Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D51260 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341144 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-query/tool/ClangQuery.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/clang-query/tool/ClangQuery.cpp b/clang-query/tool/ClangQuery.cpp index 669dc40e3..d0a4a80a3 100644 --- a/clang-query/tool/ClangQuery.cpp +++ b/clang-query/tool/ClangQuery.cpp @@ -58,6 +58,24 @@ static cl::list CommandFiles("f", cl::value_desc("file"), cl::cat(ClangQueryCategory)); +bool runCommandsInFile(const char *ExeName, std::string const &FileName, + QuerySession &QS) { + std::ifstream Input(FileName.c_str()); + if (!Input.is_open()) { + llvm::errs() << ExeName << ": cannot open " << FileName << "\n"; + return 1; + } + while (Input.good()) { + std::string Line; + std::getline(Input, Line); + + QueryRef Q = QueryParser::parse(Line, QS); + if (!Q->run(llvm::outs(), QS)) + return true; + } + return false; +} + int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); @@ -84,19 +102,8 @@ int main(int argc, const char **argv) { } } else if (!CommandFiles.empty()) { for (auto I = CommandFiles.begin(), E = CommandFiles.end(); I != E; ++I) { - std::ifstream Input(I->c_str()); - if (!Input.is_open()) { - llvm::errs() << argv[0] << ": cannot open " << *I << "\n"; + if (runCommandsInFile(argv[0], *I, QS)) return 1; - } - while (Input.good()) { - std::string Line; - std::getline(Input, Line); - - QueryRef Q = QueryParser::parse(Line, QS); - if (!Q->run(llvm::outs(), QS)) - return 1; - } } } else { LineEditor LE("clang-query"); From 4e32cf3d39dd3288e4cf9539abdedc0be2b7c62b Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Thu, 30 Aug 2018 23:25:44 +0000 Subject: [PATCH 111/686] Add preload option to clang-query Summary: This allows loading a file with pre-defined let commands for example. Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D51261 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341145 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-query/tool/ClangQuery.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/clang-query/tool/ClangQuery.cpp b/clang-query/tool/ClangQuery.cpp index d0a4a80a3..26a2ccf6f 100644 --- a/clang-query/tool/ClangQuery.cpp +++ b/clang-query/tool/ClangQuery.cpp @@ -58,6 +58,11 @@ static cl::list CommandFiles("f", cl::value_desc("file"), cl::cat(ClangQueryCategory)); +static cl::opt PreloadFile( + "preload", + cl::desc("Preload commands from file and start interactive mode"), + cl::value_desc("file"), cl::cat(ClangQueryCategory)); + bool runCommandsInFile(const char *ExeName, std::string const &FileName, QuerySession &QS) { std::ifstream Input(FileName.c_str()); @@ -86,6 +91,12 @@ int main(int argc, const char **argv) { return 1; } + if ((!Commands.empty() || !CommandFiles.empty()) && !PreloadFile.empty()) { + llvm::errs() << argv[0] + << ": cannot specify both -c or -f with --preload\n"; + return 1; + } + ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList()); std::vector> ASTs; @@ -106,6 +117,10 @@ int main(int argc, const char **argv) { return 1; } } else { + if (!PreloadFile.empty()) { + if (runCommandsInFile(argv[0], PreloadFile, QS)) + return 1; + } LineEditor LE("clang-query"); LE.setListCompleter([&QS](StringRef Line, size_t Pos) { return QueryParser::complete(Line, Pos, QS); From 6aaf9d468ec526b588ce98a8cf940aeaca10cc04 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Fri, 31 Aug 2018 00:26:46 +0000 Subject: [PATCH 112/686] Import lit.llvm after rL341130 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341152 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/lit.site.cfg.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index bea2dbfb1..bf33b1513 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -23,7 +23,7 @@ except KeyError: key, = e.args lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) -import lit +import lit.llvm lit.llvm.initialize(lit_config, config) # Let the main config do the real work. From 277233682b3029bf62188b8ed37dbda74d111747 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 31 Aug 2018 03:51:33 +0000 Subject: [PATCH 113/686] [clang-move] Explicitly ignore implicit UsingDirectiveDecls instead of depending on them missing source locations This is adjustment to allow the logic to work even if implicit UsingDirectiveDecls get actual source locations. There should be no functionality change. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341161 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-move/ClangMove.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang-move/ClangMove.cpp b/clang-move/ClangMove.cpp index eac6fcd0e..f20cf1ec4 100644 --- a/clang-move/ClangMove.cpp +++ b/clang-move/ClangMove.cpp @@ -558,7 +558,8 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) { // namespace, these decls are always copied to new.h/cc. Those in classes, // functions are covered in other matchers. Finder->addMatcher(namedDecl(anyOf(usingDecl(IsOldCCTopLevelDecl), - usingDirectiveDecl(IsOldCCTopLevelDecl), + usingDirectiveDecl(unless(isImplicit()), + IsOldCCTopLevelDecl), typeAliasDecl(IsOldCCTopLevelDecl)), notInMacro()) .bind("using_decl"), From c9b83fe8e2543ccaa9622d03ee302a901c7ee6bc Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Fri, 31 Aug 2018 08:19:50 +0000 Subject: [PATCH 114/686] [NFC] Use LLVM naming conventions within FileDistance Reviewed by: sammccall Differential Revision: https://reviews.llvm.org/D51527 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341182 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/FileDistance.cpp | 8 ++++---- clangd/FileDistance.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clangd/FileDistance.cpp b/clangd/FileDistance.cpp index a0ce25b48..c25ad9ac5 100644 --- a/clangd/FileDistance.cpp +++ b/clangd/FileDistance.cpp @@ -102,7 +102,7 @@ FileDistance::FileDistance(StringMap Sources, auto ParentCost = Cache.lookup(Next.front()); for (auto Child : DownEdges.lookup(Next.front())) { auto &ChildCost = - Cache.try_emplace(Child, kUnreachable).first->getSecond(); + Cache.try_emplace(Child, Unreachable).first->getSecond(); if (ParentCost + Opts.DownCost < ChildCost) ChildCost = ParentCost + Opts.DownCost; Next.push(Child); @@ -113,7 +113,7 @@ FileDistance::FileDistance(StringMap Sources, unsigned FileDistance::distance(StringRef Path) { auto Canonical = canonicalize(Path); - unsigned Cost = kUnreachable; + unsigned Cost = Unreachable; SmallVector Ancestors; // Walk up ancestors until we find a path we know the distance for. for (StringRef Rest = Canonical; !Rest.empty(); @@ -129,7 +129,7 @@ unsigned FileDistance::distance(StringRef Path) { // Now we know the costs for (known node, queried node]. // Fill these in, walking down the directory tree. for (hash_code Hash : reverse(Ancestors)) { - if (Cost != kUnreachable) + if (Cost != Unreachable) Cost += Opts.DownCost; Cache.try_emplace(Hash, Cost); } @@ -138,7 +138,7 @@ unsigned FileDistance::distance(StringRef Path) { } unsigned URIDistance::distance(llvm::StringRef URI) { - auto R = Cache.try_emplace(llvm::hash_value(URI), FileDistance::kUnreachable); + auto R = Cache.try_emplace(llvm::hash_value(URI), FileDistance::Unreachable); if (!R.second) return R.first->getSecond(); if (auto U = clangd::URI::parse(URI)) { diff --git a/clangd/FileDistance.h b/clangd/FileDistance.h index f07804fcb..631fddf5e 100644 --- a/clangd/FileDistance.h +++ b/clangd/FileDistance.h @@ -66,7 +66,7 @@ struct SourceParams { // This object should be reused, it memoizes intermediate computations. class FileDistance { public: - static constexpr unsigned kUnreachable = std::numeric_limits::max(); + static constexpr unsigned Unreachable = std::numeric_limits::max(); FileDistance(llvm::StringMap Sources, const FileDistanceOptions &Opts = {}); From b70503ec8c1c7590c2707c91f837c8b15eb0775b Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Fri, 31 Aug 2018 08:29:48 +0000 Subject: [PATCH 115/686] NFC: Fix build failure after rL341182 Didn't rename another variable reference. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341184 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/FileDistance.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/FileDistance.cpp b/clangd/FileDistance.cpp index c25ad9ac5..e24d030dd 100644 --- a/clangd/FileDistance.cpp +++ b/clangd/FileDistance.cpp @@ -53,7 +53,7 @@ static SmallString<128> canonicalize(StringRef Path) { return Result; } -constexpr const unsigned FileDistance::kUnreachable; +constexpr const unsigned FileDistance::Unreachable; FileDistance::FileDistance(StringMap Sources, const FileDistanceOptions &Opts) From a79f6edb59f888b2d352ee513d56c3f6513faff6 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Fri, 31 Aug 2018 09:17:02 +0000 Subject: [PATCH 116/686] [NFC] Cleanup Dex * Use consistent assertion messages in iterators implementations * Silence a bunch of clang-tidy warnings: use `emplace_back` instead of `push_back` where possible, make sure arguments have the same name in header and implementation file, use for loop over ranges where possible Reviewed by: ioeric Differential Revision: https://reviews.llvm.org/D51528 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341190 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/DexIndex.cpp | 2 +- clangd/index/dex/DexIndex.h | 2 +- clangd/index/dex/Iterator.cpp | 51 ++++++++++++++++++----------------- clangd/index/dex/Trigram.cpp | 8 +++--- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/DexIndex.cpp index dd993a95c..292ed82f2 100644 --- a/clangd/index/dex/DexIndex.cpp +++ b/clangd/index/dex/DexIndex.cpp @@ -30,7 +30,7 @@ namespace { // * Types std::vector generateSearchTokens(const Symbol &Sym) { std::vector Result = generateIdentifierTrigrams(Sym.Name); - Result.push_back(Token(Token::Kind::Scope, Sym.Scope)); + Result.emplace_back(Token::Kind::Scope, Sym.Scope); return Result; } diff --git a/clangd/index/dex/DexIndex.h b/clangd/index/dex/DexIndex.h index d9b2cd143..8631a234d 100644 --- a/clangd/index/dex/DexIndex.h +++ b/clangd/index/dex/DexIndex.h @@ -41,7 +41,7 @@ class DexIndex : public SymbolIndex { public: /// \brief (Re-)Build index for `Symbols`. All symbol pointers must remain /// accessible as long as `Symbols` is kept alive. - void build(std::shared_ptr> Symbols); + void build(std::shared_ptr> Syms); /// \brief Build index from a symbol slab. static std::unique_ptr build(SymbolSlab Slab); diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index bf4274a4f..9e4606a44 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -30,23 +30,26 @@ class DocumentIterator : public Iterator { /// Advances cursor to the next item. void advance() override { - assert(!reachedEnd() && "DocumentIterator can't advance at the end."); + assert(!reachedEnd() && "DOCUMENT iterator can't advance() at the end."); ++Index; } /// Applies binary search to advance cursor to the next item with DocID equal /// or higher than the given one. void advanceTo(DocID ID) override { - assert(!reachedEnd() && "DocumentIterator can't advance at the end."); + assert(!reachedEnd() && "DOCUMENT iterator can't advance() at the end."); Index = std::lower_bound(Index, std::end(Documents), ID); } DocID peek() const override { - assert(!reachedEnd() && "DocumentIterator can't call peek() at the end."); + assert(!reachedEnd() && "DOCUMENT iterator can't peek() at the end."); return *Index; } - float consume() override { return DEFAULT_BOOST_SCORE; } + float consume() override { + assert(!reachedEnd() && "DOCUMENT iterator can't consume() at the end."); + return DEFAULT_BOOST_SCORE; + } size_t estimateSize() const override { return Documents.size(); } @@ -84,7 +87,7 @@ class AndIterator : public Iterator { public: AndIterator(std::vector> AllChildren) : Children(std::move(AllChildren)) { - assert(!Children.empty() && "AndIterator should have at least one child."); + assert(!Children.empty() && "AND iterator should have at least one child."); // Establish invariants. sync(); // When children are sorted by the estimateSize(), sync() calls are more @@ -105,14 +108,14 @@ class AndIterator : public Iterator { /// Advances all children to the next common item. void advance() override { - assert(!reachedEnd() && "AndIterator can't call advance() at the end."); + assert(!reachedEnd() && "AND iterator can't advance() at the end."); Children.front()->advance(); sync(); } /// Advances all children to the next common item with DocumentID >= ID. void advanceTo(DocID ID) override { - assert(!reachedEnd() && "AndIterator can't call advanceTo() at the end."); + assert(!reachedEnd() && "AND iterator can't advanceTo() at the end."); Children.front()->advanceTo(ID); sync(); } @@ -120,7 +123,7 @@ class AndIterator : public Iterator { DocID peek() const override { return Children.front()->peek(); } float consume() override { - assert(!reachedEnd() && "AndIterator can't consume() at the end."); + assert(!reachedEnd() && "AND iterator can't consume() at the end."); return std::accumulate( begin(Children), end(Children), DEFAULT_BOOST_SCORE, [&](float Current, const std::unique_ptr &Child) { @@ -192,7 +195,7 @@ class OrIterator : public Iterator { public: OrIterator(std::vector> AllChildren) : Children(std::move(AllChildren)) { - assert(Children.size() > 0 && "Or Iterator must have at least one child."); + assert(Children.size() > 0 && "OR iterator must have at least one child."); } /// Returns true if all children are exhausted. @@ -205,8 +208,7 @@ class OrIterator : public Iterator { /// Moves each child pointing to the smallest DocID to the next item. void advance() override { - assert(!reachedEnd() && - "OrIterator can't call advance() after it reached the end."); + assert(!reachedEnd() && "OR iterator can't advance() at the end."); const auto SmallestID = peek(); for (const auto &Child : Children) if (!Child->reachedEnd() && Child->peek() == SmallestID) @@ -215,7 +217,7 @@ class OrIterator : public Iterator { /// Advances each child to the next existing element with DocumentID >= ID. void advanceTo(DocID ID) override { - assert(!reachedEnd() && "Can't advance iterator after it reached the end."); + assert(!reachedEnd() && "OR iterator can't advanceTo() at the end."); for (const auto &Child : Children) if (!Child->reachedEnd()) Child->advanceTo(ID); @@ -224,8 +226,7 @@ class OrIterator : public Iterator { /// Returns the element under cursor of the child with smallest Child->peek() /// value. DocID peek() const override { - assert(!reachedEnd() && - "OrIterator can't peek() after it reached the end."); + assert(!reachedEnd() && "OR iterator can't peek() at the end."); DocID Result = std::numeric_limits::max(); for (const auto &Child : Children) @@ -238,8 +239,7 @@ class OrIterator : public Iterator { // Returns the maximum boosting score among all Children when iterator is not // exhausted and points to the given ID, DEFAULT_BOOST_SCORE otherwise. float consume() override { - assert(!reachedEnd() && - "OrIterator can't consume() after it reached the end."); + assert(!reachedEnd() && "OR iterator can't consume() at the end."); const DocID ID = peek(); return std::accumulate( begin(Children), end(Children), DEFAULT_BOOST_SCORE, @@ -284,21 +284,24 @@ class TrueIterator : public Iterator { bool reachedEnd() const override { return Index >= Size; } void advance() override { - assert(!reachedEnd() && "Can't advance iterator after it reached the end."); + assert(!reachedEnd() && "TRUE iterator can't advance() at the end."); ++Index; } void advanceTo(DocID ID) override { - assert(!reachedEnd() && "Can't advance iterator after it reached the end."); + assert(!reachedEnd() && "TRUE iterator can't advanceTo() at the end."); Index = std::min(ID, Size); } DocID peek() const override { - assert(!reachedEnd() && "TrueIterator can't call peek() at the end."); + assert(!reachedEnd() && "TRUE iterator can't peek() at the end."); return Index; } - float consume() override { return DEFAULT_BOOST_SCORE; } + float consume() override { + assert(!reachedEnd() && "TRUE iterator can't consume() at the end."); + return DEFAULT_BOOST_SCORE; + } size_t estimateSize() const override { return Size; } @@ -364,7 +367,7 @@ class LimitIterator : public Iterator { /// Decreases the limit in case the element consumed at top of the query tree /// comes from the underlying iterator. float consume() override { - assert(!reachedEnd() && "LimitIterator can't consume at the end."); + assert(!reachedEnd() && "LimitIterator can't consume() at the end."); --ItemsLeft; return Child->consume(); } @@ -389,7 +392,7 @@ class LimitIterator : public Iterator { std::vector> consume(Iterator &It) { std::vector> Result; for (; !It.reachedEnd(); It.advance()) - Result.push_back(std::make_pair(It.peek(), It.consume())); + Result.emplace_back(It.peek(), It.consume()); return Result; } @@ -417,8 +420,8 @@ std::unique_ptr createBoost(std::unique_ptr Child, } std::unique_ptr createLimit(std::unique_ptr Child, - size_t Size) { - return llvm::make_unique(move(Child), Size); + size_t Limit) { + return llvm::make_unique(move(Child), Limit); } } // namespace dex diff --git a/clangd/index/dex/Trigram.cpp b/clangd/index/dex/Trigram.cpp index 25a14ffe4..91044fefb 100644 --- a/clangd/index/dex/Trigram.cpp +++ b/clangd/index/dex/Trigram.cpp @@ -87,10 +87,10 @@ std::vector generateIdentifierTrigrams(llvm::StringRef Identifier) { if (Roles[I] != Head && Roles[I] != Tail) continue; for (const unsigned J : Next[I]) { - if (!J) + if (J == 0) continue; for (const unsigned K : Next[J]) { - if (!K) + if (K == 0) continue; add({{LowercaseIdentifier[I], LowercaseIdentifier[J], LowercaseIdentifier[K]}}); @@ -113,8 +113,8 @@ std::vector generateQueryTrigrams(llvm::StringRef Query) { // Additional pass is necessary to count valid identifier characters. // Depending on that, this function might return incomplete trigram. unsigned ValidSymbolsCount = 0; - for (size_t I = 0; I < Roles.size(); ++I) - if (Roles[I] == Head || Roles[I] == Tail) + for (const auto Role : Roles) + if (Role == Head || Role == Tail) ++ValidSymbolsCount; std::string LowercaseQuery = Query.lower(); From 5dbb7d872e747beab87f3ab14522fb7ede177295 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Fri, 31 Aug 2018 12:54:13 +0000 Subject: [PATCH 117/686] [clangd] Collect symbol occurrences in SymbolCollector. SymbolCollector will be used for two cases: - collect Symbol type only, used for indexing preamble AST. - collect Symbol and SymbolOccurrences, used for indexing main AST. For finding local references from the AST, we will implement it in other ways. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341208 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Index.cpp | 43 ++++++++++++ clangd/index/Index.h | 81 +++++++++++++++++++++-- clangd/index/SymbolCollector.cpp | 74 ++++++++++++++++----- clangd/index/SymbolCollector.h | 16 +++++ unittests/clangd/SymbolCollectorTests.cpp | 78 ++++++++++++++++++++-- 5 files changed, 265 insertions(+), 27 deletions(-) diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index eef43603e..bee7e0ae7 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -128,5 +128,48 @@ SymbolSlab SymbolSlab::Builder::build() && { return SymbolSlab(std::move(NewArena), std::move(Symbols)); } +raw_ostream &operator<<(raw_ostream &OS, SymbolOccurrenceKind K) { + if (K == SymbolOccurrenceKind::Unknown) + return OS << "Unknown"; + static const std::vector Messages = {"Decl", "Def", "Ref"}; + bool VisitedOnce = false; + for (unsigned I = 0; I < Messages.size(); ++I) { + if (static_cast(K) & 1u << I) { + if (VisitedOnce) + OS << ", "; + OS << Messages[I]; + VisitedOnce = true; + } + } + return OS; +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const SymbolOccurrence &Occurrence) { + OS << Occurrence.Location << ":" << Occurrence.Kind; + return OS; +} + +void SymbolOccurrenceSlab::insert(const SymbolID &SymID, + const SymbolOccurrence &Occurrence) { + assert(!Frozen && + "Can't insert a symbol occurrence after the slab has been frozen!"); + auto &SymOccurrences = Occurrences[SymID]; + SymOccurrences.push_back(Occurrence); + SymOccurrences.back().Location.FileURI = + UniqueStrings.save(Occurrence.Location.FileURI); +} + +void SymbolOccurrenceSlab::freeze() { + // Deduplicate symbol occurrenes. + for (auto &IDAndOccurrence : Occurrences) { + auto &Occurrence = IDAndOccurrence.getSecond(); + std::sort(Occurrence.begin(), Occurrence.end()); + Occurrence.erase(std::unique(Occurrence.begin(), Occurrence.end()), + Occurrence.end()); + } + Frozen = true; +} + } // namespace clangd } // namespace clang diff --git a/clangd/index/Index.h b/clangd/index/Index.h index c813ab829..f5b96b426 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -32,9 +32,6 @@ struct SymbolLocation { uint32_t Line = 0; // 0-based // Using UTF-16 code units. uint32_t Column = 0; // 0-based - bool operator==(const Position& P) const { - return Line == P.Line && Column == P.Column; - } }; // The URI of the source file where a symbol occurs. @@ -45,11 +42,23 @@ struct SymbolLocation { Position End; explicit operator bool() const { return !FileURI.empty(); } - bool operator==(const SymbolLocation& Loc) const { - return std::tie(FileURI, Start, End) == - std::tie(Loc.FileURI, Loc.Start, Loc.End); - } }; +inline bool operator==(const SymbolLocation::Position &L, + const SymbolLocation::Position &R) { + return std::tie(L.Line, L.Column) == std::tie(R.Line, R.Column); +} +inline bool operator<(const SymbolLocation::Position &L, + const SymbolLocation::Position &R) { + return std::tie(L.Line, L.Column) < std::tie(R.Line, R.Column); +} +inline bool operator==(const SymbolLocation &L, const SymbolLocation &R) { + return std::tie(L.FileURI, L.Start, L.End) == + std::tie(R.FileURI, R.Start, R.End); +} +inline bool operator<(const SymbolLocation &L, const SymbolLocation &R) { + return std::tie(L.FileURI, L.Start, L.End) < + std::tie(R.FileURI, R.Start, R.End); +} llvm::raw_ostream &operator<<(llvm::raw_ostream &, const SymbolLocation &); // The class identifies a particular C++ symbol (class, function, method, etc). @@ -314,6 +323,9 @@ inline SymbolOccurrenceKind operator&(SymbolOccurrenceKind A, return static_cast(static_cast(A) & static_cast(B)); } +static const SymbolOccurrenceKind AllOccurrenceKinds = + SymbolOccurrenceKind::Declaration | SymbolOccurrenceKind::Definition | + SymbolOccurrenceKind::Reference; // Represents a symbol occurrence in the source file. It could be a // declaration/definition/reference occurrence. @@ -324,6 +336,61 @@ struct SymbolOccurrence { SymbolLocation Location; SymbolOccurrenceKind Kind = SymbolOccurrenceKind::Unknown; }; +inline bool operator<(const SymbolOccurrence &L, const SymbolOccurrence &R) { + return std::tie(L.Location, L.Kind) < std::tie(R.Location, R.Kind); +} +inline bool operator==(const SymbolOccurrence &L, const SymbolOccurrence &R) { + return std::tie(L.Location, L.Kind) == std::tie(R.Location, R.Kind); +} +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const SymbolOccurrence &Occurrence); + +// An efficient structure of storing large set of symbol occurrences in memory. +// Filenames are deduplicated. +class SymbolOccurrenceSlab { +public: + using const_iterator = + llvm::DenseMap>::const_iterator; + using iterator = const_iterator; + + SymbolOccurrenceSlab() : UniqueStrings(Arena) {} + + // Define move semantics for the slab, allowing assignment from an rvalue. + // Implicit move assignment is deleted by the compiler because + // StringSaver has a reference type member. + SymbolOccurrenceSlab(SymbolOccurrenceSlab &&Slab) = default; + SymbolOccurrenceSlab &operator=(SymbolOccurrenceSlab &&RHS) { + assert(RHS.Frozen && + "SymbolOcucrrenceSlab must be frozen when move assigned!"); + Arena = std::move(RHS.Arena); + Frozen = true; + Occurrences = std::move(RHS.Occurrences); + return *this; + } + + const_iterator begin() const { return Occurrences.begin(); } + const_iterator end() const { return Occurrences.end(); } + + // Adds a symbol occurrence. + // This is a deep copy: underlying FileURI will be owned by the slab. + void insert(const SymbolID &SymID, const SymbolOccurrence &Occurrence); + + llvm::ArrayRef find(const SymbolID &ID) const { + assert(Frozen && "SymbolOccurrenceSlab must be frozen before looking up!"); + auto It = Occurrences.find(ID); + if (It == Occurrences.end()) + return {}; + return It->second; + } + + void freeze(); + +private: + bool Frozen = false; + llvm::BumpPtrAllocator Arena; + llvm::UniqueStringSaver UniqueStrings; + llvm::DenseMap> Occurrences; +}; struct FuzzyFindRequest { /// \brief A query string for the fuzzy find. This is matched against symbols' diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp index 6c6a79245..67c193984 100644 --- a/clangd/index/SymbolCollector.cpp +++ b/clangd/index/SymbolCollector.cpp @@ -182,7 +182,24 @@ getIncludeHeader(llvm::StringRef QName, const SourceManager &SM, return toURI(SM, Header, Opts); } -// Return the symbol location of the token at \p Loc. +// Return the symbol range of the token at \p TokLoc. +std::pair +getTokenRange(SourceLocation TokLoc, const SourceManager &SM, + const LangOptions &LangOpts) { + auto CreatePosition = [&SM](SourceLocation Loc) { + auto LSPLoc = sourceLocToPosition(SM, Loc); + SymbolLocation::Position Pos; + Pos.Line = LSPLoc.line; + Pos.Column = LSPLoc.character; + return Pos; + }; + + auto TokenLength = clang::Lexer::MeasureTokenLength(TokLoc, SM, LangOpts); + return {CreatePosition(TokLoc), + CreatePosition(TokLoc.getLocWithOffset(TokenLength))}; +} + +// Return the symbol location of the token at \p TokLoc. llvm::Optional getTokenLocation(SourceLocation TokLoc, const SourceManager &SM, const SymbolCollector::Options &Opts, @@ -194,19 +211,9 @@ getTokenLocation(SourceLocation TokLoc, const SourceManager &SM, FileURIStorage = std::move(*U); SymbolLocation Result; Result.FileURI = FileURIStorage; - auto TokenLength = clang::Lexer::MeasureTokenLength(TokLoc, SM, LangOpts); - - auto CreatePosition = [&SM](SourceLocation Loc) { - auto LSPLoc = sourceLocToPosition(SM, Loc); - SymbolLocation::Position Pos; - Pos.Line = LSPLoc.line; - Pos.Column = LSPLoc.character; - return Pos; - }; - - Result.Start = CreatePosition(TokLoc); - auto EndLoc = TokLoc.getLocWithOffset(TokenLength); - Result.End = CreatePosition(EndLoc); + auto Range = getTokenRange(TokLoc, SM, LangOpts); + Result.Start = Range.first; + Result.End = Range.second; return std::move(Result); } @@ -224,6 +231,11 @@ bool isPreferredDeclaration(const NamedDecl &ND, index::SymbolRoleSet Roles) { match(decl(isExpansionInMainFile()), ND, ND.getASTContext()).empty(); } +SymbolOccurrenceKind toOccurrenceKind(index::SymbolRoleSet Roles) { + return static_cast( + static_cast(AllOccurrenceKinds) & Roles); +} + } // namespace SymbolCollector::SymbolCollector(Options Opts) : Opts(std::move(Opts)) {} @@ -308,11 +320,16 @@ bool SymbolCollector::handleDeclOccurence( // Mark D as referenced if this is a reference coming from the main file. // D may not be an interesting symbol, but it's cheaper to check at the end. auto &SM = ASTCtx->getSourceManager(); + auto SpellingLoc = SM.getSpellingLoc(Loc); if (Opts.CountReferences && (Roles & static_cast(index::SymbolRole::Reference)) && - SM.getFileID(SM.getSpellingLoc(Loc)) == SM.getMainFileID()) + SM.getFileID(SpellingLoc) == SM.getMainFileID()) ReferencedDecls.insert(ND); + if ((static_cast(Opts.OccurrenceFilter) & Roles) && + SM.getFileID(SpellingLoc) == SM.getMainFileID()) + DeclOccurrences[ND].emplace_back(SpellingLoc, Roles); + // Don't continue indexing if this is a mere reference. if (!(Roles & static_cast(index::SymbolRole::Declaration) || Roles & static_cast(index::SymbolRole::Definition))) @@ -436,8 +453,35 @@ void SymbolCollector::finish() { IncRef(SymbolID(USR)); } } + + const auto &SM = ASTCtx->getSourceManager(); + auto* MainFileEntry = SM.getFileEntryForID(SM.getMainFileID()); + + if (auto MainFileURI = toURI(SM, MainFileEntry->getName(), Opts)) { + std::string MainURI = *MainFileURI; + for (const auto &It : DeclOccurrences) { + if (auto ID = getSymbolID(It.first)) { + if (Symbols.find(*ID)) { + for (const auto &LocAndRole : It.second) { + SymbolOccurrence Occurrence; + auto Range = + getTokenRange(LocAndRole.first, SM, ASTCtx->getLangOpts()); + Occurrence.Location.Start = Range.first; + Occurrence.Location.End = Range.second; + Occurrence.Location.FileURI = MainURI; + Occurrence.Kind = toOccurrenceKind(LocAndRole.second); + SymbolOccurrences.insert(*ID, Occurrence); + } + } + } + } + } else { + log("Failed to create URI for main file: {0}", MainFileEntry->getName()); + } + ReferencedDecls.clear(); ReferencedMacros.clear(); + DeclOccurrences.clear(); } const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, diff --git a/clangd/index/SymbolCollector.h b/clangd/index/SymbolCollector.h index bc882e47a..fb98b683e 100644 --- a/clangd/index/SymbolCollector.h +++ b/clangd/index/SymbolCollector.h @@ -52,6 +52,10 @@ class SymbolCollector : public index::IndexDataConsumer { const CanonicalIncludes *Includes = nullptr; // Populate the Symbol.References field. bool CountReferences = false; + /// The symbol occurrence kind that will be collected. + /// If not set (Unknown), SymbolCollector will not collect any symbol + /// occurrences. + SymbolOccurrenceKind OccurrenceFilter = SymbolOccurrenceKind::Unknown; // Every symbol collected will be stamped with this origin. SymbolOrigin Origin = SymbolOrigin::Unknown; /// Collect macros. @@ -86,6 +90,11 @@ class SymbolCollector : public index::IndexDataConsumer { SymbolSlab takeSymbols() { return std::move(Symbols).build(); } + SymbolOccurrenceSlab takeOccurrences() { + SymbolOccurrences.freeze(); + return std::move(SymbolOccurrences); + } + void finish() override; private: @@ -94,14 +103,21 @@ class SymbolCollector : public index::IndexDataConsumer { // All Symbols collected from the AST. SymbolSlab::Builder Symbols; + // All symbol occurrences collected from the AST. + // Only symbols declared in preamble (from #inclues) and references from the + // main file will be included. + SymbolOccurrenceSlab SymbolOccurrences; ASTContext *ASTCtx; std::shared_ptr PP; std::shared_ptr CompletionAllocator; std::unique_ptr CompletionTUInfo; Options Opts; + using DeclOccurrence = std::pair; // Symbols referenced from the current TU, flushed on finish(). llvm::DenseSet ReferencedDecls; llvm::DenseSet ReferencedMacros; + llvm::DenseMap> + DeclOccurrences; // Maps canonical declaration provided by clang to canonical declaration for // an index symbol, if clangd prefers a different declaration than that // provided by clang. For example, friend declaration might be considered diff --git a/unittests/clangd/SymbolCollectorTests.cpp b/unittests/clangd/SymbolCollectorTests.cpp index 666d0bb04..279ee0e3f 100644 --- a/unittests/clangd/SymbolCollectorTests.cpp +++ b/unittests/clangd/SymbolCollectorTests.cpp @@ -28,9 +28,15 @@ #include #include +namespace clang { +namespace clangd { + +namespace { + using testing::AllOf; using testing::Eq; using testing::Field; +using testing::IsEmpty; using testing::Not; using testing::UnorderedElementsAre; using testing::UnorderedElementsAreArray; @@ -74,11 +80,18 @@ MATCHER_P(Refs, R, "") { return int(arg.References) == R; } MATCHER_P(ForCodeCompletion, IsIndexedForCodeCompletion, "") { return arg.IsIndexedForCodeCompletion == IsIndexedForCodeCompletion; } - -namespace clang { -namespace clangd { - -namespace { +MATCHER(OccurrenceRange, "") { + const SymbolOccurrence &Pos = testing::get<0>(arg); + const Range &Range = testing::get<1>(arg); + return std::tie(Pos.Location.Start.Line, Pos.Location.Start.Column, + Pos.Location.End.Line, Pos.Location.End.Column) == + std::tie(Range.start.line, Range.start.character, Range.end.line, + Range.end.character); +} +testing::Matcher &> +HaveRanges(const std::vector Ranges) { + return testing::UnorderedPointwise(OccurrenceRange(), Ranges); +} class ShouldCollectSymbolTest : public ::testing::Test { public: @@ -237,6 +250,7 @@ class SymbolCollectorTest : public ::testing::Test { llvm::MemoryBuffer::getMemBuffer(MainCode)); Invocation.run(); Symbols = Factory->Collector->takeSymbols(); + SymbolOccurrences = Factory->Collector->takeOccurrences(); return true; } @@ -247,6 +261,7 @@ class SymbolCollectorTest : public ::testing::Test { std::string TestFileName; std::string TestFileURI; SymbolSlab Symbols; + SymbolOccurrenceSlab SymbolOccurrences; SymbolCollector::Options CollectorOpts; std::unique_ptr PragmaHandler; }; @@ -413,6 +428,59 @@ o]](); )); } +TEST_F(SymbolCollectorTest, Occurrences) { + Annotations Header(R"( + class $foo[[Foo]] { + public: + $foo[[Foo]]() {} + $foo[[Foo]](int); + }; + class $bar[[Bar]]; + void $func[[func]](); + )"); + Annotations Main(R"( + class $bar[[Bar]] {}; + + void $func[[func]](); + + void fff() { + $foo[[Foo]] foo; + $bar[[Bar]] bar; + $func[[func]](); + int abc = 0; + $foo[[Foo]] foo2 = abc; + } + )"); + Annotations SymbolsOnlyInMainCode(R"( + int a; + void b() {} + static const int c = 0; + class d {}; + )"); + CollectorOpts.OccurrenceFilter = AllOccurrenceKinds; + runSymbolCollector(Header.code(), + (Main.code() + SymbolsOnlyInMainCode.code()).str()); + auto HeaderSymbols = TestTU::withHeaderCode(Header.code()).headerSymbols(); + + EXPECT_THAT(SymbolOccurrences.find(findSymbol(Symbols, "Foo").ID), + HaveRanges(Main.ranges("foo"))); + EXPECT_THAT(SymbolOccurrences.find(findSymbol(Symbols, "Bar").ID), + HaveRanges(Main.ranges("bar"))); + EXPECT_THAT(SymbolOccurrences.find(findSymbol(Symbols, "func").ID), + HaveRanges(Main.ranges("func"))); + + // Retrieve IDs for symbols *only* in the main file, and verify these symbols + // are not collected. + auto MainSymbols = + TestTU::withHeaderCode(SymbolsOnlyInMainCode.code()).headerSymbols(); + EXPECT_THAT(SymbolOccurrences.find(findSymbol(MainSymbols, "a").ID), + IsEmpty()); + EXPECT_THAT(SymbolOccurrences.find(findSymbol(MainSymbols, "b").ID), + IsEmpty()); + EXPECT_THAT(SymbolOccurrences.find(findSymbol(MainSymbols, "c").ID), + IsEmpty()); +} + TEST_F(SymbolCollectorTest, References) { const std::string Header = R"( class W; From ef447bd3f257f441df010d21ddfe257958fc4414 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Fri, 31 Aug 2018 13:55:01 +0000 Subject: [PATCH 118/686] [clangd] Flatten out Symbol::Details. It was ill-conceived, sorry. Reviewers: ioeric Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51504 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341211 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 19 ++++---- .../GlobalSymbolBuilderMain.cpp | 12 ++---- clangd/index/Index.cpp | 15 ++----- clangd/index/Index.h | 37 ++++++---------- clangd/index/Merge.cpp | 30 ++++--------- clangd/index/Merge.h | 2 +- clangd/index/SymbolCollector.cpp | 12 ++---- clangd/index/SymbolYAML.cpp | 43 +++---------------- clangd/index/SymbolYAML.h | 5 +-- unittests/clangd/CodeCompleteTests.cpp | 21 +++------ unittests/clangd/FileIndexTests.cpp | 2 +- unittests/clangd/IndexTests.cpp | 19 +++----- unittests/clangd/SymbolCollectorTests.cpp | 19 +++----- 13 files changed, 67 insertions(+), 169 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index e355a8332..6cda2f0b9 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -285,8 +285,7 @@ struct CompletionCandidate { } llvm::Optional headerToInsertIfNotPresent() const { - if (!IndexResult || !IndexResult->Detail || - IndexResult->Detail->IncludeHeader.empty()) + if (!IndexResult || IndexResult->IncludeHeader.empty()) return llvm::None; if (SemaResult && SemaResult->Declaration) { // Avoid inserting new #include if the declaration is found in the current @@ -296,7 +295,7 @@ struct CompletionCandidate { if (SM.isInMainFile(SM.getExpansionLoc(RD->getBeginLoc()))) return llvm::None; } - return IndexResult->Detail->IncludeHeader; + return IndexResult->IncludeHeader; } using Bundle = llvm::SmallVector; @@ -382,7 +381,7 @@ struct CodeCompletionBuilder { log("Failed to generate include insertion edits for adding header " "(FileURI='{0}', IncludeHeader='{1}') into {2}", C.IndexResult->CanonicalDeclaration.FileURI, - C.IndexResult->Detail->IncludeHeader, FileName); + C.IndexResult->IncludeHeader, FileName); } } @@ -397,12 +396,11 @@ struct CodeCompletionBuilder { } else if (C.IndexResult) { S.Signature = C.IndexResult->Signature; S.SnippetSuffix = C.IndexResult->CompletionSnippetSuffix; - if (auto *D = C.IndexResult->Detail) - S.ReturnType = D->ReturnType; + S.ReturnType = C.IndexResult->ReturnType; } if (ExtractDocumentation && Completion.Documentation.empty()) { - if (C.IndexResult && C.IndexResult->Detail) - Completion.Documentation = C.IndexResult->Detail->Documentation; + if (C.IndexResult) + Completion.Documentation = C.IndexResult->Documentation; else if (C.SemaResult) Completion.Documentation = getDocComment(ASTCtx, *C.SemaResult, /*CommentsFromHeader=*/false); @@ -846,9 +844,8 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { IndexRequest.IDs.insert(*S.IDForDoc); } Index->lookup(IndexRequest, [&](const Symbol &S) { - if (!S.Detail || S.Detail->Documentation.empty()) - return; - FetchedDocs[S.ID] = S.Detail->Documentation; + if (!S.Documentation.empty()) + FetchedDocs[S.ID] = S.Documentation; }); log("SigHelp: requested docs for {0} symbols from the index, got {1} " "symbols with non-empty docs in the response", diff --git a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp b/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp index 2437839bd..84211c6cb 100644 --- a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp +++ b/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp @@ -160,17 +160,14 @@ class ToolExecutorConsumer : public SymbolsConsumer { SymbolSlab mergeResults() override { SymbolSlab::Builder UniqueSymbols; - llvm::BumpPtrAllocator Arena; - Symbol::Details Scratch; Executor.getToolResults()->forEachResult( [&](llvm::StringRef Key, llvm::StringRef Value) { - Arena.Reset(); - llvm::yaml::Input Yin(Value, &Arena); - auto Sym = clang::clangd::SymbolFromYAML(Yin, Arena); + llvm::yaml::Input Yin(Value); + auto Sym = clang::clangd::SymbolFromYAML(Yin); clang::clangd::SymbolID ID; Key >> ID; if (const auto *Existing = UniqueSymbols.find(ID)) - UniqueSymbols.insert(mergeSymbol(*Existing, Sym, &Scratch)); + UniqueSymbols.insert(mergeSymbol(*Existing, Sym)); else UniqueSymbols.insert(Sym); }); @@ -188,9 +185,8 @@ class OnTheFlyConsumer : public SymbolsConsumer { void consumeSymbols(SymbolSlab Symbols) override { std::lock_guard Lock(Mut); for (auto &&Sym : Symbols) { - Symbol::Details Scratch; if (const auto *Existing = Result.find(Sym.ID)) - Result.insert(mergeSymbol(*Existing, Sym, &Scratch)); + Result.insert(mergeSymbol(*Existing, Sym)); else Result.insert(Sym); } diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index bee7e0ae7..3a53ce42a 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -90,18 +90,9 @@ static void own(Symbol &S, llvm::UniqueStringSaver &Strings, Intern(S.Signature); Intern(S.CompletionSnippetSuffix); - - if (S.Detail) { - // Copy values of StringRefs into arena. - auto *Detail = Arena.Allocate(); - *Detail = *S.Detail; - // Intern the actual strings. - Intern(Detail->Documentation); - Intern(Detail->ReturnType); - Intern(Detail->IncludeHeader); - // Replace the detail pointer with our copy. - S.Detail = Detail; - } + Intern(S.Documentation); + Intern(S.ReturnType); + Intern(S.IncludeHeader); } void SymbolSlab::Builder::insert(const Symbol &S) { diff --git a/clangd/index/Index.h b/clangd/index/Index.h index f5b96b426..12e0e7ebd 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -208,30 +208,19 @@ struct Symbol { /// This is in LSP snippet syntax (e.g. "({$0})" for a no-args function). /// (When snippets are disabled, the symbol name alone is used). llvm::StringRef CompletionSnippetSuffix; - - /// Optional symbol details that are not required to be set. For example, an - /// index fuzzy match can return a large number of symbol candidates, and it - /// is preferable to send only core symbol information in the batched results - /// and have clients resolve full symbol information for a specific candidate - /// if needed. - struct Details { - /// Documentation including comment for the symbol declaration. - llvm::StringRef Documentation; - /// Type when this symbol is used in an expression. (Short display form). - /// e.g. return type of a function, or type of a variable. - llvm::StringRef ReturnType; - /// This can be either a URI of the header to be #include'd for this symbol, - /// or a literal header quoted with <> or "" that is suitable to be included - /// directly. When this is a URI, the exact #include path needs to be - /// calculated according to the URI scheme. - /// - /// This is a canonical include for the symbol and can be different from - /// FileURI in the CanonicalDeclaration. - llvm::StringRef IncludeHeader; - }; - - // Optional details of the symbol. - const Details *Detail = nullptr; + /// Documentation including comment for the symbol declaration. + llvm::StringRef Documentation; + /// Type when this symbol is used in an expression. (Short display form). + /// e.g. return type of a function, or type of a variable. + llvm::StringRef ReturnType; + /// This can be either a URI of the header to be #include'd for this symbol, + /// or a literal header quoted with <> or "" that is suitable to be included + /// directly. When this is a URI, the exact #include path needs to be + /// calculated according to the URI scheme. + /// + /// This is a canonical include for the symbol and can be different from + /// FileURI in the CanonicalDeclaration. + llvm::StringRef IncludeHeader; // FIXME: add all occurrences support. // FIXME: add extra fields for index scoring signals. diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index 84928ca1a..2f49aa2b2 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -42,13 +42,12 @@ class MergedIndex : public SymbolIndex { SymbolSlab Dyn = std::move(DynB).build(); DenseSet SeenDynamicSymbols; - Symbol::Details Scratch; More |= Static->fuzzyFind(Req, [&](const Symbol &S) { auto DynS = Dyn.find(S.ID); if (DynS == Dyn.end()) return Callback(S); SeenDynamicSymbols.insert(S.ID); - Callback(mergeSymbol(*DynS, S, &Scratch)); + Callback(mergeSymbol(*DynS, S)); }); for (const Symbol &S : Dyn) if (!SeenDynamicSymbols.count(S.ID)) @@ -64,14 +63,13 @@ class MergedIndex : public SymbolIndex { Dynamic->lookup(Req, [&](const Symbol &S) { B.insert(S); }); auto RemainingIDs = Req.IDs; - Symbol::Details Scratch; Static->lookup(Req, [&](const Symbol &S) { const Symbol *Sym = B.find(S.ID); RemainingIDs.erase(S.ID); if (!Sym) Callback(S); else - Callback(mergeSymbol(*Sym, S, &Scratch)); + Callback(mergeSymbol(*Sym, S)); }); for (const auto &ID : RemainingIDs) if (const Symbol *Sym = B.find(ID)) @@ -93,8 +91,7 @@ class MergedIndex : public SymbolIndex { }; } // namespace -Symbol -mergeSymbol(const Symbol &L, const Symbol &R, Symbol::Details *Scratch) { +Symbol mergeSymbol(const Symbol &L, const Symbol &R) { assert(L.ID == R.ID); // We prefer information from TUs that saw the definition. // Classes: this is the def itself. Functions: hopefully the header decl. @@ -114,21 +111,12 @@ mergeSymbol(const Symbol &L, const Symbol &R, Symbol::Details *Scratch) { S.Signature = O.Signature; if (S.CompletionSnippetSuffix == "") S.CompletionSnippetSuffix = O.CompletionSnippetSuffix; - - if (O.Detail) { - if (S.Detail) { - // Copy into scratch space so we can merge. - *Scratch = *S.Detail; - if (Scratch->Documentation == "") - Scratch->Documentation = O.Detail->Documentation; - if (Scratch->ReturnType == "") - Scratch->ReturnType = O.Detail->ReturnType; - if (Scratch->IncludeHeader == "") - Scratch->IncludeHeader = O.Detail->IncludeHeader; - S.Detail = Scratch; - } else - S.Detail = O.Detail; - } + if (S.Documentation == "") + S.Documentation = O.Documentation; + if (S.ReturnType == "") + S.ReturnType = O.ReturnType; + if (S.IncludeHeader == "") + S.IncludeHeader = O.IncludeHeader; S.Origin |= O.Origin | SymbolOrigin::Merge; return S; diff --git a/clangd/index/Merge.h b/clangd/index/Merge.h index 48c3bc6c7..281ae0171 100644 --- a/clangd/index/Merge.h +++ b/clangd/index/Merge.h @@ -18,7 +18,7 @@ namespace clangd { // Merge symbols L and R, preferring data from L in case of conflict. // The two symbols must have the same ID. // Returned symbol may contain data owned by either source. -Symbol mergeSymbol(const Symbol &L, const Symbol &R, Symbol::Details *Scratch); +Symbol mergeSymbol(const Symbol &L, const Symbol &R); // mergedIndex returns a composite index based on two provided Indexes: // - the Dynamic index covers few files, but is relatively up-to-date. diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp index 67c193984..214522145 100644 --- a/clangd/index/SymbolCollector.cpp +++ b/clangd/index/SymbolCollector.cpp @@ -422,9 +422,7 @@ bool SymbolCollector::handleMacroOccurence(const IdentifierInfo *Name, } S.Signature = Signature; S.CompletionSnippetSuffix = SnippetSuffix; - Symbol::Details Detail; - Detail.IncludeHeader = Include; - S.Detail = &Detail; + S.IncludeHeader = Include; Symbols.insert(S); return true; } @@ -530,11 +528,9 @@ const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, } S.Signature = Signature; S.CompletionSnippetSuffix = SnippetSuffix; - Symbol::Details Detail; - Detail.Documentation = Documentation; - Detail.ReturnType = ReturnType; - Detail.IncludeHeader = Include; - S.Detail = &Detail; + S.Documentation = Documentation; + S.ReturnType = ReturnType; + S.IncludeHeader = Include; S.Origin = Opts.Origin; Symbols.insert(S); diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/SymbolYAML.cpp index 1701b5a09..6d9c84b73 100644 --- a/clangd/index/SymbolYAML.cpp +++ b/clangd/index/SymbolYAML.cpp @@ -66,40 +66,9 @@ template <> struct MappingTraits { } }; -template <> struct MappingTraits { - static void mapping(IO &io, Symbol::Details &Detail) { - io.mapOptional("Documentation", Detail.Documentation); - io.mapOptional("ReturnType", Detail.ReturnType); - io.mapOptional("IncludeHeader", Detail.IncludeHeader); - } -}; - -// A YamlIO normalizer for fields of type "const T*" allocated on an arena. -// Normalizes to Optional, so traits should be provided for T. -template struct ArenaPtr { - ArenaPtr(IO &) {} - ArenaPtr(IO &, const T *D) { - if (D) - Opt = *D; - } - - const T *denormalize(IO &IO) { - assert(IO.getContext() && "Expecting an arena (as context) to allocate " - "data for read symbols."); - if (!Opt) - return nullptr; - return new (*static_cast(IO.getContext())) - T(std::move(*Opt)); // Allocate a copy of Opt on the arena. - } - - llvm::Optional Opt; -}; - template <> struct MappingTraits { static void mapping(IO &IO, Symbol &Sym) { MappingNormalization NSymbolID(IO, Sym.ID); - MappingNormalization, const Symbol::Details *> - NDetail(IO, Sym.Detail); IO.mapRequired("ID", NSymbolID->HexString); IO.mapRequired("Name", Sym.Name); IO.mapRequired("Scope", Sym.Scope); @@ -112,7 +81,9 @@ template <> struct MappingTraits { false); IO.mapOptional("Signature", Sym.Signature); IO.mapOptional("CompletionSnippetSuffix", Sym.CompletionSnippetSuffix); - IO.mapOptional("Detail", NDetail->Opt); + IO.mapOptional("Documentation", Sym.Documentation); + IO.mapOptional("ReturnType", Sym.ReturnType); + IO.mapOptional("IncludeHeader", Sym.IncludeHeader); } }; @@ -169,9 +140,7 @@ namespace clang { namespace clangd { SymbolSlab symbolsFromYAML(llvm::StringRef YAMLContent) { - // Store data of pointer fields (excl. `StringRef`) like `Detail`. - llvm::BumpPtrAllocator Arena; - llvm::yaml::Input Yin(YAMLContent, &Arena); + llvm::yaml::Input Yin(YAMLContent); std::vector S; Yin >> S; @@ -181,9 +150,7 @@ SymbolSlab symbolsFromYAML(llvm::StringRef YAMLContent) { return std::move(Syms).build(); } -Symbol SymbolFromYAML(llvm::yaml::Input &Input, llvm::BumpPtrAllocator &Arena) { - // We could grab Arena out of Input, but it'd be a huge hazard for callers. - assert(Input.getContext() == &Arena); +Symbol SymbolFromYAML(llvm::yaml::Input &Input) { Symbol S; Input >> S; return S; diff --git a/clangd/index/SymbolYAML.h b/clangd/index/SymbolYAML.h index a4b945c37..897b1b7c6 100644 --- a/clangd/index/SymbolYAML.h +++ b/clangd/index/SymbolYAML.h @@ -30,9 +30,8 @@ namespace clangd { SymbolSlab symbolsFromYAML(llvm::StringRef YAMLContent); // Read one symbol from a YAML-stream. -// The arena must be the Input's context! (i.e. yaml::Input Input(Text, &Arena)) -// The returned symbol is backed by both Input and Arena. -Symbol SymbolFromYAML(llvm::yaml::Input &Input, llvm::BumpPtrAllocator &Arena); +// The returned symbol is backed by Input. +Symbol SymbolFromYAML(llvm::yaml::Input &Input); // Convert a single symbol to YAML-format string. // The YAML result is safe to concatenate. diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index cb0e9a3e5..145eb8018 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -564,12 +564,10 @@ TEST(CompletionTest, IncludeInsertionPreprocessorIntegrationTests) { IgnoreDiagnostics DiagConsumer; ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); - Symbol::Details Scratch; auto BarURI = URI::createFile(BarHeader).toString(); Symbol Sym = cls("ns::X"); Sym.CanonicalDeclaration.FileURI = BarURI; - Scratch.IncludeHeader = BarURI; - Sym.Detail = &Scratch; + Sym.IncludeHeader = BarURI; // Shoten include path based on search dirctory and insert. auto Results = completions(Server, R"cpp( @@ -595,16 +593,14 @@ TEST(CompletionTest, NoIncludeInsertionWhenDeclFoundInFile) { IgnoreDiagnostics DiagConsumer; ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); - Symbol::Details Scratch; Symbol SymX = cls("ns::X"); Symbol SymY = cls("ns::Y"); std::string BarHeader = testPath("bar.h"); auto BarURI = URI::createFile(BarHeader).toString(); SymX.CanonicalDeclaration.FileURI = BarURI; SymY.CanonicalDeclaration.FileURI = BarURI; - Scratch.IncludeHeader = ""; - SymX.Detail = &Scratch; - SymY.Detail = &Scratch; + SymX.IncludeHeader = ""; + SymY.IncludeHeader = ""; // Shoten include path based on search dirctory and insert. auto Results = completions(Server, R"cpp( @@ -1179,11 +1175,9 @@ TEST(CompletionTest, OverloadBundling) { UnorderedElementsAre(Labeled("GFuncC(…)"), Labeled("GFuncD(int)"))); // Differences in header-to-insert suppress bundling. - Symbol::Details Detail; std::string DeclFile = URI::createFile(testPath("foo")).toString(); NoArgsGFunc.CanonicalDeclaration.FileURI = DeclFile; - Detail.IncludeHeader = ""; - NoArgsGFunc.Detail = &Detail; + NoArgsGFunc.IncludeHeader = ""; EXPECT_THAT( completions(Context + "int y = GFunc^", {NoArgsGFunc}, Opts).Completions, UnorderedElementsAre(AllOf(Named("GFuncC"), InsertInclude("")), @@ -1664,13 +1658,10 @@ TEST(SignatureHelpTest, InstantiatedSignatures) { } TEST(SignatureHelpTest, IndexDocumentation) { - Symbol::Details DocDetails; - DocDetails.Documentation = "Doc from the index"; - Symbol Foo0 = sym("foo", index::SymbolKind::Function, "@F@\\0#"); - Foo0.Detail = &DocDetails; + Foo0.Documentation = "Doc from the index"; Symbol Foo1 = sym("foo", index::SymbolKind::Function, "@F@\\0#I#"); - Foo1.Detail = &DocDetails; + Foo1.Documentation = "Doc from the index"; Symbol Foo2 = sym("foo", index::SymbolKind::Function, "@F@\\0#I#I#"); StringRef Sig0 = R"cpp( diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index 08e85689e..97ba2d63a 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -177,7 +177,7 @@ TEST(FileIndexTest, NoIncludeCollected) { Req.Query = ""; bool SeenSymbol = false; M.fuzzyFind(Req, [&](const Symbol &Sym) { - EXPECT_TRUE(Sym.Detail->IncludeHeader.empty()); + EXPECT_TRUE(Sym.IncludeHeader.empty()); SeenSymbol = true; }); EXPECT_TRUE(SeenSymbol); diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp index 5d3e85ff5..01f44bbd0 100644 --- a/unittests/clangd/IndexTests.cpp +++ b/unittests/clangd/IndexTests.cpp @@ -198,32 +198,23 @@ TEST(MergeTest, Merge) { R.References = 2; L.Signature = "()"; // present in left only R.CompletionSnippetSuffix = "{$1:0}"; // present in right only - Symbol::Details DetL, DetR; - DetL.ReturnType = "DetL"; - DetR.ReturnType = "DetR"; - DetR.Documentation = "--doc--"; - L.Detail = &DetL; - R.Detail = &DetR; + R.Documentation = "--doc--"; L.Origin = SymbolOrigin::Dynamic; R.Origin = SymbolOrigin::Static; - Symbol::Details Scratch; - Symbol M = mergeSymbol(L, R, &Scratch); + Symbol M = mergeSymbol(L, R); EXPECT_EQ(M.Name, "Foo"); EXPECT_EQ(M.CanonicalDeclaration.FileURI, "file:///left.h"); EXPECT_EQ(M.References, 3u); EXPECT_EQ(M.Signature, "()"); EXPECT_EQ(M.CompletionSnippetSuffix, "{$1:0}"); - ASSERT_TRUE(M.Detail); - EXPECT_EQ(M.Detail->ReturnType, "DetL"); - EXPECT_EQ(M.Detail->Documentation, "--doc--"); + EXPECT_EQ(M.Documentation, "--doc--"); EXPECT_EQ(M.Origin, SymbolOrigin::Dynamic | SymbolOrigin::Static | SymbolOrigin::Merge); } TEST(MergeTest, PreferSymbolWithDefn) { Symbol L, R; - Symbol::Details Scratch; L.ID = R.ID = SymbolID("hello"); L.CanonicalDeclaration.FileURI = "file:/left.h"; @@ -231,13 +222,13 @@ TEST(MergeTest, PreferSymbolWithDefn) { L.Name = "left"; R.Name = "right"; - Symbol M = mergeSymbol(L, R, &Scratch); + Symbol M = mergeSymbol(L, R); EXPECT_EQ(M.CanonicalDeclaration.FileURI, "file:/left.h"); EXPECT_EQ(M.Definition.FileURI, ""); EXPECT_EQ(M.Name, "left"); R.Definition.FileURI = "file:/right.cpp"; // Now right will be favored. - M = mergeSymbol(L, R, &Scratch); + M = mergeSymbol(L, R); EXPECT_EQ(M.CanonicalDeclaration.FileURI, "file:/right.h"); EXPECT_EQ(M.Definition.FileURI, "file:/right.cpp"); EXPECT_EQ(M.Name, "right"); diff --git a/unittests/clangd/SymbolCollectorTests.cpp b/unittests/clangd/SymbolCollectorTests.cpp index 279ee0e3f..5769a2668 100644 --- a/unittests/clangd/SymbolCollectorTests.cpp +++ b/unittests/clangd/SymbolCollectorTests.cpp @@ -45,22 +45,16 @@ using testing::UnorderedElementsAreArray; MATCHER_P(Labeled, Label, "") { return (arg.Name + arg.Signature).str() == Label; } -MATCHER(HasReturnType, "") { - return arg.Detail && !arg.Detail->ReturnType.empty(); -} -MATCHER_P(ReturnType, D, "") { - return arg.Detail && arg.Detail->ReturnType == D; -} -MATCHER_P(Doc, D, "") { return arg.Detail && arg.Detail->Documentation == D; } +MATCHER(HasReturnType, "") { return !arg.ReturnType.empty(); } +MATCHER_P(ReturnType, D, "") { return arg.ReturnType == D; } +MATCHER_P(Doc, D, "") { return arg.Documentation == D; } MATCHER_P(Snippet, S, "") { return (arg.Name + arg.CompletionSnippetSuffix).str() == S; } MATCHER_P(QName, Name, "") { return (arg.Scope + arg.Name).str() == Name; } MATCHER_P(DeclURI, P, "") { return arg.CanonicalDeclaration.FileURI == P; } MATCHER_P(DefURI, P, "") { return arg.Definition.FileURI == P; } -MATCHER_P(IncludeHeader, P, "") { - return arg.Detail && arg.Detail->IncludeHeader == P; -} +MATCHER_P(IncludeHeader, P, "") { return arg.IncludeHeader == P; } MATCHER_P(DeclRange, Pos, "") { return std::tie(arg.CanonicalDeclaration.Start.Line, arg.CanonicalDeclaration.Start.Column, @@ -764,9 +758,8 @@ Scope: 'clang::' Line: 1 Column: 1 IsIndexedForCodeCompletion: true -Detail: - Documentation: 'Foo doc' - ReturnType: 'int' +Documentation: 'Foo doc' +ReturnType: 'int' ... )"; const std::string YAML2 = R"( From be56198bc317c6412f61ab15f81f758af1899e5b Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Fri, 31 Aug 2018 19:53:37 +0000 Subject: [PATCH 119/686] [clangd] Implement findOccurrences interface in dynamic index. Summary: Implement the interface in - FileIndex - MemIndex - MergeIndex Depends on https://reviews.llvm.org/D50385. Reviewers: sammccall, ilya-biryukov Reviewed By: sammccall Subscribers: mgrang, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51279 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341242 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/FileIndex.cpp | 76 +++++++++++++++--- clangd/index/FileIndex.h | 17 ++-- clangd/index/Index.h | 12 +++ clangd/index/MemIndex.cpp | 40 +++++++++- clangd/index/MemIndex.h | 24 ++++-- clangd/index/Merge.cpp | 23 +++++- clangd/index/Merge.h | 4 + clangd/tool/ClangdMain.cpp | 3 +- unittests/clangd/CodeCompleteTests.cpp | 3 +- unittests/clangd/FileIndexTests.cpp | 104 ++++++++++++++++++++++-- unittests/clangd/IndexTests.cpp | 106 +++++++++++++++++++++---- unittests/clangd/TestTU.cpp | 5 +- 12 files changed, 361 insertions(+), 56 deletions(-) diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 48d05959d..883faf9f3 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -16,9 +16,10 @@ namespace clang { namespace clangd { -SymbolSlab indexAST(ASTContext &AST, std::shared_ptr PP, - llvm::Optional> TopLevelDecls, - llvm::ArrayRef URISchemes) { +std::pair +indexAST(ASTContext &AST, std::shared_ptr PP, + llvm::Optional> TopLevelDecls, + llvm::ArrayRef URISchemes) { SymbolCollector::Options CollectorOpts; // FIXME(ioeric): we might also want to collect include headers. We would need // to make sure all includes are canonicalized (with CanonicalIncludes), which @@ -31,8 +32,6 @@ SymbolSlab indexAST(ASTContext &AST, std::shared_ptr PP, CollectorOpts.URISchemes = URISchemes; CollectorOpts.Origin = SymbolOrigin::Dynamic; - SymbolCollector Collector(std::move(CollectorOpts)); - Collector.setPreprocessor(PP); index::IndexingOptions IndexOpts; // We only need declarations, because we don't count references. IndexOpts.SystemSymbolFilter = @@ -46,20 +45,45 @@ SymbolSlab indexAST(ASTContext &AST, std::shared_ptr PP, DeclsToIndex.assign(AST.getTranslationUnitDecl()->decls().begin(), AST.getTranslationUnitDecl()->decls().end()); + // We only collect occurrences when indexing main AST. + // FIXME: this is a hacky way to detect whether we are indexing preamble AST + // or main AST, we should make it explicitly. + bool IsIndexMainAST = TopLevelDecls.hasValue(); + if (IsIndexMainAST) + CollectorOpts.OccurrenceFilter = AllOccurrenceKinds; + + SymbolCollector Collector(std::move(CollectorOpts)); + Collector.setPreprocessor(PP); index::indexTopLevelDecls(AST, DeclsToIndex, Collector, IndexOpts); - return Collector.takeSymbols(); + const auto &SM = AST.getSourceManager(); + const auto *MainFileEntry = SM.getFileEntryForID(SM.getMainFileID()); + std::string FileName = MainFileEntry ? MainFileEntry->getName() : ""; + + auto Syms = Collector.takeSymbols(); + auto Occurrences = Collector.takeOccurrences(); + vlog("index {0}AST for {1}: \n" + " symbol slab: {2} symbols, {3} bytes\n" + " occurrence slab: {4} symbols, {5} bytes", + IsIndexMainAST ? "Main" : "Preamble", FileName, Syms.size(), + Syms.bytes(), Occurrences.size(), Occurrences.bytes()); + return {std::move(Syms), std::move(Occurrences)}; } FileIndex::FileIndex(std::vector URISchemes) : URISchemes(std::move(URISchemes)) {} -void FileSymbols::update(PathRef Path, std::unique_ptr Slab) { +void FileSymbols::update(PathRef Path, std::unique_ptr Slab, + std::unique_ptr Occurrences) { std::lock_guard Lock(Mutex); if (!Slab) FileToSlabs.erase(Path); else FileToSlabs[Path] = std::move(Slab); + if (!Occurrences) + FileToOccurrenceSlabs.erase(Path); + else + FileToOccurrenceSlabs[Path] = std::move(Occurrences); } std::shared_ptr> FileSymbols::allSymbols() { @@ -85,19 +109,47 @@ std::shared_ptr> FileSymbols::allSymbols() { return {std::move(Snap), Pointers}; } +std::shared_ptr FileSymbols::allOccurrences() const { + // The snapshot manages life time of symbol occurrence slabs and provides + // pointers to all occurrences in all occurrence slabs. + struct Snapshot { + MemIndex::OccurrenceMap Occurrences; // ID => {Occurrence} + std::vector> KeepAlive; + }; + + auto Snap = std::make_shared(); + { + std::lock_guard Lock(Mutex); + + for (const auto &FileAndSlab : FileToOccurrenceSlabs) { + Snap->KeepAlive.push_back(FileAndSlab.second); + for (const auto &IDAndOccurrences : *FileAndSlab.second) { + auto &Occurrences = Snap->Occurrences[IDAndOccurrences.first]; + for (const auto &Occurrence : IDAndOccurrences.second) + Occurrences.push_back(&Occurrence); + } + } + } + + return {std::move(Snap), &Snap->Occurrences}; +} + void FileIndex::update(PathRef Path, ASTContext *AST, std::shared_ptr PP, llvm::Optional> TopLevelDecls) { if (!AST) { - FSymbols.update(Path, nullptr); + FSymbols.update(Path, nullptr, nullptr); } else { assert(PP); auto Slab = llvm::make_unique(); - *Slab = indexAST(*AST, PP, TopLevelDecls, URISchemes); - FSymbols.update(Path, std::move(Slab)); + auto OccurrenceSlab = llvm::make_unique(); + auto IndexResults = indexAST(*AST, PP, TopLevelDecls, URISchemes); + std::tie(*Slab, *OccurrenceSlab) = + indexAST(*AST, PP, TopLevelDecls, URISchemes); + FSymbols.update(Path, std::move(Slab), std::move(OccurrenceSlab)); } auto Symbols = FSymbols.allSymbols(); - Index.build(std::move(Symbols)); + Index.build(std::move(Symbols), FSymbols.allOccurrences()); } bool FileIndex::fuzzyFind( @@ -115,7 +167,7 @@ void FileIndex::lookup( void FileIndex::findOccurrences( const OccurrencesRequest &Req, llvm::function_ref Callback) const { - log("findOccurrences is not implemented."); + Index.findOccurrences(Req, Callback); } size_t FileIndex::estimateMemoryUsage() const { diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index 58fad2f62..079447288 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -39,18 +39,25 @@ namespace clangd { /// locking when we swap or obtain refereces to snapshots. class FileSymbols { public: - /// \brief Updates all symbols in a file. If \p Slab is nullptr, symbols for - /// \p Path will be removed. - void update(PathRef Path, std::unique_ptr Slab); + /// \brief Updates all symbols and occurrences in a file. + /// If \p Slab (Occurrence) is nullptr, symbols (occurrences) for \p Path + /// will be removed. + void update(PathRef Path, std::unique_ptr Slab, + std::unique_ptr Occurrences); // The shared_ptr keeps the symbols alive std::shared_ptr> allSymbols(); + /// Returns all symbol occurrences for all active files. + std::shared_ptr allOccurrences() const; + private: mutable std::mutex Mutex; /// \brief Stores the latest snapshots for all active files. llvm::StringMap> FileToSlabs; + /// Stores the latest occurrence slabs for all active files. + llvm::StringMap> FileToOccurrenceSlabs; }; /// \brief This manages symbls from files and an in-memory index on all symbols. @@ -90,12 +97,12 @@ class FileIndex : public SymbolIndex { std::vector URISchemes; }; -/// Retrieves namespace and class level symbols in \p AST. +/// Retrieves symbols and symbol occurrences in \p AST. /// Exposed to assist in unit tests. /// If URISchemes is empty, the default schemes in SymbolCollector will be used. /// If \p TopLevelDecls is set, only these decls are indexed. Otherwise, all top /// level decls obtained from \p AST are indexed. -SymbolSlab +std::pair indexAST(ASTContext &AST, std::shared_ptr PP, llvm::Optional> TopLevelDecls = llvm::None, llvm::ArrayRef URISchemes = {}); diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 12e0e7ebd..410919bed 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -344,6 +344,12 @@ class SymbolOccurrenceSlab { SymbolOccurrenceSlab() : UniqueStrings(Arena) {} + static SymbolOccurrenceSlab createEmpty() { + SymbolOccurrenceSlab Empty; + Empty.freeze(); + return Empty; + } + // Define move semantics for the slab, allowing assignment from an rvalue. // Implicit move assignment is deleted by the compiler because // StringSaver has a reference type member. @@ -360,6 +366,12 @@ class SymbolOccurrenceSlab { const_iterator begin() const { return Occurrences.begin(); } const_iterator end() const { return Occurrences.end(); } + size_t bytes() const { + return sizeof(*this) + Arena.getTotalMemory() + Occurrences.getMemorySize(); + } + + size_t size() const { return Occurrences.size(); } + // Adds a symbol occurrence. // This is a deep copy: underlying FileURI will be owned by the slab. void insert(const SymbolID &SymID, const SymbolOccurrence &Occurrence); diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index 19a64ad95..8d314d1fe 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -15,7 +15,27 @@ namespace clang { namespace clangd { -void MemIndex::build(std::shared_ptr> Syms) { +static std::shared_ptr +getOccurrencesFromSlab(SymbolOccurrenceSlab OccurrencesSlab) { + struct Snapshot { + SymbolOccurrenceSlab Slab; + MemIndex::OccurrenceMap Occurrences; + }; + + auto Snap = std::make_shared(); + Snap->Slab = std::move(OccurrencesSlab); + for (const auto &IDAndOccurrences : Snap->Slab) { + auto &Occurrences = Snap->Occurrences[IDAndOccurrences.first]; + for (const auto &Occurrence : IDAndOccurrences.second) + Occurrences.push_back(&Occurrence); + } + return {std::move(Snap), &Snap->Occurrences}; +} + +void MemIndex::build(std::shared_ptr> Syms, + std::shared_ptr AllOccurrences) { + assert(Syms && "Syms must be set when build MemIndex"); + assert(AllOccurrences && "Occurrences must be set when build MemIndex"); llvm::DenseMap TempIndex; for (const Symbol *Sym : *Syms) TempIndex[Sym->ID] = Sym; @@ -25,15 +45,18 @@ void MemIndex::build(std::shared_ptr> Syms) { std::lock_guard Lock(Mutex); Index = std::move(TempIndex); Symbols = std::move(Syms); // Relase old symbols. + Occurrences = std::move(AllOccurrences); } vlog("Built MemIndex with estimated memory usage {0} bytes.", estimateMemoryUsage()); } -std::unique_ptr MemIndex::build(SymbolSlab Slab) { +std::unique_ptr MemIndex::build(SymbolSlab Symbols, + SymbolOccurrenceSlab Occurrences) { auto Idx = llvm::make_unique(); - Idx->build(getSymbolsFromSlab(std::move(Slab))); + Idx->build(getSymbolsFromSlab(std::move(Symbols)), + getOccurrencesFromSlab(std::move(Occurrences))); return std::move(Idx); } @@ -84,7 +107,16 @@ void MemIndex::lookup(const LookupRequest &Req, void MemIndex::findOccurrences( const OccurrencesRequest &Req, llvm::function_ref Callback) const { - log("findOccurrences is not implemented."); + std::lock_guard Lock(Mutex); + for (const auto &ReqID : Req.IDs) { + auto FoundOccurrences = Occurrences->find(ReqID); + if (FoundOccurrences == Occurrences->end()) + continue; + for (const auto *O : FoundOccurrences->second) { + if (static_cast(Req.Filter & O->Kind)) + Callback(*O); + } + } } std::shared_ptr> diff --git a/clangd/index/MemIndex.h b/clangd/index/MemIndex.h index feca87af5..6d16afa5b 100644 --- a/clangd/index/MemIndex.h +++ b/clangd/index/MemIndex.h @@ -16,16 +16,24 @@ namespace clang { namespace clangd { -/// \brief This implements an index for a (relatively small) set of symbols that -/// can be easily managed in memory. +/// \brief This implements an index for a (relatively small) set of symbols (or +/// symbol occurrences) that can be easily managed in memory. class MemIndex : public SymbolIndex { public: - /// \brief (Re-)Build index for `Symbols`. All symbol pointers must remain - /// accessible as long as `Symbols` is kept alive. - void build(std::shared_ptr> Symbols); + /// Maps from a symbol ID to all corresponding symbol occurrences. + /// The map doesn't own occurrence objects. + using OccurrenceMap = + llvm::DenseMap>; - /// \brief Build index from a symbol slab. - static std::unique_ptr build(SymbolSlab Slab); + /// \brief (Re-)Build index for `Symbols` and update `Occurrences`. + /// All symbol pointers and symbol occurrence pointers must remain accessible + /// as long as `Symbols` and `Occurrences` are kept alive. + void build(std::shared_ptr> Symbols, + std::shared_ptr Occurrences); + + /// \brief Build index from a symbol slab and a symbol occurrence slab. + static std::unique_ptr build(SymbolSlab Symbols, + SymbolOccurrenceSlab Occurrences); bool fuzzyFind(const FuzzyFindRequest &Req, @@ -47,6 +55,8 @@ class MemIndex : public SymbolIndex { // Index is a set of symbols that are deduplicated by symbol IDs. // FIXME: build smarter index structure. llvm::DenseMap Index; + // A map from symbol ID to symbol occurrences, support query by IDs. + std::shared_ptr Occurrences; mutable std::mutex Mutex; }; diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index 2f49aa2b2..27a478544 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +#include + #include "Merge.h" #include "../Logger.h" #include "llvm/ADT/STLExtras.h" @@ -79,7 +81,26 @@ class MergedIndex : public SymbolIndex { void findOccurrences(const OccurrencesRequest &Req, llvm::function_ref Callback) const override { - log("findOccurrences is not implemented."); + // We don't want duplicated occurrences from the static/dynamic indexes, + // and we can't reliably duplicate them because occurrence offsets may + // differ slightly. + // We consider the dynamic index authoritative and report all its + // occurrences, and only report static index occurrences from other files. + // + // FIXME: The heuristic fails if the dynamic index containts a file, but all + // occurrences were removed (we will report stale ones from the static + // index). Ultimately we should explicit check which index has the file + // instead. + std::set DynamicIndexFileURIs; + Dynamic->findOccurrences(Req, [&](const SymbolOccurrence &O) { + DynamicIndexFileURIs.insert(O.Location.FileURI); + Callback(O); + }); + Static->findOccurrences(Req, [&](const SymbolOccurrence &O) { + if (DynamicIndexFileURIs.count(O.Location.FileURI)) + return; + Callback(O); + }); } size_t estimateMemoryUsage() const override { diff --git a/clangd/index/Merge.h b/clangd/index/Merge.h index 281ae0171..38a688704 100644 --- a/clangd/index/Merge.h +++ b/clangd/index/Merge.h @@ -24,6 +24,10 @@ Symbol mergeSymbol(const Symbol &L, const Symbol &R); // - the Dynamic index covers few files, but is relatively up-to-date. // - the Static index covers a bigger set of files, but is relatively stale. // The returned index attempts to combine results, and avoid duplicates. +// +// FIXME: We don't have a mechanism in Index to track deleted symbols and +// occurrences in dirty files, so the merged index may return stale symbols +// and occurrences from Static index. std::unique_ptr mergeIndex(const SymbolIndex *Dynamic, const SymbolIndex *Static); diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 52c76400f..cf5c7fac0 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -54,7 +54,8 @@ std::unique_ptr buildStaticIndex(llvm::StringRef YamlSymbolFile) { SymsBuilder.insert(Sym); return UseDex ? dex::DexIndex::build(std::move(SymsBuilder).build()) - : MemIndex::build(std::move(SymsBuilder).build()); + : MemIndex::build(std::move(SymsBuilder).build(), + SymbolOccurrenceSlab::createEmpty()); } } // namespace diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 145eb8018..284149a5e 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -76,7 +76,8 @@ std::unique_ptr memIndex(std::vector Symbols) { SymbolSlab::Builder Slab; for (const auto &Sym : Symbols) Slab.insert(Sym); - return MemIndex::build(std::move(Slab).build()); + return MemIndex::build(std::move(Slab).build(), + SymbolOccurrenceSlab::createEmpty()); } CodeCompleteResult completions(ClangdServer &Server, StringRef TestCode, diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index 97ba2d63a..727240d74 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -7,18 +7,28 @@ // //===----------------------------------------------------------------------===// +#include "Annotations.h" #include "ClangdUnit.h" #include "TestFS.h" #include "TestTU.h" +#include "gmock/gmock.h" #include "index/FileIndex.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/PCHContainerOperations.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/CompilationDatabase.h" -#include "gmock/gmock.h" #include "gtest/gtest.h" using testing::UnorderedElementsAre; +using testing::AllOf; + +MATCHER_P(OccurrenceRange, Range, "") { + return std::tie(arg.Location.Start.Line, arg.Location.Start.Column, + arg.Location.End.Line, arg.Location.End.Column) == + std::tie(Range.start.line, Range.start.character, Range.end.line, + Range.end.character); +} +MATCHER_P(FileURI, F, "") { return arg.Location.FileURI == F; } namespace clang { namespace clangd { @@ -39,6 +49,15 @@ std::unique_ptr numSlab(int Begin, int End) { return llvm::make_unique(std::move(Slab).build()); } +std::unique_ptr occurrenceSlab(const SymbolID &ID, + llvm::StringRef Path) { + auto Slab = llvm::make_unique(); + SymbolOccurrence Occurrence; + Occurrence.Location.FileURI = Path; + Slab->insert(ID, Occurrence); + return Slab; +} + std::vector getSymbolNames(const std::vector &Symbols) { std::vector Names; @@ -47,19 +66,38 @@ getSymbolNames(const std::vector &Symbols) { return Names; } +std::vector +getOccurrencePath(const std::vector &Occurrences) { + std::vector Paths; + for (const auto *O : Occurrences) + Paths.push_back(O->Location.FileURI); + return Paths; +} + +std::unique_ptr emptyOccurrence() { + auto EmptySlab = llvm::make_unique(); + EmptySlab->freeze(); + return EmptySlab; +} + TEST(FileSymbolsTest, UpdateAndGet) { FileSymbols FS; EXPECT_THAT(getSymbolNames(*FS.allSymbols()), UnorderedElementsAre()); + EXPECT_TRUE(FS.allOccurrences()->empty()); - FS.update("f1", numSlab(1, 3)); + SymbolID ID("1"); + FS.update("f1", numSlab(1, 3), occurrenceSlab(ID, "f1.cc")); EXPECT_THAT(getSymbolNames(*FS.allSymbols()), UnorderedElementsAre("1", "2", "3")); + auto Occurrences = FS.allOccurrences(); + EXPECT_THAT(getOccurrencePath((*Occurrences)[ID]), + UnorderedElementsAre("f1.cc")); } TEST(FileSymbolsTest, Overlap) { FileSymbols FS; - FS.update("f1", numSlab(1, 3)); - FS.update("f2", numSlab(3, 5)); + FS.update("f1", numSlab(1, 3), emptyOccurrence()); + FS.update("f2", numSlab(3, 5), emptyOccurrence()); EXPECT_THAT(getSymbolNames(*FS.allSymbols()), UnorderedElementsAre("1", "2", "3", "3", "4", "5")); } @@ -67,14 +105,22 @@ TEST(FileSymbolsTest, Overlap) { TEST(FileSymbolsTest, SnapshotAliveAfterRemove) { FileSymbols FS; - FS.update("f1", numSlab(1, 3)); + SymbolID ID("1"); + FS.update("f1", numSlab(1, 3), occurrenceSlab(ID, "f1.cc")); auto Symbols = FS.allSymbols(); EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3")); + auto Occurrences = FS.allOccurrences(); + EXPECT_THAT(getOccurrencePath((*Occurrences)[ID]), + UnorderedElementsAre("f1.cc")); - FS.update("f1", nullptr); + FS.update("f1", nullptr, nullptr); EXPECT_THAT(getSymbolNames(*FS.allSymbols()), UnorderedElementsAre()); EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3")); + + EXPECT_TRUE(FS.allOccurrences()->empty()); + EXPECT_THAT(getOccurrencePath((*Occurrences)[ID]), + UnorderedElementsAre("f1.cc")); } std::vector match(const SymbolIndex &I, @@ -270,6 +316,52 @@ TEST(FileIndexTest, RebuildWithPreamble) { UnorderedElementsAre("ns_in_header", "ns_in_header::func_in_header")); } +TEST(FileIndexTest, Occurrences) { + const char *HeaderCode = "class Foo {};"; + Annotations MainCode(R"cpp( + void f() { + $foo[[Foo]] foo; + } + )cpp"); + + auto Foo = + findSymbol(TestTU::withHeaderCode(HeaderCode).headerSymbols(), "Foo"); + + OccurrencesRequest Request; + Request.IDs = {Foo.ID}; + Request.Filter = SymbolOccurrenceKind::Declaration | + SymbolOccurrenceKind::Definition | + SymbolOccurrenceKind::Reference; + + FileIndex Index(/*URISchemes*/ {"unittest"}); + // Add test.cc + TestTU Test; + Test.HeaderCode = HeaderCode; + Test.Code = MainCode.code(); + Test.Filename = "test.cc"; + auto AST = Test.build(); + Index.update(Test.Filename, &AST.getASTContext(), AST.getPreprocessorPtr(), + AST.getLocalTopLevelDecls()); + // Add test2.cc + TestTU Test2; + Test2.HeaderCode = HeaderCode; + Test2.Code = MainCode.code(); + Test2.Filename = "test2.cc"; + AST = Test2.build(); + Index.update(Test2.Filename, &AST.getASTContext(), AST.getPreprocessorPtr(), + AST.getLocalTopLevelDecls()); + + std::vector Results; + Index.findOccurrences( + Request, [&Results](const SymbolOccurrence &O) { Results.push_back(O); }); + + EXPECT_THAT(Results, + UnorderedElementsAre(AllOf(OccurrenceRange(MainCode.range("foo")), + FileURI("unittest:///test.cc")), + AllOf(OccurrenceRange(MainCode.range("foo")), + FileURI("unittest:///test2.cc")))); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp index 01f44bbd0..1ac710340 100644 --- a/unittests/clangd/IndexTests.cpp +++ b/unittests/clangd/IndexTests.cpp @@ -7,21 +7,36 @@ // //===----------------------------------------------------------------------===// +#include "Annotations.h" #include "TestIndex.h" +#include "TestTU.h" +#include "gmock/gmock.h" +#include "index/FileIndex.h" #include "index/Index.h" #include "index/MemIndex.h" #include "index/Merge.h" -#include "gmock/gmock.h" #include "gtest/gtest.h" using testing::Pointee; using testing::UnorderedElementsAre; +using testing::AllOf; namespace clang { namespace clangd { namespace { +std::shared_ptr emptyOccurrences() { + return llvm::make_unique(); +} + MATCHER_P(Named, N, "") { return arg.Name == N; } +MATCHER_P(OccurrenceRange, Range, "") { + return std::tie(arg.Location.Start.Line, arg.Location.Start.Column, + arg.Location.End.Line, arg.Location.End.Column) == + std::tie(Range.start.line, Range.start.character, Range.end.line, + Range.end.character); +} +MATCHER_P(FileURI, F, "") { return arg.Location.FileURI == F; } TEST(SymbolSlab, FindAndIterate) { SymbolSlab::Builder B; @@ -42,14 +57,14 @@ TEST(SymbolSlab, FindAndIterate) { TEST(MemIndexTest, MemIndexSymbolsRecycled) { MemIndex I; std::weak_ptr Symbols; - I.build(generateNumSymbols(0, 10, &Symbols)); + I.build(generateNumSymbols(0, 10, &Symbols), emptyOccurrences()); FuzzyFindRequest Req; Req.Query = "7"; EXPECT_THAT(match(I, Req), UnorderedElementsAre("7")); EXPECT_FALSE(Symbols.expired()); // Release old symbols. - I.build(generateNumSymbols(0, 0)); + I.build(generateNumSymbols(0, 0), emptyOccurrences()); EXPECT_TRUE(Symbols.expired()); } @@ -65,14 +80,14 @@ TEST(MemIndexTest, MemIndexDeduplicate) { FuzzyFindRequest Req; Req.Query = "7"; MemIndex I; - I.build(std::move(Symbols)); + I.build(std::move(Symbols), emptyOccurrences()); auto Matches = match(I, Req); EXPECT_EQ(Matches.size(), 1u); } TEST(MemIndexTest, MemIndexLimitedNumMatches) { MemIndex I; - I.build(generateNumSymbols(0, 100)); + I.build(generateNumSymbols(0, 100), emptyOccurrences()); FuzzyFindRequest Req; Req.Query = "5"; Req.MaxCandidateCount = 3; @@ -85,7 +100,8 @@ TEST(MemIndexTest, MemIndexLimitedNumMatches) { TEST(MemIndexTest, FuzzyMatch) { MemIndex I; I.build( - generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"})); + generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}), + emptyOccurrences()); FuzzyFindRequest Req; Req.Query = "lol"; Req.MaxCandidateCount = 2; @@ -95,7 +111,7 @@ TEST(MemIndexTest, FuzzyMatch) { TEST(MemIndexTest, MatchQualifiedNamesWithoutSpecificScope) { MemIndex I; - I.build(generateSymbols({"a::y1", "b::y2", "y3"})); + I.build(generateSymbols({"a::y1", "b::y2", "y3"}), emptyOccurrences()); FuzzyFindRequest Req; Req.Query = "y"; EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3")); @@ -103,7 +119,7 @@ TEST(MemIndexTest, MatchQualifiedNamesWithoutSpecificScope) { TEST(MemIndexTest, MatchQualifiedNamesWithGlobalScope) { MemIndex I; - I.build(generateSymbols({"a::y1", "b::y2", "y3"})); + I.build(generateSymbols({"a::y1", "b::y2", "y3"}), emptyOccurrences()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {""}; @@ -112,7 +128,8 @@ TEST(MemIndexTest, MatchQualifiedNamesWithGlobalScope) { TEST(MemIndexTest, MatchQualifiedNamesWithOneScope) { MemIndex I; - I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"})); + I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}), + emptyOccurrences()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; @@ -121,7 +138,8 @@ TEST(MemIndexTest, MatchQualifiedNamesWithOneScope) { TEST(MemIndexTest, MatchQualifiedNamesWithMultipleScopes) { MemIndex I; - I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"})); + I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}), + emptyOccurrences()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::", "b::"}; @@ -130,7 +148,7 @@ TEST(MemIndexTest, MatchQualifiedNamesWithMultipleScopes) { TEST(MemIndexTest, NoMatchNestedScopes) { MemIndex I; - I.build(generateSymbols({"a::y1", "a::b::y2"})); + I.build(generateSymbols({"a::y1", "a::b::y2"}), emptyOccurrences()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; @@ -139,7 +157,7 @@ TEST(MemIndexTest, NoMatchNestedScopes) { TEST(MemIndexTest, IgnoreCases) { MemIndex I; - I.build(generateSymbols({"ns::ABC", "ns::abc"})); + I.build(generateSymbols({"ns::ABC", "ns::abc"}), emptyOccurrences()); FuzzyFindRequest Req; Req.Query = "AB"; Req.Scopes = {"ns::"}; @@ -148,7 +166,7 @@ TEST(MemIndexTest, IgnoreCases) { TEST(MemIndexTest, Lookup) { MemIndex I; - I.build(generateSymbols({"ns::abc", "ns::xyz"})); + I.build(generateSymbols({"ns::abc", "ns::xyz"}), emptyOccurrences()); EXPECT_THAT(lookup(I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); EXPECT_THAT(lookup(I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::abc", "ns::xyz")); @@ -159,8 +177,8 @@ TEST(MemIndexTest, Lookup) { TEST(MergeIndexTest, Lookup) { MemIndex I, J; - I.build(generateSymbols({"ns::A", "ns::B"})); - J.build(generateSymbols({"ns::B", "ns::C"})); + I.build(generateSymbols({"ns::A", "ns::B"}), emptyOccurrences()); + J.build(generateSymbols({"ns::B", "ns::C"}), emptyOccurrences()); EXPECT_THAT(lookup(*mergeIndex(&I, &J), SymbolID("ns::A")), UnorderedElementsAre("ns::A")); EXPECT_THAT(lookup(*mergeIndex(&I, &J), SymbolID("ns::B")), @@ -180,8 +198,8 @@ TEST(MergeIndexTest, Lookup) { TEST(MergeIndexTest, FuzzyFind) { MemIndex I, J; - I.build(generateSymbols({"ns::A", "ns::B"})); - J.build(generateSymbols({"ns::B", "ns::C"})); + I.build(generateSymbols({"ns::A", "ns::B"}), emptyOccurrences()); + J.build(generateSymbols({"ns::B", "ns::C"}), emptyOccurrences()); FuzzyFindRequest Req; Req.Scopes = {"ns::"}; EXPECT_THAT(match(*mergeIndex(&I, &J), Req), @@ -234,6 +252,60 @@ TEST(MergeTest, PreferSymbolWithDefn) { EXPECT_EQ(M.Name, "right"); } +TEST(MergeIndexTest, FindOccurrences) { + FileIndex Dyn({"unittest"}); + FileIndex StaticIndex({"unittest"}); + auto MergedIndex = mergeIndex(&Dyn, &StaticIndex); + + const char *HeaderCode = "class Foo;"; + auto HeaderSymbols = TestTU::withHeaderCode("class Foo;").headerSymbols(); + auto Foo = findSymbol(HeaderSymbols, "Foo"); + + // Build dynamic index for test.cc. + Annotations Test1Code(R"(class $Foo[[Foo]];)"); + TestTU Test; + Test.HeaderCode = HeaderCode; + Test.Code = Test1Code.code(); + Test.Filename = "test.cc"; + auto AST = Test.build(); + Dyn.update(Test.Filename, &AST.getASTContext(), AST.getPreprocessorPtr(), + AST.getLocalTopLevelDecls()); + + // Build static index for test.cc. + Test.HeaderCode = HeaderCode; + Test.Code = "// static\nclass Foo {};"; + Test.Filename = "test.cc"; + auto StaticAST = Test.build(); + // Add stale occurrences for test.cc. + StaticIndex.update(Test.Filename, &StaticAST.getASTContext(), + StaticAST.getPreprocessorPtr(), + StaticAST.getLocalTopLevelDecls()); + + // Add occcurrences for test2.cc + Annotations Test2Code(R"(class $Foo[[Foo]] {};)"); + TestTU Test2; + Test2.HeaderCode = HeaderCode; + Test2.Code = Test2Code.code(); + Test2.Filename = "test2.cc"; + StaticAST = Test2.build(); + StaticIndex.update(Test2.Filename, &StaticAST.getASTContext(), + StaticAST.getPreprocessorPtr(), + StaticAST.getLocalTopLevelDecls()); + + OccurrencesRequest Request; + Request.IDs = {Foo.ID}; + Request.Filter = AllOccurrenceKinds; + std::vector Results; + MergedIndex->findOccurrences( + Request, [&](const SymbolOccurrence &O) { Results.push_back(O); }); + + EXPECT_THAT(Results, UnorderedElementsAre( + AllOf(OccurrenceRange(Test1Code.range("Foo")), + FileURI("unittest:///test.cc")), + AllOf(OccurrenceRange(Test2Code.range("Foo")), + FileURI("unittest:///test2.cc")))); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/unittests/clangd/TestTU.cpp b/unittests/clangd/TestTU.cpp index c2b97a9a0..9fd38a816 100644 --- a/unittests/clangd/TestTU.cpp +++ b/unittests/clangd/TestTU.cpp @@ -45,11 +45,12 @@ ParsedAST TestTU::build() const { SymbolSlab TestTU::headerSymbols() const { auto AST = build(); - return indexAST(AST.getASTContext(), AST.getPreprocessorPtr()); + return indexAST(AST.getASTContext(), AST.getPreprocessorPtr()).first; } std::unique_ptr TestTU::index() const { - return MemIndex::build(headerSymbols()); + // FIXME: we should generate proper occurrences for TestTU. + return MemIndex::build(headerSymbols(), SymbolOccurrenceSlab::createEmpty()); } const Symbol &findSymbol(const SymbolSlab &Slab, llvm::StringRef QName) { From a63e7914f7d120c2e926b04ac238c8f833f58675 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sat, 1 Sep 2018 07:47:03 +0000 Subject: [PATCH 120/686] [clangd] Fix many typos. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341273 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/Logger.h | 2 +- clangd/index/FileIndex.h | 4 ++-- clangd/index/Index.cpp | 2 +- clangd/index/Index.h | 2 +- clangd/index/MemIndex.cpp | 2 +- clangd/index/Merge.cpp | 2 +- clangd/index/Merge.h | 2 +- clangd/index/SymbolCollector.h | 2 +- clangd/index/dex/Trigram.h | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clangd/Logger.h b/clangd/Logger.h index cc6e3a0a7..0c8203c03 100644 --- a/clangd/Logger.h +++ b/clangd/Logger.h @@ -56,7 +56,7 @@ void log(Logger::Level L, const char *Fmt, Ts &&... Vals) { template void elog(const char *Fmt, Ts &&... Vals) { detail::log(Logger::Error, Fmt, std::forward(Vals)...); } -// log() is used for information important to understanding a clangd session. +// log() is used for information important to understand a clangd session. // e.g. the names of LSP messages sent are logged at this level. // This level could be enabled in production builds to allow later inspection. template void log(const char *Fmt, Ts &&... Vals) { diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index 079447288..a19d73d35 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -36,7 +36,7 @@ namespace clangd { /// the snapshot, either this class or the symbol index. /// /// The snapshot semantics keeps critical sections minimal since we only need -/// locking when we swap or obtain refereces to snapshots. +/// locking when we swap or obtain references to snapshots. class FileSymbols { public: /// \brief Updates all symbols and occurrences in a file. @@ -60,7 +60,7 @@ class FileSymbols { llvm::StringMap> FileToOccurrenceSlabs; }; -/// \brief This manages symbls from files and an in-memory index on all symbols. +/// \brief This manages symbols from files and an in-memory index on all symbols. class FileIndex : public SymbolIndex { public: /// If URISchemes is empty, the default schemes in SymbolCollector will be diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index 3a53ce42a..20e4b44cc 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -152,7 +152,7 @@ void SymbolOccurrenceSlab::insert(const SymbolID &SymID, } void SymbolOccurrenceSlab::freeze() { - // Deduplicate symbol occurrenes. + // Deduplicate symbol occurrences. for (auto &IDAndOccurrence : Occurrences) { auto &Occurrence = IDAndOccurrence.getSecond(); std::sort(Occurrence.begin(), Occurrence.end()); diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 410919bed..63e87a23d 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -456,7 +456,7 @@ class SymbolIndex { /// CrossReference finds all symbol occurrences (e.g. references, /// declarations, definitions) and applies \p Callback on each result. /// - /// Resutls are returned in arbitrary order. + /// Results are returned in arbitrary order. /// /// The returned result must be deep-copied if it's used outside Callback. virtual void findOccurrences( diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index 8d314d1fe..c73c09a40 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -44,7 +44,7 @@ void MemIndex::build(std::shared_ptr> Syms, { std::lock_guard Lock(Mutex); Index = std::move(TempIndex); - Symbols = std::move(Syms); // Relase old symbols. + Symbols = std::move(Syms); // Release old symbols. Occurrences = std::move(AllOccurrences); } diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index 27a478544..1bca61e79 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -87,7 +87,7 @@ class MergedIndex : public SymbolIndex { // We consider the dynamic index authoritative and report all its // occurrences, and only report static index occurrences from other files. // - // FIXME: The heuristic fails if the dynamic index containts a file, but all + // FIXME: The heuristic fails if the dynamic index contains a file, but all // occurrences were removed (we will report stale ones from the static // index). Ultimately we should explicit check which index has the file // instead. diff --git a/clangd/index/Merge.h b/clangd/index/Merge.h index 38a688704..acf9ce1d2 100644 --- a/clangd/index/Merge.h +++ b/clangd/index/Merge.h @@ -20,7 +20,7 @@ namespace clangd { // Returned symbol may contain data owned by either source. Symbol mergeSymbol(const Symbol &L, const Symbol &R); -// mergedIndex returns a composite index based on two provided Indexes: +// mergeIndex returns a composite index based on two provided Indexes: // - the Dynamic index covers few files, but is relatively up-to-date. // - the Static index covers a bigger set of files, but is relatively stale. // The returned index attempts to combine results, and avoid duplicates. diff --git a/clangd/index/SymbolCollector.h b/clangd/index/SymbolCollector.h index fb98b683e..ab88c066d 100644 --- a/clangd/index/SymbolCollector.h +++ b/clangd/index/SymbolCollector.h @@ -104,7 +104,7 @@ class SymbolCollector : public index::IndexDataConsumer { // All Symbols collected from the AST. SymbolSlab::Builder Symbols; // All symbol occurrences collected from the AST. - // Only symbols declared in preamble (from #inclues) and references from the + // Only symbols declared in preamble (from #include) and references from the // main file will be included. SymbolOccurrenceSlab SymbolOccurrences; ASTContext *ASTCtx; diff --git a/clangd/index/dex/Trigram.h b/clangd/index/dex/Trigram.h index a3fc6b32b..665b0dae7 100644 --- a/clangd/index/dex/Trigram.h +++ b/clangd/index/dex/Trigram.h @@ -62,7 +62,7 @@ std::vector generateIdentifierTrigrams(llvm::StringRef Identifier); /// /// For short queries (less than 3 characters with Head or Tail roles in Fuzzy /// Matching segmentation) this returns a single trigram with the first -/// characters (up to 3) to perfrom prefix match. +/// characters (up to 3) to perform prefix match. std::vector generateQueryTrigrams(llvm::StringRef Query); } // namespace dex From edb9ae249a7bd0869d49158c64f86c1c5796f988 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Mon, 3 Sep 2018 10:18:21 +0000 Subject: [PATCH 121/686] [clangd] Support multiple #include headers in one symbol. Summary: Currently, a symbol can have only one #include header attached, which might not work well if the symbol can be imported via different #includes depending on where it's used. This patch stores multiple #include headers (with # references) for each symbol, so that CodeCompletion can decide which include to insert. In this patch, code completion simply picks the most popular include as the default inserted header. We also return all possible includes and their edits in the `CodeCompletion` results. Reviewers: sammccall Reviewed By: sammccall Subscribers: mgrang, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51291 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341304 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 103 +++++++++++++++------- clangd/CodeComplete.h | 21 +++-- clangd/index/Index.cpp | 5 +- clangd/index/Index.h | 35 ++++++-- clangd/index/Merge.cpp | 18 +++- clangd/index/SymbolCollector.cpp | 7 +- clangd/index/SymbolYAML.cpp | 13 ++- unittests/clangd/CodeCompleteTests.cpp | 59 +++++++++++-- unittests/clangd/FileIndexTests.cpp | 2 +- unittests/clangd/IndexTests.cpp | 40 +++++++++ unittests/clangd/SymbolCollectorTests.cpp | 24 ++++- 11 files changed, 262 insertions(+), 65 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 6cda2f0b9..617046b69 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -44,10 +44,13 @@ #include "clang/Sema/Sema.h" #include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/ScopedPrinter.h" +#include +#include #include // We log detailed candidate here if you run with -debug-only=codecomplete. @@ -247,6 +250,7 @@ struct CompletionCandidate { // We may have a result from Sema, from the index, or both. const CodeCompletionResult *SemaResult = nullptr; const Symbol *IndexResult = nullptr; + llvm::SmallVector RankedIncludeHeaders; // States whether this item is an override suggestion. bool IsOverride = false; @@ -267,7 +271,7 @@ struct CompletionCandidate { // This could break #include insertion. return hash_combine( (IndexResult->Scope + IndexResult->Name).toStringRef(Scratch), - headerToInsertIfNotPresent().getValueOr("")); + headerToInsertIfAllowed().getValueOr("")); default: return 0; } @@ -281,11 +285,12 @@ struct CompletionCandidate { llvm::raw_svector_ostream OS(Scratch); D->printQualifiedName(OS); } - return hash_combine(Scratch, headerToInsertIfNotPresent().getValueOr("")); + return hash_combine(Scratch, headerToInsertIfAllowed().getValueOr("")); } - llvm::Optional headerToInsertIfNotPresent() const { - if (!IndexResult || IndexResult->IncludeHeader.empty()) + // The best header to include if include insertion is allowed. + llvm::Optional headerToInsertIfAllowed() const { + if (RankedIncludeHeaders.empty()) return llvm::None; if (SemaResult && SemaResult->Declaration) { // Avoid inserting new #include if the declaration is found in the current @@ -295,7 +300,7 @@ struct CompletionCandidate { if (SM.isInMainFile(SM.getExpansionLoc(RD->getBeginLoc()))) return llvm::None; } - return IndexResult->IncludeHeader; + return RankedIncludeHeaders[0]; } using Bundle = llvm::SmallVector; @@ -358,31 +363,41 @@ struct CodeCompletionBuilder { if (Completion.Name.empty()) Completion.Name = C.IndexResult->Name; } - if (auto Inserted = C.headerToInsertIfNotPresent()) { - // Turn absolute path into a literal string that can be #included. - auto Include = [&]() -> Expected> { - auto ResolvedDeclaring = - toHeaderFile(C.IndexResult->CanonicalDeclaration.FileURI, FileName); - if (!ResolvedDeclaring) - return ResolvedDeclaring.takeError(); - auto ResolvedInserted = toHeaderFile(*Inserted, FileName); - if (!ResolvedInserted) - return ResolvedInserted.takeError(); - return std::make_pair(Includes.calculateIncludePath(*ResolvedDeclaring, - *ResolvedInserted), - Includes.shouldInsertInclude(*ResolvedDeclaring, - *ResolvedInserted)); - }(); - if (Include) { - Completion.Header = Include->first; - if (Include->second) - Completion.HeaderInsertion = Includes.insert(Include->first); + + // Turn absolute path into a literal string that can be #included. + auto Inserted = + [&](StringRef Header) -> Expected> { + auto ResolvedDeclaring = + toHeaderFile(C.IndexResult->CanonicalDeclaration.FileURI, FileName); + if (!ResolvedDeclaring) + return ResolvedDeclaring.takeError(); + auto ResolvedInserted = toHeaderFile(Header, FileName); + if (!ResolvedInserted) + return ResolvedInserted.takeError(); + return std::make_pair( + Includes.calculateIncludePath(*ResolvedDeclaring, *ResolvedInserted), + Includes.shouldInsertInclude(*ResolvedDeclaring, *ResolvedInserted)); + }; + bool ShouldInsert = C.headerToInsertIfAllowed().hasValue(); + // Calculate include paths and edits for all possible headers. + for (const auto &Inc : C.RankedIncludeHeaders) { + if (auto ToInclude = Inserted(Inc)) { + CodeCompletion::IncludeCandidate Include; + Include.Header = ToInclude->first; + if (ToInclude->second && ShouldInsert) + Include.Insertion = Includes.insert(ToInclude->first); + Completion.Includes.push_back(std::move(Include)); } else log("Failed to generate include insertion edits for adding header " "(FileURI='{0}', IncludeHeader='{1}') into {2}", - C.IndexResult->CanonicalDeclaration.FileURI, - C.IndexResult->IncludeHeader, FileName); + C.IndexResult->CanonicalDeclaration.FileURI, Inc, FileName); } + // Prefer includes that do not need edits (i.e. already exist). + std::stable_partition(Completion.Includes.begin(), + Completion.Includes.end(), + [](const CodeCompletion::IncludeCandidate &I) { + return !I.Insertion.hasValue(); + }); } void add(const CompletionCandidate &C, CodeCompletionString *SemaCCS) { @@ -1135,6 +1150,26 @@ clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const { return Result; } +// Returns the most popular include header for \p Sym. If two headers are +// equally popular, prefer the shorter one. Returns empty string if \p Sym has +// no include header. +llvm::SmallVector +getRankedIncludes(const Symbol &Sym) { + auto Includes = Sym.IncludeHeaders; + // Sort in descending order by reference count and header length. + std::sort(Includes.begin(), Includes.end(), + [](const Symbol::IncludeHeaderWithReferences &LHS, + const Symbol::IncludeHeaderWithReferences &RHS) { + if (LHS.References == RHS.References) + return LHS.IncludeHeader.size() < RHS.IncludeHeader.size(); + return LHS.References > RHS.References; + }); + llvm::SmallVector Headers; + for (const auto &Include : Includes) + Headers.push_back(Include.IncludeHeader); + return Headers; +} + // Runs Sema-based (AST) and Index-based completion, returns merged results. // // There are a few tricky considerations: @@ -1383,6 +1418,8 @@ class CodeCompleteFlow { CompletionCandidate C; C.SemaResult = SemaResult; C.IndexResult = IndexResult; + if (C.IndexResult) + C.RankedIncludeHeaders = getRankedIncludes(*C.IndexResult); C.IsOverride = IsOverride; C.Name = IndexResult ? IndexResult->Name : Recorder->getName(*SemaResult); if (auto OverloadSet = Opts.BundleOverloads ? C.overloadSet() : 0) { @@ -1576,16 +1613,18 @@ bool isIndexedForCodeCompletion(const NamedDecl &ND, ASTContext &ASTCtx) { CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const { CompletionItem LSP; - LSP.label = (HeaderInsertion ? Opts.IncludeIndicator.Insert - : Opts.IncludeIndicator.NoInsert) + + const auto *InsertInclude = Includes.empty() ? nullptr : &Includes[0]; + LSP.label = ((InsertInclude && InsertInclude->Insertion) + ? Opts.IncludeIndicator.Insert + : Opts.IncludeIndicator.NoInsert) + (Opts.ShowOrigins ? "[" + llvm::to_string(Origin) + "]" : "") + RequiredQualifier + Name + Signature; LSP.kind = Kind; LSP.detail = BundleSize > 1 ? llvm::formatv("[{0} overloads]", BundleSize) : ReturnType; - if (!Header.empty()) - LSP.detail += "\n" + Header; + if (InsertInclude) + LSP.detail += "\n" + InsertInclude->Header; LSP.documentation = Documentation; LSP.sortText = sortText(Score.Total, Name); LSP.filterText = Name; @@ -1613,8 +1652,8 @@ CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const { LSP.insertText = LSP.textEdit->newText; LSP.insertTextFormat = Opts.EnableSnippets ? InsertTextFormat::Snippet : InsertTextFormat::PlainText; - if (HeaderInsertion) - LSP.additionalTextEdits.push_back(*HeaderInsertion); + if (InsertInclude && InsertInclude->Insertion) + LSP.additionalTextEdits.push_back(*InsertInclude->Insertion); return LSP; } diff --git a/clangd/CodeComplete.h b/clangd/CodeComplete.h index 1785fb6a6..3213acc7a 100644 --- a/clangd/CodeComplete.h +++ b/clangd/CodeComplete.h @@ -26,6 +26,7 @@ #include "clang/Sema/CodeCompleteOptions.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include @@ -131,12 +132,20 @@ struct CodeCompletion { // Other fields should apply equally to all bundled completions. unsigned BundleSize = 1; SymbolOrigin Origin = SymbolOrigin::Unknown; - // The header through which this symbol could be included. - // Quoted string as expected by an #include directive, e.g. "". - // Empty for non-symbol completions, or when not known. - std::string Header; - // Present if Header is set and should be inserted to use this item. - llvm::Optional HeaderInsertion; + + struct IncludeCandidate { + // The header through which this symbol could be included. + // Quoted string as expected by an #include directive, e.g. "". + // Empty for non-symbol completions, or when not known. + std::string Header; + // Present if Header should be inserted to use this item. + llvm::Optional Insertion; + }; + // All possible include headers ranked by preference. By default, the first + // include is used. + // If we've bundled together overloads that have different sets of includes, + // thse includes may not be accurate for all of them. + llvm::SmallVector Includes; /// Holds information about small corrections that needs to be done. Like /// converting '->' to '.' on member access. diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index 20e4b44cc..1120b22bb 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -9,6 +9,7 @@ #include "Index.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/SHA1.h" #include "llvm/Support/raw_ostream.h" @@ -90,9 +91,11 @@ static void own(Symbol &S, llvm::UniqueStringSaver &Strings, Intern(S.Signature); Intern(S.CompletionSnippetSuffix); + Intern(S.Documentation); Intern(S.ReturnType); - Intern(S.IncludeHeader); + for (auto &I : S.IncludeHeaders) + Intern(I.IncludeHeader); } void SymbolSlab::Builder::insert(const Symbol &S) { diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 63e87a23d..08f8530e0 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -16,7 +16,9 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/StringSaver.h" #include #include @@ -213,14 +215,31 @@ struct Symbol { /// Type when this symbol is used in an expression. (Short display form). /// e.g. return type of a function, or type of a variable. llvm::StringRef ReturnType; - /// This can be either a URI of the header to be #include'd for this symbol, - /// or a literal header quoted with <> or "" that is suitable to be included - /// directly. When this is a URI, the exact #include path needs to be - /// calculated according to the URI scheme. - /// - /// This is a canonical include for the symbol and can be different from - /// FileURI in the CanonicalDeclaration. - llvm::StringRef IncludeHeader; + + struct IncludeHeaderWithReferences { + IncludeHeaderWithReferences() = default; + + IncludeHeaderWithReferences(llvm::StringRef IncludeHeader, + unsigned References) + : IncludeHeader(IncludeHeader), References(References) {} + + /// This can be either a URI of the header to be #include'd + /// for this symbol, or a literal header quoted with <> or "" that is + /// suitable to be included directly. When it is a URI, the exact #include + /// path needs to be calculated according to the URI scheme. + /// + /// Note that the include header is a canonical include for the symbol and + /// can be different from FileURI in the CanonicalDeclaration. + llvm::StringRef IncludeHeader = ""; + /// The number of translation units that reference this symbol and include + /// this header. This number is only meaningful if aggregated in an index. + unsigned References = 0; + }; + /// One Symbol can potentially be incuded via different headers. + /// - If we haven't seen a definition, this covers all declarations. + /// - If we have seen a definition, this covers declarations visible from + /// any definition. + llvm::SmallVector IncludeHeaders; // FIXME: add all occurrences support. // FIXME: add extra fields for index scoring signals. diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index 1bca61e79..6c384bfd6 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -118,6 +118,10 @@ Symbol mergeSymbol(const Symbol &L, const Symbol &R) { // Classes: this is the def itself. Functions: hopefully the header decl. // If both did (or both didn't), continue to prefer L over R. bool PreferR = R.Definition && !L.Definition; + // Merge include headers only if both have definitions or both have no + // definition; otherwise, only accumulate references of common includes. + bool MergeIncludes = + L.Definition.FileURI.empty() == R.Definition.FileURI.empty(); Symbol S = PreferR ? R : L; // The target symbol we're merging into. const Symbol &O = PreferR ? L : R; // The "other" less-preferred symbol. @@ -136,8 +140,18 @@ Symbol mergeSymbol(const Symbol &L, const Symbol &R) { S.Documentation = O.Documentation; if (S.ReturnType == "") S.ReturnType = O.ReturnType; - if (S.IncludeHeader == "") - S.IncludeHeader = O.IncludeHeader; + for (const auto &OI : O.IncludeHeaders) { + bool Found = false; + for (auto &SI : S.IncludeHeaders) { + if (SI.IncludeHeader == OI.IncludeHeader) { + Found = true; + SI.References += OI.References; + break; + } + } + if (!Found && MergeIncludes) + S.IncludeHeaders.emplace_back(OI.IncludeHeader, OI.References); + } S.Origin |= O.Origin | SymbolOrigin::Merge; return S; diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp index 214522145..912c65085 100644 --- a/clangd/index/SymbolCollector.cpp +++ b/clangd/index/SymbolCollector.cpp @@ -422,7 +422,9 @@ bool SymbolCollector::handleMacroOccurence(const IdentifierInfo *Name, } S.Signature = Signature; S.CompletionSnippetSuffix = SnippetSuffix; - S.IncludeHeader = Include; + if (!Include.empty()) + S.IncludeHeaders.emplace_back(Include, 1); + Symbols.insert(S); return true; } @@ -530,7 +532,8 @@ const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, S.CompletionSnippetSuffix = SnippetSuffix; S.Documentation = Documentation; S.ReturnType = ReturnType; - S.IncludeHeader = Include; + if (!Include.empty()) + S.IncludeHeaders.emplace_back(Include, 1); S.Origin = Opts.Origin; Symbols.insert(S); diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/SymbolYAML.cpp index 6d9c84b73..d3947fa14 100644 --- a/clangd/index/SymbolYAML.cpp +++ b/clangd/index/SymbolYAML.cpp @@ -10,11 +10,13 @@ #include "SymbolYAML.h" #include "Index.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/Errc.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(clang::clangd::Symbol) +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Symbol::IncludeHeaderWithReferences) namespace llvm { namespace yaml { @@ -66,6 +68,15 @@ template <> struct MappingTraits { } }; +template <> +struct MappingTraits { + static void mapping(IO &io, + clang::clangd::Symbol::IncludeHeaderWithReferences &Inc) { + io.mapRequired("Header", Inc.IncludeHeader); + io.mapRequired("References", Inc.References); + } +}; + template <> struct MappingTraits { static void mapping(IO &IO, Symbol &Sym) { MappingNormalization NSymbolID(IO, Sym.ID); @@ -83,7 +94,7 @@ template <> struct MappingTraits { IO.mapOptional("CompletionSnippetSuffix", Sym.CompletionSnippetSuffix); IO.mapOptional("Documentation", Sym.Documentation); IO.mapOptional("ReturnType", Sym.ReturnType); - IO.mapOptional("IncludeHeader", Sym.IncludeHeader); + IO.mapOptional("IncludeHeaders", Sym.IncludeHeaders); } }; diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 284149a5e..80e5f9143 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -55,10 +55,16 @@ MATCHER_P(SigHelpLabeled, Label, "") { return arg.label == Label; } MATCHER_P(Kind, K, "") { return arg.Kind == K; } MATCHER_P(Doc, D, "") { return arg.Documentation == D; } MATCHER_P(ReturnType, D, "") { return arg.ReturnType == D; } +MATCHER_P(HasInclude, IncludeHeader, "") { + return !arg.Includes.empty() && arg.Includes[0].Header == IncludeHeader; +} MATCHER_P(InsertInclude, IncludeHeader, "") { - return arg.Header == IncludeHeader && bool(arg.HeaderInsertion); + return !arg.Includes.empty() && arg.Includes[0].Header == IncludeHeader && + bool(arg.Includes[0].Insertion); +} +MATCHER(InsertInclude, "") { + return !arg.Includes.empty() && bool(arg.Includes[0].Insertion); } -MATCHER(InsertInclude, "") { return bool(arg.HeaderInsertion); } MATCHER_P(SnippetSuffix, Text, "") { return arg.SnippetSuffix == Text; } MATCHER_P(Origin, OriginSet, "") { return arg.Origin == OriginSet; } @@ -568,7 +574,7 @@ TEST(CompletionTest, IncludeInsertionPreprocessorIntegrationTests) { auto BarURI = URI::createFile(BarHeader).toString(); Symbol Sym = cls("ns::X"); Sym.CanonicalDeclaration.FileURI = BarURI; - Sym.IncludeHeader = BarURI; + Sym.IncludeHeaders.emplace_back(BarURI, 1); // Shoten include path based on search dirctory and insert. auto Results = completions(Server, R"cpp( @@ -600,8 +606,8 @@ TEST(CompletionTest, NoIncludeInsertionWhenDeclFoundInFile) { auto BarURI = URI::createFile(BarHeader).toString(); SymX.CanonicalDeclaration.FileURI = BarURI; SymY.CanonicalDeclaration.FileURI = BarURI; - SymX.IncludeHeader = ""; - SymY.IncludeHeader = ""; + SymX.IncludeHeaders.emplace_back("", 1); + SymY.IncludeHeaders.emplace_back("", 1); // Shoten include path based on search dirctory and insert. auto Results = completions(Server, R"cpp( @@ -1178,7 +1184,7 @@ TEST(CompletionTest, OverloadBundling) { // Differences in header-to-insert suppress bundling. std::string DeclFile = URI::createFile(testPath("foo")).toString(); NoArgsGFunc.CanonicalDeclaration.FileURI = DeclFile; - NoArgsGFunc.IncludeHeader = ""; + NoArgsGFunc.IncludeHeaders.emplace_back("", 1); EXPECT_THAT( completions(Context + "int y = GFunc^", {NoArgsGFunc}, Opts).Completions, UnorderedElementsAre(AllOf(Named("GFuncC"), InsertInclude("")), @@ -1345,7 +1351,9 @@ TEST(CompletionTest, Render) { C.RequiredQualifier = "Foo::"; C.Scope = "ns::Foo::"; C.Documentation = "This is x()."; - C.Header = "\"foo.h\""; + C.Includes.emplace_back(); + auto &Include = C.Includes.back(); + Include.Header = "\"foo.h\""; C.Kind = CompletionItemKind::Method; C.Score.Total = 1.0; C.Origin = SymbolOrigin::AST | SymbolOrigin::Static; @@ -1370,7 +1378,7 @@ TEST(CompletionTest, Render) { EXPECT_EQ(R.insertText, "Foo::x(${0:bool})"); EXPECT_EQ(R.insertTextFormat, InsertTextFormat::Snippet); - C.HeaderInsertion.emplace(); + Include.Insertion.emplace(); R = C.render(Opts); EXPECT_EQ(R.label, "^Foo::x(bool) const"); EXPECT_THAT(R.additionalTextEdits, Not(IsEmpty())); @@ -1826,6 +1834,41 @@ TEST(CompletionTest, EnableSpeculativeIndexRequest) { ASSERT_EQ(Reqs3.size(), 2u); } +TEST(CompletionTest, InsertTheMostPopularHeader) { + std::string DeclFile = URI::createFile(testPath("foo")).toString(); + Symbol sym = func("Func"); + sym.CanonicalDeclaration.FileURI = DeclFile; + sym.IncludeHeaders.emplace_back("\"foo.h\"", 2); + sym.IncludeHeaders.emplace_back("\"bar.h\"", 1000); + + auto Results = completions("Fun^", {sym}).Completions; + assert(!Results.empty()); + EXPECT_THAT(Results[0], AllOf(Named("Func"), InsertInclude("\"bar.h\""))); + EXPECT_EQ(Results[0].Includes.size(), 2u); +} + +TEST(CompletionTest, NoInsertIncludeIfOnePresent) { + MockFSProvider FS; + MockCompilationDatabase CDB; + + std::string FooHeader = testPath("foo.h"); + FS.Files[FooHeader] = ""; + + IgnoreDiagnostics DiagConsumer; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + std::string DeclFile = URI::createFile(testPath("foo")).toString(); + Symbol sym = func("Func"); + sym.CanonicalDeclaration.FileURI = DeclFile; + sym.IncludeHeaders.emplace_back("\"foo.h\"", 2); + sym.IncludeHeaders.emplace_back("\"bar.h\"", 1000); + + EXPECT_THAT( + completions(Server, "#include \"foo.h\"\nFun^", {sym}).Completions, + UnorderedElementsAre( + AllOf(Named("Func"), HasInclude("\"foo.h\""), Not(InsertInclude())))); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index 727240d74..d18f18096 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -223,7 +223,7 @@ TEST(FileIndexTest, NoIncludeCollected) { Req.Query = ""; bool SeenSymbol = false; M.fuzzyFind(Req, [&](const Symbol &Sym) { - EXPECT_TRUE(Sym.IncludeHeader.empty()); + EXPECT_TRUE(Sym.IncludeHeaders.empty()); SeenSymbol = true; }); EXPECT_TRUE(SeenSymbol); diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp index 1ac710340..7fd39e60e 100644 --- a/unittests/clangd/IndexTests.cpp +++ b/unittests/clangd/IndexTests.cpp @@ -306,6 +306,46 @@ TEST(MergeIndexTest, FindOccurrences) { FileURI("unittest:///test2.cc")))); } +MATCHER_P2(IncludeHeaderWithRef, IncludeHeader, References, "") { + return (arg.IncludeHeader == IncludeHeader) && (arg.References == References); +} + +TEST(MergeTest, MergeIncludesOnDifferentDefinitions) { + Symbol L, R; + L.Name = "left"; + R.Name = "right"; + L.ID = R.ID = SymbolID("hello"); + L.IncludeHeaders.emplace_back("common", 1); + R.IncludeHeaders.emplace_back("common", 1); + R.IncludeHeaders.emplace_back("new", 1); + + // Both have no definition. + Symbol M = mergeSymbol(L, R); + EXPECT_THAT(M.IncludeHeaders, + UnorderedElementsAre(IncludeHeaderWithRef("common", 2u), + IncludeHeaderWithRef("new", 1u))); + + // Only merge references of the same includes but do not merge new #includes. + L.Definition.FileURI = "file:/left.h"; + M = mergeSymbol(L, R); + EXPECT_THAT(M.IncludeHeaders, + UnorderedElementsAre(IncludeHeaderWithRef("common", 2u))); + + // Definitions are the same. + R.Definition.FileURI = "file:/right.h"; + M = mergeSymbol(L, R); + EXPECT_THAT(M.IncludeHeaders, + UnorderedElementsAre(IncludeHeaderWithRef("common", 2u), + IncludeHeaderWithRef("new", 1u))); + + // Definitions are different. + R.Definition.FileURI = "file:/right.h"; + M = mergeSymbol(L, R); + EXPECT_THAT(M.IncludeHeaders, + UnorderedElementsAre(IncludeHeaderWithRef("common", 2u), + IncludeHeaderWithRef("new", 1u))); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/unittests/clangd/SymbolCollectorTests.cpp b/unittests/clangd/SymbolCollectorTests.cpp index 5769a2668..b9efa5ba4 100644 --- a/unittests/clangd/SymbolCollectorTests.cpp +++ b/unittests/clangd/SymbolCollectorTests.cpp @@ -54,7 +54,13 @@ MATCHER_P(Snippet, S, "") { MATCHER_P(QName, Name, "") { return (arg.Scope + arg.Name).str() == Name; } MATCHER_P(DeclURI, P, "") { return arg.CanonicalDeclaration.FileURI == P; } MATCHER_P(DefURI, P, "") { return arg.Definition.FileURI == P; } -MATCHER_P(IncludeHeader, P, "") { return arg.IncludeHeader == P; } +MATCHER_P(IncludeHeader, P, "") { + return (arg.IncludeHeaders.size() == 1) && + (arg.IncludeHeaders.begin()->IncludeHeader == P); +} +MATCHER_P2(IncludeHeaderWithRef, IncludeHeader, References, "") { + return (arg.IncludeHeader == IncludeHeader) && (arg.References == References); +} MATCHER_P(DeclRange, Pos, "") { return std::tie(arg.CanonicalDeclaration.Start.Line, arg.CanonicalDeclaration.Start.Column, @@ -760,6 +766,11 @@ Scope: 'clang::' IsIndexedForCodeCompletion: true Documentation: 'Foo doc' ReturnType: 'int' +IncludeHeaders: + - Header: 'include1' + References: 7 + - Header: 'include2' + References: 3 ... )"; const std::string YAML2 = R"( @@ -791,6 +802,10 @@ CompletionSnippetSuffix: '-snippet' Doc("Foo doc"), ReturnType("int"), DeclURI("file:///path/foo.h"), ForCodeCompletion(true)))); + auto &Sym1 = *Symbols1.begin(); + EXPECT_THAT(Sym1.IncludeHeaders, + UnorderedElementsAre(IncludeHeaderWithRef("include1", 7u), + IncludeHeaderWithRef("include2", 3u))); auto Symbols2 = symbolsFromYAML(YAML2); EXPECT_THAT(Symbols2, UnorderedElementsAre(AllOf( QName("clang::Foo2"), Labeled("Foo2-sig"), @@ -812,9 +827,10 @@ CompletionSnippetSuffix: '-snippet' TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) { CollectorOpts.CollectIncludePath = true; runSymbolCollector("class Foo {};", /*Main=*/""); - EXPECT_THAT(Symbols, - UnorderedElementsAre(AllOf(QName("Foo"), DeclURI(TestHeaderURI), - IncludeHeader(TestHeaderURI)))); + EXPECT_THAT(Symbols, UnorderedElementsAre( + AllOf(QName("Foo"), DeclURI(TestHeaderURI)))); + EXPECT_THAT(Symbols.begin()->IncludeHeaders, + UnorderedElementsAre(IncludeHeaderWithRef(TestHeaderURI, 1u))); } #ifndef _WIN32 From 0b3a9bd4e7b0041b5ade637fcc876bb42a93ca06 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Mon, 3 Sep 2018 14:37:43 +0000 Subject: [PATCH 122/686] [clangd] Factor out the data-swapping functionality from MemIndex/DexIndex. Summary: This is now handled by a wrapper class SwapIndex, so MemIndex/DexIndex can be immutable and focus on their job. Old and busted: I have a MemIndex, which holds a shared_ptr>, which keeps the symbol slab alive. I update by calling build(shared_ptr>). New hotness: I have a SwapIndex, which holds a unique_ptr, which holds a MemIndex, which holds a shared_ptr, which keeps backing data alive. I update by building a new MemIndex and calling SwapIndex::reset(). Reviewers: kbobyrev, ioeric Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, mgrang, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51422 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341318 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/FileIndex.cpp | 91 +++++----------- clangd/index/FileIndex.h | 40 +++---- clangd/index/Index.cpp | 31 ++++++ clangd/index/Index.h | 31 +++++- clangd/index/MemIndex.cpp | 103 +++++------------- clangd/index/MemIndex.h | 45 ++++---- clangd/index/dex/DexIndex.cpp | 156 ++++++++++++---------------- clangd/index/dex/DexIndex.h | 35 ++++--- unittests/clangd/DexIndexTests.cpp | 116 ++++++++------------- unittests/clangd/FileIndexTests.cpp | 51 +++++---- unittests/clangd/IndexTests.cpp | 150 ++++++++++++-------------- unittests/clangd/TestIndex.cpp | 20 +--- unittests/clangd/TestIndex.h | 23 +--- 13 files changed, 375 insertions(+), 517 deletions(-) diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 883faf9f3..aaf97bd43 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -71,7 +71,9 @@ indexAST(ASTContext &AST, std::shared_ptr PP, } FileIndex::FileIndex(std::vector URISchemes) - : URISchemes(std::move(URISchemes)) {} + : URISchemes(std::move(URISchemes)) { + reset(FSymbols.buildMemIndex()); +} void FileSymbols::update(PathRef Path, std::unique_ptr Slab, std::unique_ptr Occurrences) { @@ -86,52 +88,32 @@ void FileSymbols::update(PathRef Path, std::unique_ptr Slab, FileToOccurrenceSlabs[Path] = std::move(Occurrences); } -std::shared_ptr> FileSymbols::allSymbols() { - // The snapshot manages life time of symbol slabs and provides pointers of all - // symbols in all slabs. - struct Snapshot { - std::vector Pointers; - std::vector> KeepAlive; - }; - auto Snap = std::make_shared(); +std::unique_ptr FileSymbols::buildMemIndex() { + std::vector> Slabs; + std::vector> OccurrenceSlabs; { std::lock_guard Lock(Mutex); - - for (const auto &FileAndSlab : FileToSlabs) { - Snap->KeepAlive.push_back(FileAndSlab.second); - for (const auto &Iter : *FileAndSlab.second) - Snap->Pointers.push_back(&Iter); - } + for (const auto &FileAndSlab : FileToSlabs) + Slabs.push_back(FileAndSlab.second); + for (const auto &FileAndOccurrenceSlab : FileToOccurrenceSlabs) + OccurrenceSlabs.push_back(FileAndOccurrenceSlab.second); } - auto *Pointers = &Snap->Pointers; - // Use aliasing constructor to keep the snapshot alive along with the - // pointers. - return {std::move(Snap), Pointers}; -} - -std::shared_ptr FileSymbols::allOccurrences() const { - // The snapshot manages life time of symbol occurrence slabs and provides - // pointers to all occurrences in all occurrence slabs. - struct Snapshot { - MemIndex::OccurrenceMap Occurrences; // ID => {Occurrence} - std::vector> KeepAlive; - }; - - auto Snap = std::make_shared(); - { - std::lock_guard Lock(Mutex); - - for (const auto &FileAndSlab : FileToOccurrenceSlabs) { - Snap->KeepAlive.push_back(FileAndSlab.second); - for (const auto &IDAndOccurrences : *FileAndSlab.second) { - auto &Occurrences = Snap->Occurrences[IDAndOccurrences.first]; - for (const auto &Occurrence : IDAndOccurrences.second) - Occurrences.push_back(&Occurrence); - } + std::vector AllSymbols; + for (const auto &Slab : Slabs) + for (const auto &Sym : *Slab) + AllSymbols.push_back(&Sym); + MemIndex::OccurrenceMap AllOccurrences; + for (const auto &OccurrenceSlab : OccurrenceSlabs) + for (const auto &Sym : *OccurrenceSlab) { + auto &Entry = AllOccurrences[Sym.first]; + for (const auto &Occ : Sym.second) + Entry.push_back(&Occ); } - } - return {std::move(Snap), &Snap->Occurrences}; + // Index must keep the slabs alive. + return llvm::make_unique( + llvm::make_pointee_range(AllSymbols), std::move(AllOccurrences), + std::make_pair(std::move(Slabs), std::move(OccurrenceSlabs))); } void FileIndex::update(PathRef Path, ASTContext *AST, @@ -148,30 +130,7 @@ void FileIndex::update(PathRef Path, ASTContext *AST, indexAST(*AST, PP, TopLevelDecls, URISchemes); FSymbols.update(Path, std::move(Slab), std::move(OccurrenceSlab)); } - auto Symbols = FSymbols.allSymbols(); - Index.build(std::move(Symbols), FSymbols.allOccurrences()); -} - -bool FileIndex::fuzzyFind( - const FuzzyFindRequest &Req, - llvm::function_ref Callback) const { - return Index.fuzzyFind(Req, Callback); -} - -void FileIndex::lookup( - const LookupRequest &Req, - llvm::function_ref Callback) const { - Index.lookup(Req, Callback); -} - -void FileIndex::findOccurrences( - const OccurrencesRequest &Req, - llvm::function_ref Callback) const { - Index.findOccurrences(Req, Callback); -} - -size_t FileIndex::estimateMemoryUsage() const { - return Index.estimateMemoryUsage(); + reset(FSymbols.buildMemIndex()); } } // namespace clangd diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index a19d73d35..471fa4c9e 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -24,7 +24,7 @@ namespace clang { namespace clangd { -/// \brief A container of Symbols from several source files. It can be updated +/// A container of Symbols from several source files. It can be updated /// at source-file granularity, replacing all symbols from one file with a new /// set. /// @@ -39,35 +39,31 @@ namespace clangd { /// locking when we swap or obtain references to snapshots. class FileSymbols { public: - /// \brief Updates all symbols and occurrences in a file. - /// If \p Slab (Occurrence) is nullptr, symbols (occurrences) for \p Path - /// will be removed. + /// Updates all symbols and occurrences in a file. + /// If either is nullptr, corresponding data for \p Path will be removed. void update(PathRef Path, std::unique_ptr Slab, std::unique_ptr Occurrences); - // The shared_ptr keeps the symbols alive - std::shared_ptr> allSymbols(); - - /// Returns all symbol occurrences for all active files. - std::shared_ptr allOccurrences() const; + // The index keeps the symbols alive. + std::unique_ptr buildMemIndex(); private: mutable std::mutex Mutex; - /// \brief Stores the latest snapshots for all active files. + /// Stores the latest snapshots for all active files. llvm::StringMap> FileToSlabs; /// Stores the latest occurrence slabs for all active files. llvm::StringMap> FileToOccurrenceSlabs; }; -/// \brief This manages symbols from files and an in-memory index on all symbols. -class FileIndex : public SymbolIndex { +/// This manages symbols from files and an in-memory index on all symbols. +class FileIndex : public SwapIndex { public: /// If URISchemes is empty, the default schemes in SymbolCollector will be /// used. FileIndex(std::vector URISchemes = {}); - /// \brief Update symbols in \p Path with symbols in \p AST. If \p AST is + /// Update symbols in \p Path with symbols in \p AST. If \p AST is /// nullptr, this removes all symbols in the file. /// If \p AST is not null, \p PP cannot be null and it should be the /// preprocessor that was used to build \p AST. @@ -77,23 +73,11 @@ class FileIndex : public SymbolIndex { update(PathRef Path, ASTContext *AST, std::shared_ptr PP, llvm::Optional> TopLevelDecls = llvm::None); - bool - fuzzyFind(const FuzzyFindRequest &Req, - llvm::function_ref Callback) const override; - - void lookup(const LookupRequest &Req, - llvm::function_ref Callback) const override; - - - void findOccurrences(const OccurrencesRequest &Req, - llvm::function_ref - Callback) const override; - - size_t estimateMemoryUsage() const override; - private: + // Only update() should swap the index. + using SwapIndex::reset; + FileSymbols FSymbols; - MemIndex Index; std::vector URISchemes; }; diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index 1120b22bb..eaa287ef0 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -165,5 +165,36 @@ void SymbolOccurrenceSlab::freeze() { Frozen = true; } +void SwapIndex::reset(std::unique_ptr Index) { + // Keep the old index alive, so we don't destroy it under lock (may be slow). + std::shared_ptr Pin; + { + std::lock_guard Lock(Mutex); + Pin = std::move(this->Index); + this->Index = std::move(Index); + } +} +std::shared_ptr SwapIndex::snapshot() const { + std::lock_guard Lock(Mutex); + return Index; +} + +bool SwapIndex::fuzzyFind(const FuzzyFindRequest &R, + llvm::function_ref CB) const { + return snapshot()->fuzzyFind(R, CB); +} +void SwapIndex::lookup(const LookupRequest &R, + llvm::function_ref CB) const { + return snapshot()->lookup(R, CB); +} +void SwapIndex::findOccurrences( + const OccurrencesRequest &R, + llvm::function_ref CB) const { + return snapshot()->findOccurrences(R, CB); +} +size_t SwapIndex::estimateMemoryUsage() const { + return snapshot()->estimateMemoryUsage(); +} + } // namespace clangd } // namespace clang diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 08f8530e0..771261a8a 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -21,6 +21,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/StringSaver.h" #include +#include #include #include @@ -331,6 +332,7 @@ inline SymbolOccurrenceKind operator&(SymbolOccurrenceKind A, return static_cast(static_cast(A) & static_cast(B)); } +llvm::raw_ostream &operator<<(llvm::raw_ostream &, SymbolOccurrenceKind); static const SymbolOccurrenceKind AllOccurrenceKinds = SymbolOccurrenceKind::Declaration | SymbolOccurrenceKind::Definition | SymbolOccurrenceKind::Reference; @@ -447,10 +449,10 @@ struct LookupRequest { struct OccurrencesRequest { llvm::DenseSet IDs; - SymbolOccurrenceKind Filter; + SymbolOccurrenceKind Filter = AllOccurrenceKinds; }; -/// \brief Interface for symbol indexes that can be used for searching or +/// Interface for symbol indexes that can be used for searching or /// matching symbols among a set of symbols based on names or unique IDs. class SymbolIndex { public: @@ -489,6 +491,31 @@ class SymbolIndex { virtual size_t estimateMemoryUsage() const = 0; }; +// Delegating implementation of SymbolIndex whose delegate can be swapped out. +class SwapIndex : public SymbolIndex { +public: + // If an index is not provided, reset() must be called. + SwapIndex(std::unique_ptr Index = nullptr) + : Index(std::move(Index)) {} + void reset(std::unique_ptr); + + // SymbolIndex methods delegate to the current index, which is kept alive + // until the call returns (even if reset() is called). + bool fuzzyFind(const FuzzyFindRequest &, + llvm::function_ref) const override; + void lookup(const LookupRequest &, + llvm::function_ref) const override; + void findOccurrences( + const OccurrencesRequest &, + llvm::function_ref) const override; + size_t estimateMemoryUsage() const override; + +private: + std::shared_ptr snapshot() const; + mutable std::mutex Mutex; + std::shared_ptr Index; +}; + } // namespace clangd } // namespace clang diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index c73c09a40..43629f486 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -15,49 +15,16 @@ namespace clang { namespace clangd { -static std::shared_ptr -getOccurrencesFromSlab(SymbolOccurrenceSlab OccurrencesSlab) { - struct Snapshot { - SymbolOccurrenceSlab Slab; - MemIndex::OccurrenceMap Occurrences; - }; - - auto Snap = std::make_shared(); - Snap->Slab = std::move(OccurrencesSlab); - for (const auto &IDAndOccurrences : Snap->Slab) { - auto &Occurrences = Snap->Occurrences[IDAndOccurrences.first]; - for (const auto &Occurrence : IDAndOccurrences.second) - Occurrences.push_back(&Occurrence); - } - return {std::move(Snap), &Snap->Occurrences}; -} - -void MemIndex::build(std::shared_ptr> Syms, - std::shared_ptr AllOccurrences) { - assert(Syms && "Syms must be set when build MemIndex"); - assert(AllOccurrences && "Occurrences must be set when build MemIndex"); - llvm::DenseMap TempIndex; - for (const Symbol *Sym : *Syms) - TempIndex[Sym->ID] = Sym; - - // Swap out the old symbols and index. - { - std::lock_guard Lock(Mutex); - Index = std::move(TempIndex); - Symbols = std::move(Syms); // Release old symbols. - Occurrences = std::move(AllOccurrences); - } - - vlog("Built MemIndex with estimated memory usage {0} bytes.", - estimateMemoryUsage()); -} - -std::unique_ptr MemIndex::build(SymbolSlab Symbols, +std::unique_ptr MemIndex::build(SymbolSlab Slab, SymbolOccurrenceSlab Occurrences) { - auto Idx = llvm::make_unique(); - Idx->build(getSymbolsFromSlab(std::move(Symbols)), - getOccurrencesFromSlab(std::move(Occurrences))); - return std::move(Idx); + OccurrenceMap M; + for (const auto &SymbolAndOccurrences : Occurrences) { + auto &Entry = M[SymbolAndOccurrences.first]; + for (const auto &Occurrence : SymbolAndOccurrences.second) + Entry.push_back(&Occurrence); + } + auto Data = std::make_pair(std::move(Slab), std::move(Occurrences)); + return llvm::make_unique(Data.first, std::move(M), std::move(Data)); } bool MemIndex::fuzzyFind( @@ -69,34 +36,30 @@ bool MemIndex::fuzzyFind( std::priority_queue> Top; FuzzyMatcher Filter(Req.Query); bool More = false; - { - std::lock_guard Lock(Mutex); - for (const auto Pair : Index) { - const Symbol *Sym = Pair.second; + for (const auto Pair : Index) { + const Symbol *Sym = Pair.second; - // Exact match against all possible scopes. - if (!Req.Scopes.empty() && !llvm::is_contained(Req.Scopes, Sym->Scope)) - continue; - if (Req.RestrictForCodeCompletion && !Sym->IsIndexedForCodeCompletion) - continue; + // Exact match against all possible scopes. + if (!Req.Scopes.empty() && !llvm::is_contained(Req.Scopes, Sym->Scope)) + continue; + if (Req.RestrictForCodeCompletion && !Sym->IsIndexedForCodeCompletion) + continue; - if (auto Score = Filter.match(Sym->Name)) { - Top.emplace(-*Score * quality(*Sym), Sym); - if (Top.size() > Req.MaxCandidateCount) { - More = true; - Top.pop(); - } + if (auto Score = Filter.match(Sym->Name)) { + Top.emplace(-*Score * quality(*Sym), Sym); + if (Top.size() > Req.MaxCandidateCount) { + More = true; + Top.pop(); } } - for (; !Top.empty(); Top.pop()) - Callback(*Top.top().second); } + for (; !Top.empty(); Top.pop()) + Callback(*Top.top().second); return More; } void MemIndex::lookup(const LookupRequest &Req, llvm::function_ref Callback) const { - std::lock_guard Lock(Mutex); for (const auto &ID : Req.IDs) { auto I = Index.find(ID); if (I != Index.end()) @@ -107,10 +70,9 @@ void MemIndex::lookup(const LookupRequest &Req, void MemIndex::findOccurrences( const OccurrencesRequest &Req, llvm::function_ref Callback) const { - std::lock_guard Lock(Mutex); for (const auto &ReqID : Req.IDs) { - auto FoundOccurrences = Occurrences->find(ReqID); - if (FoundOccurrences == Occurrences->end()) + auto FoundOccurrences = Occurrences.find(ReqID); + if (FoundOccurrences == Occurrences.end()) continue; for (const auto *O : FoundOccurrences->second) { if (static_cast(Req.Filter & O->Kind)) @@ -119,22 +81,7 @@ void MemIndex::findOccurrences( } } -std::shared_ptr> -getSymbolsFromSlab(SymbolSlab Slab) { - struct Snapshot { - SymbolSlab Slab; - std::vector Pointers; - }; - auto Snap = std::make_shared(); - Snap->Slab = std::move(Slab); - for (auto &Sym : Snap->Slab) - Snap->Pointers.push_back(&Sym); - return std::shared_ptr>(std::move(Snap), - &Snap->Pointers); -} - size_t MemIndex::estimateMemoryUsage() const { - std::lock_guard Lock(Mutex); return Index.getMemorySize(); } diff --git a/clangd/index/MemIndex.h b/clangd/index/MemIndex.h index 6d16afa5b..3f1dcdfed 100644 --- a/clangd/index/MemIndex.h +++ b/clangd/index/MemIndex.h @@ -16,8 +16,7 @@ namespace clang { namespace clangd { -/// \brief This implements an index for a (relatively small) set of symbols (or -/// symbol occurrences) that can be easily managed in memory. +/// MemIndex is a naive in-memory index suitable for a small set of symbols. class MemIndex : public SymbolIndex { public: /// Maps from a symbol ID to all corresponding symbol occurrences. @@ -25,23 +24,33 @@ class MemIndex : public SymbolIndex { using OccurrenceMap = llvm::DenseMap>; - /// \brief (Re-)Build index for `Symbols` and update `Occurrences`. - /// All symbol pointers and symbol occurrence pointers must remain accessible - /// as long as `Symbols` and `Occurrences` are kept alive. - void build(std::shared_ptr> Symbols, - std::shared_ptr Occurrences); + MemIndex() = default; + // All symbols and occurrences must outlive this index. + // TODO: find a better type for Occurrences here. + template + MemIndex(SymbolRange &&Symbols, OccurrenceMap Occurrences) + : Occurrences(std::move(Occurrences)) { + for (const Symbol &S : Symbols) + Index[S.ID] = &S; + } + // Symbols are owned by BackingData, Index takes ownership. + template + MemIndex(Range &&Symbols, OccurrenceMap Occurrences, Payload &&BackingData) + : MemIndex(std::forward(Symbols), std::move(Occurrences)) { + KeepAlive = std::shared_ptr( + std::make_shared(std::move(BackingData)), nullptr); + } - /// \brief Build index from a symbol slab and a symbol occurrence slab. - static std::unique_ptr build(SymbolSlab Symbols, + /// Builds an index from a slab. The index takes ownership of the data. + static std::unique_ptr build(SymbolSlab Slab, SymbolOccurrenceSlab Occurrences); bool fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref Callback) const override; - void - lookup(const LookupRequest &Req, - llvm::function_ref Callback) const override; + void lookup(const LookupRequest &Req, + llvm::function_ref Callback) const override; void findOccurrences(const OccurrencesRequest &Req, llvm::function_ref @@ -50,21 +59,13 @@ class MemIndex : public SymbolIndex { size_t estimateMemoryUsage() const override; private: - - std::shared_ptr> Symbols; // Index is a set of symbols that are deduplicated by symbol IDs. - // FIXME: build smarter index structure. llvm::DenseMap Index; // A map from symbol ID to symbol occurrences, support query by IDs. - std::shared_ptr Occurrences; - mutable std::mutex Mutex; + OccurrenceMap Occurrences; + std::shared_ptr KeepAlive; // poor man's move-only std::any }; -// Returns pointers to the symbols in given slab and bundles slab lifetime with -// returned symbol pointers so that the pointers are never invalid. -std::shared_ptr> -getSymbolsFromSlab(SymbolSlab Slab); - } // namespace clangd } // namespace clang diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/DexIndex.cpp index 292ed82f2..3275cb7bb 100644 --- a/clangd/index/dex/DexIndex.cpp +++ b/clangd/index/dex/DexIndex.cpp @@ -36,48 +36,30 @@ std::vector generateSearchTokens(const Symbol &Sym) { } // namespace -void DexIndex::build(std::shared_ptr> Syms) { - llvm::DenseMap TempLookupTable; - llvm::DenseMap TempSymbolQuality; - for (const Symbol *Sym : *Syms) { - TempLookupTable[Sym->ID] = Sym; - TempSymbolQuality[Sym] = quality(*Sym); +void DexIndex::buildIndex() { + for (const Symbol *Sym : Symbols) { + LookupTable[Sym->ID] = Sym; + SymbolQuality[Sym] = quality(*Sym); } // Symbols are sorted by symbol qualities so that items in the posting lists // are stored in the descending order of symbol quality. - std::sort(begin(*Syms), end(*Syms), + std::sort(begin(Symbols), end(Symbols), [&](const Symbol *LHS, const Symbol *RHS) { - return TempSymbolQuality[LHS] > TempSymbolQuality[RHS]; + return SymbolQuality[LHS] > SymbolQuality[RHS]; }); - llvm::DenseMap TempInvertedIndex; + // Populate TempInvertedIndex with posting lists for index symbols. - for (DocID SymbolRank = 0; SymbolRank < Syms->size(); ++SymbolRank) { - const auto *Sym = (*Syms)[SymbolRank]; + for (DocID SymbolRank = 0; SymbolRank < Symbols.size(); ++SymbolRank) { + const auto *Sym = Symbols[SymbolRank]; for (const auto &Token : generateSearchTokens(*Sym)) - TempInvertedIndex[Token].push_back(SymbolRank); - } - - { - std::lock_guard Lock(Mutex); - - // Replace outdated index with the new one. - LookupTable = std::move(TempLookupTable); - Symbols = std::move(Syms); - InvertedIndex = std::move(TempInvertedIndex); - SymbolQuality = std::move(TempSymbolQuality); + InvertedIndex[Token].push_back(SymbolRank); } vlog("Built DexIndex with estimated memory usage {0} bytes.", estimateMemoryUsage()); } -std::unique_ptr DexIndex::build(SymbolSlab Slab) { - auto Idx = llvm::make_unique(); - Idx->build(getSymbolsFromSlab(std::move(Slab))); - return std::move(Idx); -} - /// Constructs iterators over tokens extracted from the query and exhausts it /// while applying Callback to each symbol in the order of decreasing quality /// of the matched symbols. @@ -92,75 +74,69 @@ bool DexIndex::fuzzyFind( std::vector> TopLevelChildren; const auto TrigramTokens = generateIdentifierTrigrams(Req.Query); - { - std::lock_guard Lock(Mutex); - - // Generate query trigrams and construct AND iterator over all query - // trigrams. - std::vector> TrigramIterators; - for (const auto &Trigram : TrigramTokens) { - const auto It = InvertedIndex.find(Trigram); - if (It != InvertedIndex.end()) - TrigramIterators.push_back(create(It->second)); - } - if (!TrigramIterators.empty()) - TopLevelChildren.push_back(createAnd(move(TrigramIterators))); - - // Generate scope tokens for search query. - std::vector> ScopeIterators; - for (const auto &Scope : Req.Scopes) { - const auto It = InvertedIndex.find(Token(Token::Kind::Scope, Scope)); - if (It != InvertedIndex.end()) - ScopeIterators.push_back(create(It->second)); - } - // Add OR iterator for scopes if there are any Scope Iterators. - if (!ScopeIterators.empty()) - TopLevelChildren.push_back(createOr(move(ScopeIterators))); - - // Use TRUE iterator if both trigrams and scopes from the query are not - // present in the symbol index. - auto QueryIterator = TopLevelChildren.empty() - ? createTrue(Symbols->size()) - : createAnd(move(TopLevelChildren)); - // Retrieve more items than it was requested: some of the items with high - // final score might not be retrieved otherwise. - // FIXME(kbobyrev): Pre-scoring retrieval threshold should be adjusted as - // using 100x of the requested number might not be good in practice, e.g. - // when the requested number of items is small. - const unsigned ItemsToRetrieve = 100 * Req.MaxCandidateCount; - auto Root = createLimit(move(QueryIterator), ItemsToRetrieve); - // FIXME(kbobyrev): Add boosting to the query and utilize retrieved - // boosting scores. - std::vector> SymbolDocIDs = consume(*Root); - - // Retrieve top Req.MaxCandidateCount items. - std::priority_queue> Top; - for (const auto &P : SymbolDocIDs) { - const DocID SymbolDocID = P.first; - const auto *Sym = (*Symbols)[SymbolDocID]; - const llvm::Optional Score = Filter.match(Sym->Name); - if (!Score) - continue; - // Multiply score by a negative factor so that Top stores items with the - // highest actual score. - Top.emplace(-(*Score) * SymbolQuality.find(Sym)->second, Sym); - if (Top.size() > Req.MaxCandidateCount) { - More = true; - Top.pop(); - } + // Generate query trigrams and construct AND iterator over all query + // trigrams. + std::vector> TrigramIterators; + for (const auto &Trigram : TrigramTokens) { + const auto It = InvertedIndex.find(Trigram); + if (It != InvertedIndex.end()) + TrigramIterators.push_back(create(It->second)); + } + if (!TrigramIterators.empty()) + TopLevelChildren.push_back(createAnd(move(TrigramIterators))); + + // Generate scope tokens for search query. + std::vector> ScopeIterators; + for (const auto &Scope : Req.Scopes) { + const auto It = InvertedIndex.find(Token(Token::Kind::Scope, Scope)); + if (It != InvertedIndex.end()) + ScopeIterators.push_back(create(It->second)); + } + // Add OR iterator for scopes if there are any Scope Iterators. + if (!ScopeIterators.empty()) + TopLevelChildren.push_back(createOr(move(ScopeIterators))); + + // Use TRUE iterator if both trigrams and scopes from the query are not + // present in the symbol index. + auto QueryIterator = TopLevelChildren.empty() + ? createTrue(Symbols.size()) + : createAnd(move(TopLevelChildren)); + // Retrieve more items than it was requested: some of the items with high + // final score might not be retrieved otherwise. + // FIXME(kbobyrev): Pre-scoring retrieval threshold should be adjusted as + // using 100x of the requested number might not be good in practice, e.g. + // when the requested number of items is small. + const unsigned ItemsToRetrieve = 100 * Req.MaxCandidateCount; + auto Root = createLimit(move(QueryIterator), ItemsToRetrieve); + // FIXME(kbobyrev): Add boosting to the query and utilize retrieved + // boosting scores. + std::vector> SymbolDocIDs = consume(*Root); + + // Retrieve top Req.MaxCandidateCount items. + std::priority_queue> Top; + for (const auto &P : SymbolDocIDs) { + const DocID SymbolDocID = P.first; + const auto *Sym = Symbols[SymbolDocID]; + const llvm::Optional Score = Filter.match(Sym->Name); + if (!Score) + continue; + // Multiply score by a negative factor so that Top stores items with the + // highest actual score. + Top.emplace(-(*Score) * SymbolQuality.find(Sym)->second, Sym); + if (Top.size() > Req.MaxCandidateCount) { + More = true; + Top.pop(); } - - // Apply callback to the top Req.MaxCandidateCount items. - for (; !Top.empty(); Top.pop()) - Callback(*Top.top().second); } + // Apply callback to the top Req.MaxCandidateCount items. + for (; !Top.empty(); Top.pop()) + Callback(*Top.top().second); return More; } void DexIndex::lookup(const LookupRequest &Req, llvm::function_ref Callback) const { - std::lock_guard Lock(Mutex); for (const auto &ID : Req.IDs) { auto I = LookupTable.find(ID); if (I != LookupTable.end()) @@ -175,8 +151,6 @@ void DexIndex::findOccurrences( } size_t DexIndex::estimateMemoryUsage() const { - std::lock_guard Lock(Mutex); - size_t Bytes = LookupTable.size() * sizeof(std::pair); Bytes += SymbolQuality.size() * sizeof(std::pair); diff --git a/clangd/index/dex/DexIndex.h b/clangd/index/dex/DexIndex.h index 8631a234d..ad379136b 100644 --- a/clangd/index/dex/DexIndex.h +++ b/clangd/index/dex/DexIndex.h @@ -25,7 +25,6 @@ #include "Iterator.h" #include "Token.h" #include "Trigram.h" -#include namespace clang { namespace clangd { @@ -39,12 +38,24 @@ namespace dex { // index on disk and then load it if available. class DexIndex : public SymbolIndex { public: - /// \brief (Re-)Build index for `Symbols`. All symbol pointers must remain - /// accessible as long as `Symbols` is kept alive. - void build(std::shared_ptr> Syms); + // All symbols must outlive this index. + template DexIndex(Range &&Symbols) { + for (auto &&Sym : Symbols) + this->Symbols.push_back(&Sym); + buildIndex(); + } + // Symbols are owned by BackingData, Index takes ownership. + template + DexIndex(Range &&Symbols, Payload &&BackingData) + : DexIndex(std::forward(Symbols)) { + KeepAlive = std::shared_ptr( + std::make_shared(std::move(BackingData)), nullptr); + } - /// \brief Build index from a symbol slab. - static std::unique_ptr build(SymbolSlab Slab); + /// Builds an index from a slab. The index takes ownership of the slab. + static std::unique_ptr build(SymbolSlab Slab) { + return llvm::make_unique(Slab, std::move(Slab)); + } bool fuzzyFind(const FuzzyFindRequest &Req, @@ -60,19 +71,19 @@ class DexIndex : public SymbolIndex { size_t estimateMemoryUsage() const override; private: + void buildIndex(); - mutable std::mutex Mutex; - - std::shared_ptr> Symbols /*GUARDED_BY(Mutex)*/; - llvm::DenseMap LookupTable /*GUARDED_BY(Mutex)*/; - llvm::DenseMap SymbolQuality /*GUARDED_BY(Mutex)*/; + std::vector Symbols; + llvm::DenseMap LookupTable; + llvm::DenseMap SymbolQuality; // Inverted index is a mapping from the search token to the posting list, // which contains all items which can be characterized by such search token. // For example, if the search token is scope "std::", the corresponding // posting list would contain all indices of symbols defined in namespace std. // Inverted index is used to retrieve posting lists which are processed during // the fuzzyFind process. - llvm::DenseMap InvertedIndex /*GUARDED_BY(Mutex)*/; + llvm::DenseMap InvertedIndex; + std::shared_ptr KeepAlive; // poor man's move-only std::any }; } // namespace dex diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index a66df13b0..da09596ab 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -23,6 +23,7 @@ using ::testing::ElementsAre; using ::testing::UnorderedElementsAre; +using namespace llvm; namespace clang { namespace clangd { @@ -408,171 +409,140 @@ TEST(DexIndexTrigrams, QueryTrigrams) { } TEST(DexIndex, Lookup) { - DexIndex I; - I.build(generateSymbols({"ns::abc", "ns::xyz"})); - EXPECT_THAT(lookup(I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); - EXPECT_THAT(lookup(I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), + auto I = DexIndex::build(generateSymbols({"ns::abc", "ns::xyz"})); + EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); + EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::abc", "ns::xyz")); - EXPECT_THAT(lookup(I, {SymbolID("ns::nonono"), SymbolID("ns::xyz")}), + EXPECT_THAT(lookup(*I, {SymbolID("ns::nonono"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::xyz")); - EXPECT_THAT(lookup(I, SymbolID("ns::nonono")), UnorderedElementsAre()); + EXPECT_THAT(lookup(*I, SymbolID("ns::nonono")), UnorderedElementsAre()); } TEST(DexIndex, FuzzyFind) { - DexIndex Index; - Index.build(generateSymbols({"ns::ABC", "ns::BCD", "::ABC", "ns::nested::ABC", - "other::ABC", "other::A"})); + auto Index = DexIndex::build( + generateSymbols({"ns::ABC", "ns::BCD", "::ABC", "ns::nested::ABC", + "other::ABC", "other::A"})); FuzzyFindRequest Req; Req.Query = "ABC"; Req.Scopes = {"ns::"}; - EXPECT_THAT(match(Index, Req), UnorderedElementsAre("ns::ABC")); + EXPECT_THAT(match(*Index, Req), UnorderedElementsAre("ns::ABC")); Req.Scopes = {"ns::", "ns::nested::"}; - EXPECT_THAT(match(Index, Req), + EXPECT_THAT(match(*Index, Req), UnorderedElementsAre("ns::ABC", "ns::nested::ABC")); Req.Query = "A"; Req.Scopes = {"other::"}; - EXPECT_THAT(match(Index, Req), + EXPECT_THAT(match(*Index, Req), UnorderedElementsAre("other::A", "other::ABC")); Req.Query = ""; Req.Scopes = {}; - EXPECT_THAT(match(Index, Req), + EXPECT_THAT(match(*Index, Req), UnorderedElementsAre("ns::ABC", "ns::BCD", "::ABC", "ns::nested::ABC", "other::ABC", "other::A")); } TEST(DexIndexTest, FuzzyMatchQ) { - DexIndex I; - I.build( + auto I = DexIndex::build( generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"})); FuzzyFindRequest Req; Req.Query = "lol"; Req.MaxCandidateCount = 2; - EXPECT_THAT(match(I, Req), + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); } -TEST(DexIndexTest, DexIndexSymbolsRecycled) { - DexIndex I; - std::weak_ptr Symbols; - I.build(generateNumSymbols(0, 10, &Symbols)); - FuzzyFindRequest Req; - Req.Query = "7"; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("7")); - - EXPECT_FALSE(Symbols.expired()); - // Release old symbols. - I.build(generateNumSymbols(0, 0)); - EXPECT_TRUE(Symbols.expired()); -} - // FIXME(kbobyrev): This test is different for DexIndex and MemIndex: while // MemIndex manages response deduplication, DexIndex simply returns all matched // symbols which means there might be equivalent symbols in the response. // Before drop-in replacement of MemIndex with DexIndex happens, FileIndex // should handle deduplication instead. TEST(DexIndexTest, DexIndexDeduplicate) { - auto Symbols = generateNumSymbols(0, 10); - - // Inject duplicates. - auto Sym = symbol("7"); - Symbols->push_back(&Sym); - Symbols->push_back(&Sym); - Symbols->push_back(&Sym); - + std::vector Symbols = {symbol("1"), symbol("2"), symbol("3"), + symbol("2") /* duplicate */}; FuzzyFindRequest Req; - Req.Query = "7"; - DexIndex I; - I.build(std::move(Symbols)); - auto Matches = match(I, Req); - EXPECT_EQ(Matches.size(), 4u); + Req.Query = "2"; + DexIndex I(Symbols); + EXPECT_THAT(match(I, Req), ElementsAre("2", "2")); } TEST(DexIndexTest, DexIndexLimitedNumMatches) { - DexIndex I; - I.build(generateNumSymbols(0, 100)); + auto I = DexIndex::build(generateNumSymbols(0, 100)); FuzzyFindRequest Req; Req.Query = "5"; Req.MaxCandidateCount = 3; bool Incomplete; - auto Matches = match(I, Req, &Incomplete); + auto Matches = match(*I, Req, &Incomplete); EXPECT_EQ(Matches.size(), Req.MaxCandidateCount); EXPECT_TRUE(Incomplete); } TEST(DexIndexTest, FuzzyMatch) { - DexIndex I; - I.build( + auto I = DexIndex::build( generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"})); FuzzyFindRequest Req; Req.Query = "lol"; Req.MaxCandidateCount = 2; - EXPECT_THAT(match(I, Req), + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); } TEST(DexIndexTest, MatchQualifiedNamesWithoutSpecificScope) { - DexIndex I; - I.build(generateSymbols({"a::y1", "b::y2", "y3"})); + auto I = DexIndex::build(generateSymbols({"a::y1", "b::y2", "y3"})); FuzzyFindRequest Req; Req.Query = "y"; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3")); + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3")); } TEST(DexIndexTest, MatchQualifiedNamesWithGlobalScope) { - DexIndex I; - I.build(generateSymbols({"a::y1", "b::y2", "y3"})); + auto I = DexIndex::build(generateSymbols({"a::y1", "b::y2", "y3"})); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {""}; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("y3")); + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("y3")); } TEST(DexIndexTest, MatchQualifiedNamesWithOneScope) { - DexIndex I; - I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"})); + auto I = DexIndex::build( + generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"})); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1", "a::y2")); + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "a::y2")); } TEST(DexIndexTest, MatchQualifiedNamesWithMultipleScopes) { - DexIndex I; - I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"})); + auto I = DexIndex::build( + generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"})); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::", "b::"}; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1", "a::y2", "b::y3")); + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "a::y2", "b::y3")); } TEST(DexIndexTest, NoMatchNestedScopes) { - DexIndex I; - I.build(generateSymbols({"a::y1", "a::b::y2"})); + auto I = DexIndex::build(generateSymbols({"a::y1", "a::b::y2"})); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1")); + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1")); } TEST(DexIndexTest, IgnoreCases) { - DexIndex I; - I.build(generateSymbols({"ns::ABC", "ns::abc"})); + auto I = DexIndex::build(generateSymbols({"ns::ABC", "ns::abc"})); FuzzyFindRequest Req; Req.Query = "AB"; Req.Scopes = {"ns::"}; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("ns::ABC", "ns::abc")); + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("ns::ABC", "ns::abc")); } TEST(DexIndexTest, Lookup) { - DexIndex I; - I.build(generateSymbols({"ns::abc", "ns::xyz"})); - EXPECT_THAT(lookup(I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); - EXPECT_THAT(lookup(I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), + auto I = DexIndex::build(generateSymbols({"ns::abc", "ns::xyz"})); + EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); + EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::abc", "ns::xyz")); - EXPECT_THAT(lookup(I, {SymbolID("ns::nonono"), SymbolID("ns::xyz")}), + EXPECT_THAT(lookup(*I, {SymbolID("ns::nonono"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::xyz")); - EXPECT_THAT(lookup(I, SymbolID("ns::nonono")), UnorderedElementsAre()); + EXPECT_THAT(lookup(*I, SymbolID("ns::nonono")), UnorderedElementsAre()); } } // namespace diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index d18f18096..7861f4c1c 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -54,23 +54,27 @@ std::unique_ptr occurrenceSlab(const SymbolID &ID, auto Slab = llvm::make_unique(); SymbolOccurrence Occurrence; Occurrence.Location.FileURI = Path; + Occurrence.Kind = SymbolOccurrenceKind::Reference; Slab->insert(ID, Occurrence); return Slab; } -std::vector -getSymbolNames(const std::vector &Symbols) { +std::vector getSymbolNames(const SymbolIndex &I, + std::string Query = "") { + FuzzyFindRequest Req; + Req.Query = Query; std::vector Names; - for (const Symbol *Sym : Symbols) - Names.push_back(Sym->Name); + I.fuzzyFind(Req, [&](const Symbol &S) { Names.push_back(S.Name); }); return Names; } -std::vector -getOccurrencePath(const std::vector &Occurrences) { +std::vector getOccurrencePaths(const SymbolIndex &I, SymbolID ID) { + OccurrencesRequest Req; + Req.IDs = {ID}; std::vector Paths; - for (const auto *O : Occurrences) - Paths.push_back(O->Location.FileURI); + I.findOccurrences(Req, [&](const SymbolOccurrence &S) { + Paths.push_back(S.Location.FileURI); + }); return Paths; } @@ -82,15 +86,12 @@ std::unique_ptr emptyOccurrence() { TEST(FileSymbolsTest, UpdateAndGet) { FileSymbols FS; - EXPECT_THAT(getSymbolNames(*FS.allSymbols()), UnorderedElementsAre()); - EXPECT_TRUE(FS.allOccurrences()->empty()); + EXPECT_THAT(getSymbolNames(*FS.buildMemIndex()), UnorderedElementsAre()); - SymbolID ID("1"); - FS.update("f1", numSlab(1, 3), occurrenceSlab(ID, "f1.cc")); - EXPECT_THAT(getSymbolNames(*FS.allSymbols()), + FS.update("f1", numSlab(1, 3), occurrenceSlab(SymbolID("1"), "f1.cc")); + EXPECT_THAT(getSymbolNames(*FS.buildMemIndex()), UnorderedElementsAre("1", "2", "3")); - auto Occurrences = FS.allOccurrences(); - EXPECT_THAT(getOccurrencePath((*Occurrences)[ID]), + EXPECT_THAT(getOccurrencePaths(*FS.buildMemIndex(), SymbolID("1")), UnorderedElementsAre("f1.cc")); } @@ -98,8 +99,8 @@ TEST(FileSymbolsTest, Overlap) { FileSymbols FS; FS.update("f1", numSlab(1, 3), emptyOccurrence()); FS.update("f2", numSlab(3, 5), emptyOccurrence()); - EXPECT_THAT(getSymbolNames(*FS.allSymbols()), - UnorderedElementsAre("1", "2", "3", "3", "4", "5")); + EXPECT_THAT(getSymbolNames(*FS.buildMemIndex()), + UnorderedElementsAre("1", "2", "3", "4", "5")); } TEST(FileSymbolsTest, SnapshotAliveAfterRemove) { @@ -108,19 +109,17 @@ TEST(FileSymbolsTest, SnapshotAliveAfterRemove) { SymbolID ID("1"); FS.update("f1", numSlab(1, 3), occurrenceSlab(ID, "f1.cc")); - auto Symbols = FS.allSymbols(); + auto Symbols = FS.buildMemIndex(); EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3")); - auto Occurrences = FS.allOccurrences(); - EXPECT_THAT(getOccurrencePath((*Occurrences)[ID]), - UnorderedElementsAre("f1.cc")); + EXPECT_THAT(getOccurrencePaths(*Symbols, ID), UnorderedElementsAre("f1.cc")); FS.update("f1", nullptr, nullptr); - EXPECT_THAT(getSymbolNames(*FS.allSymbols()), UnorderedElementsAre()); - EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3")); + auto Empty = FS.buildMemIndex(); + EXPECT_THAT(getSymbolNames(*Empty), UnorderedElementsAre()); + EXPECT_THAT(getOccurrencePaths(*Empty, ID), UnorderedElementsAre()); - EXPECT_TRUE(FS.allOccurrences()->empty()); - EXPECT_THAT(getOccurrencePath((*Occurrences)[ID]), - UnorderedElementsAre("f1.cc")); + EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3")); + EXPECT_THAT(getOccurrencePaths(*Symbols, ID), UnorderedElementsAre("f1.cc")); } std::vector match(const SymbolIndex &I, diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp index 7fd39e60e..e4c1100c3 100644 --- a/unittests/clangd/IndexTests.cpp +++ b/unittests/clangd/IndexTests.cpp @@ -17,18 +17,16 @@ #include "index/Merge.h" #include "gtest/gtest.h" +using testing::AllOf; +using testing::ElementsAre; using testing::Pointee; using testing::UnorderedElementsAre; -using testing::AllOf; +using namespace llvm; namespace clang { namespace clangd { namespace { -std::shared_ptr emptyOccurrences() { - return llvm::make_unique(); -} - MATCHER_P(Named, N, "") { return arg.Name == N; } MATCHER_P(OccurrenceRange, Range, "") { return std::tie(arg.Location.Start.Line, arg.Location.Start.Column, @@ -54,155 +52,139 @@ TEST(SymbolSlab, FindAndIterate) { EXPECT_THAT(*S.find(SymbolID(Sym)), Named(Sym)); } -TEST(MemIndexTest, MemIndexSymbolsRecycled) { - MemIndex I; - std::weak_ptr Symbols; - I.build(generateNumSymbols(0, 10, &Symbols), emptyOccurrences()); - FuzzyFindRequest Req; - Req.Query = "7"; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("7")); +TEST(SwapIndexTest, OldIndexRecycled) { + auto Token = std::make_shared(); + std::weak_ptr WeakToken = Token; - EXPECT_FALSE(Symbols.expired()); - // Release old symbols. - I.build(generateNumSymbols(0, 0), emptyOccurrences()); - EXPECT_TRUE(Symbols.expired()); + SwapIndex S(make_unique(SymbolSlab(), MemIndex::OccurrenceMap(), + std::move(Token))); + EXPECT_FALSE(WeakToken.expired()); // Current MemIndex keeps it alive. + S.reset(make_unique()); // Now the MemIndex is destroyed. + EXPECT_TRUE(WeakToken.expired()); // So the token is too. } TEST(MemIndexTest, MemIndexDeduplicate) { - auto Symbols = generateNumSymbols(0, 10); - - // Inject some duplicates and make sure we only match the same symbol once. - auto Sym = symbol("7"); - Symbols->push_back(&Sym); - Symbols->push_back(&Sym); - Symbols->push_back(&Sym); - + std::vector Symbols = {symbol("1"), symbol("2"), symbol("3"), + symbol("2") /* duplicate */}; FuzzyFindRequest Req; - Req.Query = "7"; - MemIndex I; - I.build(std::move(Symbols), emptyOccurrences()); - auto Matches = match(I, Req); - EXPECT_EQ(Matches.size(), 1u); + Req.Query = "2"; + MemIndex I(Symbols, MemIndex::OccurrenceMap()); + EXPECT_THAT(match(I, Req), ElementsAre("2")); } TEST(MemIndexTest, MemIndexLimitedNumMatches) { - MemIndex I; - I.build(generateNumSymbols(0, 100), emptyOccurrences()); + auto I = MemIndex::build(generateNumSymbols(0, 100), SymbolOccurrenceSlab()); FuzzyFindRequest Req; Req.Query = "5"; Req.MaxCandidateCount = 3; bool Incomplete; - auto Matches = match(I, Req, &Incomplete); + auto Matches = match(*I, Req, &Incomplete); EXPECT_EQ(Matches.size(), Req.MaxCandidateCount); EXPECT_TRUE(Incomplete); } TEST(MemIndexTest, FuzzyMatch) { - MemIndex I; - I.build( + auto I = MemIndex::build( generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}), - emptyOccurrences()); + SymbolOccurrenceSlab()); FuzzyFindRequest Req; Req.Query = "lol"; Req.MaxCandidateCount = 2; - EXPECT_THAT(match(I, Req), + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); } TEST(MemIndexTest, MatchQualifiedNamesWithoutSpecificScope) { - MemIndex I; - I.build(generateSymbols({"a::y1", "b::y2", "y3"}), emptyOccurrences()); + auto I = MemIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), + SymbolOccurrenceSlab()); FuzzyFindRequest Req; Req.Query = "y"; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3")); + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3")); } TEST(MemIndexTest, MatchQualifiedNamesWithGlobalScope) { - MemIndex I; - I.build(generateSymbols({"a::y1", "b::y2", "y3"}), emptyOccurrences()); + auto I = MemIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), + SymbolOccurrenceSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {""}; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("y3")); + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("y3")); } TEST(MemIndexTest, MatchQualifiedNamesWithOneScope) { - MemIndex I; - I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}), - emptyOccurrences()); + auto I = MemIndex::build( + generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}), + SymbolOccurrenceSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1", "a::y2")); + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "a::y2")); } TEST(MemIndexTest, MatchQualifiedNamesWithMultipleScopes) { - MemIndex I; - I.build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}), - emptyOccurrences()); + auto I = MemIndex::build( + generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}), + SymbolOccurrenceSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::", "b::"}; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1", "a::y2", "b::y3")); + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "a::y2", "b::y3")); } TEST(MemIndexTest, NoMatchNestedScopes) { - MemIndex I; - I.build(generateSymbols({"a::y1", "a::b::y2"}), emptyOccurrences()); + auto I = MemIndex::build(generateSymbols({"a::y1", "a::b::y2"}), + SymbolOccurrenceSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("a::y1")); + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1")); } TEST(MemIndexTest, IgnoreCases) { - MemIndex I; - I.build(generateSymbols({"ns::ABC", "ns::abc"}), emptyOccurrences()); + auto I = MemIndex::build(generateSymbols({"ns::ABC", "ns::abc"}), + SymbolOccurrenceSlab()); FuzzyFindRequest Req; Req.Query = "AB"; Req.Scopes = {"ns::"}; - EXPECT_THAT(match(I, Req), UnorderedElementsAre("ns::ABC", "ns::abc")); + EXPECT_THAT(match(*I, Req), UnorderedElementsAre("ns::ABC", "ns::abc")); } TEST(MemIndexTest, Lookup) { - MemIndex I; - I.build(generateSymbols({"ns::abc", "ns::xyz"}), emptyOccurrences()); - EXPECT_THAT(lookup(I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); - EXPECT_THAT(lookup(I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), + auto I = MemIndex::build(generateSymbols({"ns::abc", "ns::xyz"}), + SymbolOccurrenceSlab()); + EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); + EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::abc", "ns::xyz")); - EXPECT_THAT(lookup(I, {SymbolID("ns::nonono"), SymbolID("ns::xyz")}), + EXPECT_THAT(lookup(*I, {SymbolID("ns::nonono"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::xyz")); - EXPECT_THAT(lookup(I, SymbolID("ns::nonono")), UnorderedElementsAre()); + EXPECT_THAT(lookup(*I, SymbolID("ns::nonono")), UnorderedElementsAre()); } TEST(MergeIndexTest, Lookup) { - MemIndex I, J; - I.build(generateSymbols({"ns::A", "ns::B"}), emptyOccurrences()); - J.build(generateSymbols({"ns::B", "ns::C"}), emptyOccurrences()); - EXPECT_THAT(lookup(*mergeIndex(&I, &J), SymbolID("ns::A")), - UnorderedElementsAre("ns::A")); - EXPECT_THAT(lookup(*mergeIndex(&I, &J), SymbolID("ns::B")), - UnorderedElementsAre("ns::B")); - EXPECT_THAT(lookup(*mergeIndex(&I, &J), SymbolID("ns::C")), - UnorderedElementsAre("ns::C")); - EXPECT_THAT( - lookup(*mergeIndex(&I, &J), {SymbolID("ns::A"), SymbolID("ns::B")}), - UnorderedElementsAre("ns::A", "ns::B")); - EXPECT_THAT( - lookup(*mergeIndex(&I, &J), {SymbolID("ns::A"), SymbolID("ns::C")}), - UnorderedElementsAre("ns::A", "ns::C")); - EXPECT_THAT(lookup(*mergeIndex(&I, &J), SymbolID("ns::D")), - UnorderedElementsAre()); - EXPECT_THAT(lookup(*mergeIndex(&I, &J), {}), UnorderedElementsAre()); + auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), + SymbolOccurrenceSlab()), + J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), + SymbolOccurrenceSlab()); + auto M = mergeIndex(I.get(), J.get()); + EXPECT_THAT(lookup(*M, SymbolID("ns::A")), UnorderedElementsAre("ns::A")); + EXPECT_THAT(lookup(*M, SymbolID("ns::B")), UnorderedElementsAre("ns::B")); + EXPECT_THAT(lookup(*M, SymbolID("ns::C")), UnorderedElementsAre("ns::C")); + EXPECT_THAT(lookup(*M, {SymbolID("ns::A"), SymbolID("ns::B")}), + UnorderedElementsAre("ns::A", "ns::B")); + EXPECT_THAT(lookup(*M, {SymbolID("ns::A"), SymbolID("ns::C")}), + UnorderedElementsAre("ns::A", "ns::C")); + EXPECT_THAT(lookup(*M, SymbolID("ns::D")), UnorderedElementsAre()); + EXPECT_THAT(lookup(*M, {}), UnorderedElementsAre()); } TEST(MergeIndexTest, FuzzyFind) { - MemIndex I, J; - I.build(generateSymbols({"ns::A", "ns::B"}), emptyOccurrences()); - J.build(generateSymbols({"ns::B", "ns::C"}), emptyOccurrences()); + auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), + SymbolOccurrenceSlab()), + J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), + SymbolOccurrenceSlab()); FuzzyFindRequest Req; Req.Scopes = {"ns::"}; - EXPECT_THAT(match(*mergeIndex(&I, &J), Req), + EXPECT_THAT(match(*mergeIndex(I.get(), J.get()), Req), UnorderedElementsAre("ns::A", "ns::B", "ns::C")); } diff --git a/unittests/clangd/TestIndex.cpp b/unittests/clangd/TestIndex.cpp index 760abee61..714d3a684 100644 --- a/unittests/clangd/TestIndex.cpp +++ b/unittests/clangd/TestIndex.cpp @@ -26,30 +26,18 @@ Symbol symbol(llvm::StringRef QName) { return Sym; } -std::shared_ptr> -generateSymbols(std::vector QualifiedNames, - std::weak_ptr *WeakSymbols) { +SymbolSlab generateSymbols(std::vector QualifiedNames) { SymbolSlab::Builder Slab; for (llvm::StringRef QName : QualifiedNames) Slab.insert(symbol(QName)); - - auto Storage = std::make_shared(); - Storage->Slab = std::move(Slab).build(); - for (const auto &Sym : Storage->Slab) - Storage->Pointers.push_back(&Sym); - if (WeakSymbols) - *WeakSymbols = Storage; - auto *Pointers = &Storage->Pointers; - return {std::move(Storage), Pointers}; + return std::move(Slab).build(); } -std::shared_ptr> -generateNumSymbols(int Begin, int End, - std::weak_ptr *WeakSymbols) { +SymbolSlab generateNumSymbols(int Begin, int End) { std::vector Names; for (int i = Begin; i <= End; i++) Names.push_back(std::to_string(i)); - return generateSymbols(Names, WeakSymbols); + return generateSymbols(Names); } std::string getQualifiedName(const Symbol &Sym) { diff --git a/unittests/clangd/TestIndex.h b/unittests/clangd/TestIndex.h index f49c9c351..3f7d1e295 100644 --- a/unittests/clangd/TestIndex.h +++ b/unittests/clangd/TestIndex.h @@ -23,26 +23,11 @@ namespace clangd { // Creates Symbol instance and sets SymbolID to given QualifiedName. Symbol symbol(llvm::StringRef QName); -// Bundles symbol pointers with the actual symbol slab the pointers refer to in -// order to ensure that the slab isn't destroyed while it's used by and index. -struct SlabAndPointers { - SymbolSlab Slab; - std::vector Pointers; -}; +// Create a slab of symbols with the given qualified names as IDs and names. +SymbolSlab generateSymbols(std::vector QualifiedNames); -// Create a slab of symbols with the given qualified names as both IDs and -// names. The life time of the slab is managed by the returned shared pointer. -// If \p WeakSymbols is provided, it will be pointed to the managed object in -// the returned shared pointer. -std::shared_ptr> -generateSymbols(std::vector QualifiedNames, - std::weak_ptr *WeakSymbols = nullptr); - -// Create a slab of symbols with IDs and names [Begin, End], otherwise identical -// to the `generateSymbols` above. -std::shared_ptr> -generateNumSymbols(int Begin, int End, - std::weak_ptr *WeakSymbols = nullptr); +// Create a slab of symbols with IDs and names [Begin, End]. +SymbolSlab generateNumSymbols(int Begin, int End); // Returns fully-qualified name out of given symbol. std::string getQualifiedName(const Symbol &Sym); From 7e0ee20b0298b4b647cd54b127a8fdfe2af4041f Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Mon, 3 Sep 2018 14:39:34 +0000 Subject: [PATCH 123/686] [clangd] Handle errors before checking for cancelltion To avoid hitting assertions in llvm::Expected destructor. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341319 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 6bdd082cd..7f0b29d24 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -191,11 +191,10 @@ TaskHandle ClangdServer::codeComplete(PathRef File, Position Pos, auto Task = [PCHs, Pos, FS, CodeCompleteOpts, this](Path File, Callback CB, llvm::Expected IP) { - if (isCancelled()) - return CB(llvm::make_error()); - if (!IP) return CB(IP.takeError()); + if (isCancelled()) + return CB(llvm::make_error()); auto PreambleData = IP->Preamble; From 765b491309866a8d587d8d0f2c62a53dc3400d61 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Mon, 3 Sep 2018 15:23:01 +0000 Subject: [PATCH 124/686] [clangd] Fix ambiguous make_unique with c++17. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341321 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/IndexTests.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp index e4c1100c3..181475f2d 100644 --- a/unittests/clangd/IndexTests.cpp +++ b/unittests/clangd/IndexTests.cpp @@ -56,11 +56,11 @@ TEST(SwapIndexTest, OldIndexRecycled) { auto Token = std::make_shared(); std::weak_ptr WeakToken = Token; - SwapIndex S(make_unique(SymbolSlab(), MemIndex::OccurrenceMap(), - std::move(Token))); - EXPECT_FALSE(WeakToken.expired()); // Current MemIndex keeps it alive. - S.reset(make_unique()); // Now the MemIndex is destroyed. - EXPECT_TRUE(WeakToken.expired()); // So the token is too. + SwapIndex S(llvm::make_unique( + SymbolSlab(), MemIndex::OccurrenceMap(), std::move(Token))); + EXPECT_FALSE(WeakToken.expired()); // Current MemIndex keeps it alive. + S.reset(llvm::make_unique()); // Now the MemIndex is destroyed. + EXPECT_TRUE(WeakToken.expired()); // So the token is too. } TEST(MemIndexTest, MemIndexDeduplicate) { From 9c25343e498469ebb9eee2df82e1e86ad42c1f30 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Mon, 3 Sep 2018 15:25:27 +0000 Subject: [PATCH 125/686] [clangd] Avoid crashes in override completions Summary: NamedDecl::getName cannot be called on non-identifier names. Reviewers: kadircet, ioeric, hokein, sammccall Reviewed By: ioeric Subscribers: MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D51598 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341322 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 6 +++--- unittests/clangd/CodeCompleteTests.cpp | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 617046b69..eda6dfde7 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -210,7 +210,7 @@ getNonOverridenMethodCompletionResults(const DeclContext *DC, Sema *S) { // These are stored by name to make querying fast in the later step. llvm::StringMap> Overrides; for (auto *Method : CR->methods()) { - if (!Method->isVirtual()) + if (!Method->isVirtual() || !Method->getIdentifier()) continue; Overrides[Method->getName()].push_back(Method); } @@ -221,14 +221,14 @@ getNonOverridenMethodCompletionResults(const DeclContext *DC, Sema *S) { if (!BR) continue; for (auto *Method : BR->methods()) { - if (!Method->isVirtual()) + if (!Method->isVirtual() || !Method->getIdentifier()) continue; const auto it = Overrides.find(Method->getName()); bool IsOverriden = false; if (it != Overrides.end()) { for (auto *MD : it->second) { // If the method in current body is not an overload of this virtual - // function, that it overrides this one. + // function, then it overrides this one. if (!S->IsOverload(MD, Method, false)) { IsOverriden = true; break; diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 80e5f9143..ca2f449bf 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1765,6 +1765,21 @@ TEST(CompletionTest, SuggestOverrides) { Not(Contains(Labeled("void vfunc(bool param) override"))))); } +TEST(CompletionTest, OverridesNonIdentName) { + // Check the completions call does not crash. + completions(R"cpp( + struct Base { + virtual ~Base() = 0; + virtual operator int() = 0; + virtual Base& operator+(Base&) = 0; + }; + + struct Derived : Base { + ^ + }; + )cpp"); +} + TEST(SpeculateCompletionFilter, Filters) { Annotations F(R"cpp($bof^ $bol^ From c4e89dc695761fbd58ccc4551837657d66875d6d Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Mon, 3 Sep 2018 16:37:59 +0000 Subject: [PATCH 126/686] [clangd] Some nitpicking around the new split (preamble/main) dynamic index Summary: - DynamicIndex doesn't implement ParsingCallbacks, to make its role clearer. ParsingCallbacks is a separate object owned by the receiving TUScheduler. (I tried to get rid of the "index-like-object that doesn't implement index" but it was too messy). - Clarified(?) docs around DynamicIndex - fewer details up front, more details inside. - Exposed dynamic index from ClangdServer for memory monitoring and more direct testing of its contents (actual tests not added here, wanted to get this out for review) - Removed a redundant and sligthly confusing filename param in a callback Reviewers: ilya-biryukov Subscribers: javed.absar, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51221 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341325 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 61 +++++++++++++++++++-------- clangd/ClangdServer.h | 11 +++-- clangd/ClangdUnit.cpp | 2 +- clangd/ClangdUnit.h | 4 +- clangd/CodeComplete.cpp | 4 +- clangd/CodeComplete.h | 12 +++--- clangd/TUScheduler.cpp | 26 +++++------- clangd/TUScheduler.h | 6 +-- unittests/clangd/FileIndexTests.cpp | 5 +-- unittests/clangd/TUSchedulerTests.cpp | 20 ++++----- 10 files changed, 88 insertions(+), 63 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 7f0b29d24..30bef3ca7 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -71,34 +71,57 @@ class RefactoringResultCollector final }; } // namespace -/// Manages dynamic index for open files. Each file might contribute two sets -/// of symbols to the dynamic index: symbols from the preamble and symbols -/// from the file itself. Those have different lifetimes and we merge results -/// from both -class ClangdServer::DynamicIndex : public ParsingCallbacks { +/// The dynamic index tracks symbols visible in open files. +/// For boring reasons, it doesn't implement SymbolIndex directly - use index(). +class ClangdServer::DynamicIndex { public: DynamicIndex(std::vector URISchemes) : PreambleIdx(URISchemes), MainFileIdx(URISchemes), MergedIndex(mergeIndex(&MainFileIdx, &PreambleIdx)) {} - SymbolIndex &index() const { return *MergedIndex; } + const SymbolIndex &index() const { return *MergedIndex; } - void onPreambleAST(PathRef Path, ASTContext &Ctx, - std::shared_ptr PP) override { - PreambleIdx.update(Path, &Ctx, PP, /*TopLevelDecls=*/llvm::None); - } + // Returns callbacks that can be used to update the index with new ASTs. + // Index() presents a merged view of the supplied main-file and preamble ASTs. + std::unique_ptr makeUpdateCallbacks() { + struct CB : public ParsingCallbacks { + CB(ClangdServer::DynamicIndex *This) : This(This) {} + DynamicIndex *This; - void onMainAST(PathRef Path, ParsedAST &AST) override { + void onPreambleAST(PathRef Path, ASTContext &Ctx, + std::shared_ptr PP) override { + This->PreambleIdx.update(Path, &Ctx, std::move(PP)); + } - MainFileIdx.update(Path, &AST.getASTContext(), AST.getPreprocessorPtr(), - AST.getLocalTopLevelDecls()); - } + void onMainAST(PathRef Path, ParsedAST &AST) override { + This->MainFileIdx.update(Path, &AST.getASTContext(), + AST.getPreprocessorPtr(), + AST.getLocalTopLevelDecls()); + } + }; + return llvm::make_unique(this); + }; private: + // Contains information from each file's preamble only. + // These are large, but update fairly infrequently (preambles are stable). + // Missing information: + // - symbol occurrences (these are always "from the main file") + // - definition locations in the main file + // + // FIXME: Because the preambles for different TUs have large overlap and + // FileIndex doesn't deduplicate, this uses lots of extra RAM. + // The biggest obstacle in fixing this: the obvious approach of partitioning + // by declaring file (rather than main file) fails if headers provide + // different symbols based on preprocessor state. FileIndex PreambleIdx; + // Contains information from each file's main AST. + // These are updated frequently (on file change), but are relatively small. + // Mostly contains: + // - occurrences of symbols declared in the preamble and referenced from main + // - symbols declared both in the main file and the preamble + // (Note that symbols *only* in the main file are not indexed). FileIndex MainFileIdx; - /// Merged view into both indexes. Merges are performed in a similar manner - /// to the merges of dynamic and static index. std::unique_ptr MergedIndex; }; @@ -127,7 +150,7 @@ ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB, // FIXME(ioeric): this can be slow and we may be able to index on less // critical paths. WorkScheduler(Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory, - DynamicIdx ? *DynamicIdx : noopParsingCallbacks(), + DynamicIdx ? DynamicIdx->makeUpdateCallbacks() : nullptr, Opts.UpdateDebounce, Opts.RetentionPolicy) { if (DynamicIdx && Opts.StaticIndex) { MergedIndex = mergeIndex(&DynamicIdx->index(), Opts.StaticIndex); @@ -144,6 +167,10 @@ ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB, // ClangdServer::DynamicIndex. ClangdServer::~ClangdServer() = default; +const SymbolIndex *ClangdServer::dynamicIndex() const { + return DynamicIdx ? &DynamicIdx->index() : nullptr; +} + void ClangdServer::setRootPath(PathRef RootPath) { auto FS = FSProvider.getFileSystem(); auto Status = FS->status(RootPath); diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h index 018bddd3b..50a1aea58 100644 --- a/clangd/ClangdServer.h +++ b/clangd/ClangdServer.h @@ -191,6 +191,10 @@ class ClangdServer { /// FIXME: those metrics might be useful too, we should add them. std::vector> getUsedBytesPerFile() const; + /// Returns the active dynamic index if one was built. + /// This can be useful for testing, debugging, or observing memory usage. + const SymbolIndex *dynamicIndex() const; + // Blocks the main thread until the server is idle. Only for use in tests. // Returns false if the timeout expires. LLVM_NODISCARD bool @@ -224,11 +228,10 @@ class ClangdServer { // - the dynamic index owned by ClangdServer (DynamicIdx) // - the static index passed to the constructor // - a merged view of a static and dynamic index (MergedIndex) - SymbolIndex *Index; - /// If present, an up-to-date of symbols in open files. Read via Index. + const SymbolIndex *Index; + // If present, an index of symbols in open files. Read via *Index. std::unique_ptr DynamicIdx; - // If present, a merged view of DynamicIdx and an external index. Read via - // Index. + // If present, storage for the merged static/dynamic index. Read via *Index. std::unique_ptr MergedIndex; // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex) diff --git a/clangd/ClangdUnit.cpp b/clangd/ClangdUnit.cpp index ecb597ca7..6c184ff06 100644 --- a/clangd/ClangdUnit.cpp +++ b/clangd/ClangdUnit.cpp @@ -95,7 +95,7 @@ class CppFilePreambleCallbacks : public PreambleCallbacks { if (!ParsedCallback) return; trace::Span Tracer("Running PreambleCallback"); - ParsedCallback(File, CI.getASTContext(), CI.getPreprocessorPtr()); + ParsedCallback(CI.getASTContext(), CI.getPreprocessorPtr()); } void BeforeExecute(CompilerInstance &CI) override { diff --git a/clangd/ClangdUnit.h b/clangd/ClangdUnit.h index 0b12fb1c0..f739270a1 100644 --- a/clangd/ClangdUnit.h +++ b/clangd/ClangdUnit.h @@ -127,8 +127,8 @@ class ParsedAST { IncludeStructure Includes; }; -using PreambleParsedCallback = std::function)>; +using PreambleParsedCallback = + std::function)>; /// Builds compiler invocation that could be used to build AST or preamble. std::unique_ptr diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index eda6dfde7..8a1089966 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -798,7 +798,7 @@ struct ScoredSignature { class SignatureHelpCollector final : public CodeCompleteConsumer { public: SignatureHelpCollector(const clang::CodeCompleteOptions &CodeCompleteOpts, - SymbolIndex *Index, SignatureHelp &SigHelp) + const SymbolIndex *Index, SignatureHelp &SigHelp) : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false), SigHelp(SigHelp), @@ -1584,7 +1584,7 @@ SignatureHelp signatureHelp(PathRef FileName, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, - SymbolIndex *Index) { + const SymbolIndex *Index) { SignatureHelp Result; clang::CodeCompleteOptions Options; Options.IncludeGlobals = false; diff --git a/clangd/CodeComplete.h b/clangd/CodeComplete.h index 3213acc7a..3197e172b 100644 --- a/clangd/CodeComplete.h +++ b/clangd/CodeComplete.h @@ -220,11 +220,13 @@ CodeCompleteResult codeComplete(PathRef FileName, SpeculativeFuzzyFind *SpecFuzzyFind = nullptr); /// Get signature help at a specified \p Pos in \p FileName. -SignatureHelp -signatureHelp(PathRef FileName, const tooling::CompileCommand &Command, - PrecompiledPreamble const *Preamble, StringRef Contents, - Position Pos, IntrusiveRefCntPtr VFS, - std::shared_ptr PCHs, SymbolIndex *Index); +SignatureHelp signatureHelp(PathRef FileName, + const tooling::CompileCommand &Command, + PrecompiledPreamble const *Preamble, + StringRef Contents, Position Pos, + IntrusiveRefCntPtr VFS, + std::shared_ptr PCHs, + const SymbolIndex *Index); // For index-based completion, we only consider: // * symbols in namespaces or translation unit scopes (e.g. no class diff --git a/clangd/TUScheduler.cpp b/clangd/TUScheduler.cpp index 48dc445d1..0ae2a3c9c 100644 --- a/clangd/TUScheduler.cpp +++ b/clangd/TUScheduler.cpp @@ -365,13 +365,12 @@ void ASTWorker::update( std::shared_ptr OldPreamble = getPossiblyStalePreamble(); - std::shared_ptr NewPreamble = - buildPreamble(FileName, *Invocation, OldPreamble, OldCommand, Inputs, - PCHs, StorePreambleInMemory, - [this](PathRef Path, ASTContext &Ctx, - std::shared_ptr PP) { - Callbacks.onPreambleAST(FileName, Ctx, std::move(PP)); - }); + std::shared_ptr NewPreamble = buildPreamble( + FileName, *Invocation, OldPreamble, OldCommand, Inputs, PCHs, + StorePreambleInMemory, + [this](ASTContext &Ctx, std::shared_ptr PP) { + Callbacks.onPreambleAST(FileName, Ctx, std::move(PP)); + }); bool CanReuseAST = InputsAreTheSame && (OldPreamble == NewPreamble); { @@ -654,11 +653,6 @@ unsigned getDefaultAsyncThreadsCount() { return HardwareConcurrency; } -ParsingCallbacks &noopParsingCallbacks() { - static ParsingCallbacks *Instance = new ParsingCallbacks; - return *Instance; -} - struct TUScheduler::FileData { /// Latest inputs, passed to TUScheduler::update(). std::string Contents; @@ -668,11 +662,13 @@ struct TUScheduler::FileData { TUScheduler::TUScheduler(unsigned AsyncThreadsCount, bool StorePreamblesInMemory, - ParsingCallbacks &Callbacks, + std::unique_ptr Callbacks, std::chrono::steady_clock::duration UpdateDebounce, ASTRetentionPolicy RetentionPolicy) : StorePreamblesInMemory(StorePreamblesInMemory), - PCHOps(std::make_shared()), Callbacks(Callbacks), + PCHOps(std::make_shared()), + Callbacks(Callbacks ? move(Callbacks) + : llvm::make_unique()), Barrier(AsyncThreadsCount), IdleASTs(llvm::make_unique(RetentionPolicy.MaxRetainedASTs)), UpdateDebounce(UpdateDebounce) { @@ -711,7 +707,7 @@ void TUScheduler::update( // Create a new worker to process the AST-related tasks. ASTWorkerHandle Worker = ASTWorker::create( File, *IdleASTs, WorkerThreads ? WorkerThreads.getPointer() : nullptr, - Barrier, UpdateDebounce, PCHOps, StorePreamblesInMemory, Callbacks); + Barrier, UpdateDebounce, PCHOps, StorePreamblesInMemory, *Callbacks); FD = std::unique_ptr(new FileData{ Inputs.Contents, Inputs.CompileCommand, std::move(Worker)}); } else { diff --git a/clangd/TUScheduler.h b/clangd/TUScheduler.h index 3ef17c6b6..e049d3d8d 100644 --- a/clangd/TUScheduler.h +++ b/clangd/TUScheduler.h @@ -74,8 +74,6 @@ class ParsingCallbacks { virtual void onMainAST(PathRef Path, ParsedAST &AST) {} }; -ParsingCallbacks &noopParsingCallbacks(); - /// Handles running tasks for ClangdServer and managing the resources (e.g., /// preambles and ASTs) for opened files. /// TUScheduler is not thread-safe, only one thread should be providing updates @@ -86,7 +84,7 @@ ParsingCallbacks &noopParsingCallbacks(); class TUScheduler { public: TUScheduler(unsigned AsyncThreadsCount, bool StorePreamblesInMemory, - ParsingCallbacks &ASTCallbacks, + std::unique_ptr ASTCallbacks, std::chrono::steady_clock::duration UpdateDebounce, ASTRetentionPolicy RetentionPolicy); ~TUScheduler(); @@ -166,7 +164,7 @@ class TUScheduler { private: const bool StorePreamblesInMemory; const std::shared_ptr PCHOps; - ParsingCallbacks &Callbacks; + std::unique_ptr Callbacks; // not nullptr Semaphore Barrier; llvm::StringMap> Files; std::unique_ptr IdleASTs; diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index 7861f4c1c..debca6ac6 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -296,11 +296,10 @@ TEST(FileIndexTest, RebuildWithPreamble) { buildPreamble( FooCpp, *CI, /*OldPreamble=*/nullptr, tooling::CompileCommand(), PI, std::make_shared(), /*StoreInMemory=*/true, - [&Index, &IndexUpdated](PathRef FilePath, ASTContext &Ctx, - std::shared_ptr PP) { + [&](ASTContext &Ctx, std::shared_ptr PP) { EXPECT_FALSE(IndexUpdated) << "Expected only a single index update"; IndexUpdated = true; - Index.update(FilePath, &Ctx, std::move(PP)); + Index.update(FooCpp, &Ctx, std::move(PP)); }); ASSERT_TRUE(IndexUpdated); diff --git a/unittests/clangd/TUSchedulerTests.cpp b/unittests/clangd/TUSchedulerTests.cpp index 5222d795c..1210b1d18 100644 --- a/unittests/clangd/TUSchedulerTests.cpp +++ b/unittests/clangd/TUSchedulerTests.cpp @@ -46,7 +46,7 @@ class TUSchedulerTests : public ::testing::Test { TEST_F(TUSchedulerTests, MissingFiles) { TUScheduler S(getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, noopParsingCallbacks(), + /*StorePreamblesInMemory=*/true, /*ASTCallbacks=*/nullptr, /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); @@ -104,7 +104,7 @@ TEST_F(TUSchedulerTests, WantDiagnostics) { Notification Ready; TUScheduler S( getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, noopParsingCallbacks(), + /*StorePreamblesInMemory=*/true, /*ASTCallbacks=*/nullptr, /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); auto Path = testPath("foo.cpp"); @@ -132,7 +132,7 @@ TEST_F(TUSchedulerTests, Debounce) { std::atomic CallbackCount(0); { TUScheduler S(getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, noopParsingCallbacks(), + /*StorePreamblesInMemory=*/true, /*ASTCallbacks=*/nullptr, /*UpdateDebounce=*/std::chrono::seconds(1), ASTRetentionPolicy()); // FIXME: we could probably use timeouts lower than 1 second here. @@ -165,7 +165,7 @@ TEST_F(TUSchedulerTests, PreambleConsistency) { Notification InconsistentReadDone; // Must live longest. TUScheduler S( getDefaultAsyncThreadsCount(), /*StorePreamblesInMemory=*/true, - noopParsingCallbacks(), + /*ASTCallbacks=*/nullptr, /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); auto Path = testPath("foo.cpp"); @@ -221,7 +221,7 @@ TEST_F(TUSchedulerTests, ManyUpdates) { // Run TUScheduler and collect some stats. { TUScheduler S(getDefaultAsyncThreadsCount(), - /*StorePreamblesInMemory=*/true, noopParsingCallbacks(), + /*StorePreamblesInMemory=*/true, /*ASTCallbacks=*/nullptr, /*UpdateDebounce=*/std::chrono::milliseconds(50), ASTRetentionPolicy()); @@ -319,7 +319,7 @@ TEST_F(TUSchedulerTests, EvictedAST) { Policy.MaxRetainedASTs = 2; TUScheduler S( /*AsyncThreadsCount=*/1, /*StorePreambleInMemory=*/true, - noopParsingCallbacks(), + /*ASTCallbacks=*/nullptr, /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), Policy); llvm::StringLiteral SourceContents = R"cpp( @@ -369,7 +369,7 @@ TEST_F(TUSchedulerTests, EvictedAST) { TEST_F(TUSchedulerTests, EmptyPreamble) { TUScheduler S( /*AsyncThreadsCount=*/4, /*StorePreambleInMemory=*/true, - noopParsingCallbacks(), + /*ASTCallbacks=*/nullptr, /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); @@ -416,7 +416,7 @@ TEST_F(TUSchedulerTests, RunWaitsForPreamble) { // the same time. All reads should get the same non-null preamble. TUScheduler S( /*AsyncThreadsCount=*/4, /*StorePreambleInMemory=*/true, - noopParsingCallbacks(), + /*ASTCallbacks=*/nullptr, /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); auto Foo = testPath("foo.cpp"); @@ -449,7 +449,7 @@ TEST_F(TUSchedulerTests, RunWaitsForPreamble) { TEST_F(TUSchedulerTests, NoopOnEmptyChanges) { TUScheduler S( /*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(), - /*StorePreambleInMemory=*/true, noopParsingCallbacks(), + /*StorePreambleInMemory=*/true, /*ASTCallbacks=*/nullptr, /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); @@ -502,7 +502,7 @@ TEST_F(TUSchedulerTests, NoopOnEmptyChanges) { TEST_F(TUSchedulerTests, NoChangeDiags) { TUScheduler S( /*AsyncThreadsCount=*/getDefaultAsyncThreadsCount(), - /*StorePreambleInMemory=*/true, noopParsingCallbacks(), + /*StorePreambleInMemory=*/true, /*ASTCallbacks=*/nullptr, /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); From 82549062c0a5e8809cd12e52c013b01df5bdb918 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Mon, 3 Sep 2018 20:26:26 +0000 Subject: [PATCH 127/686] [clangd] Fix index-twice regression from r341242 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341337 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/FileIndex.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index aaf97bd43..105c7fdbe 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -125,7 +125,6 @@ void FileIndex::update(PathRef Path, ASTContext *AST, assert(PP); auto Slab = llvm::make_unique(); auto OccurrenceSlab = llvm::make_unique(); - auto IndexResults = indexAST(*AST, PP, TopLevelDecls, URISchemes); std::tie(*Slab, *OccurrenceSlab) = indexAST(*AST, PP, TopLevelDecls, URISchemes); FSymbols.update(Path, std::move(Slab), std::move(OccurrenceSlab)); From e0186bc802938f9557dfec2aada76448958887f2 Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Tue, 4 Sep 2018 12:17:10 +0000 Subject: [PATCH 128/686] Remove lambda default parameter to silence -Wpedantic warning. NFCI. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341362 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 8a1089966..89e8acbac 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -1414,7 +1414,7 @@ class CodeCompleteFlow { llvm::DenseMap BundleLookup; auto AddToBundles = [&](const CodeCompletionResult *SemaResult, const Symbol *IndexResult, - bool IsOverride = false) { + bool IsOverride) { CompletionCandidate C; C.SemaResult = SemaResult; C.IndexResult = IndexResult; @@ -1446,7 +1446,7 @@ class CodeCompleteFlow { }; // Emit all Sema results, merging them with Index results if possible. for (auto &SemaResult : Recorder->Results) - AddToBundles(&SemaResult, CorrespondingIndexResult(SemaResult)); + AddToBundles(&SemaResult, CorrespondingIndexResult(SemaResult), false); // Handle OverrideResults the same way we deal with SemaResults. Since these // results use the same structs as a SemaResult it is safe to do that, but // we need to make sure we dont' duplicate things in future if Sema starts @@ -1458,7 +1458,7 @@ class CodeCompleteFlow { for (const auto &IndexResult : IndexResults) { if (UsedIndexResults.count(&IndexResult)) continue; - AddToBundles(/*SemaResult=*/nullptr, &IndexResult); + AddToBundles(/*SemaResult=*/nullptr, &IndexResult, false); } // We only keep the best N results at any time, in "native" format. TopN Top( From d13049ab30f9a3576abddea11b905b9838c7ae2d Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 4 Sep 2018 14:39:56 +0000 Subject: [PATCH 129/686] [clangd] SymbolOccurrences -> Refs and cleanup Summary: A few things that I noticed while merging the SwapIndex patch: - SymbolOccurrences and particularly SymbolOccurrenceSlab are unwieldy names, and these names appear *a lot*. Ref, RefSlab, etc seem clear enough and read/format much better. - The asymmetry between SymbolSlab and RefSlab (build() vs freeze()) is confusing and irritating, and doesn't even save much code. Avoiding RefSlab::Builder was my idea, but it was a bad one; add it. - DenseMap> seems like a reasonable compromise for constructing MemIndex - and means many less wasted allocations than the current DenseMap> for FileIndex, and none for slabs. - RefSlab::find() is not actually used for anything, so we can throw away the DenseMap and keep the representation much more compact. - A few naming/consistency fixes: e.g. Slabs,Refs -> Symbols,Refs. Reviewers: ioeric Subscribers: ilya-biryukov, MaskRay, jkorous, mgrang, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51605 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341368 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 4 +- clangd/index/FileIndex.cpp | 89 ++++++++------ clangd/index/FileIndex.h | 16 +-- clangd/index/Index.cpp | 52 ++++---- clangd/index/Index.h | 141 +++++++++------------- clangd/index/MemIndex.cpp | 29 ++--- clangd/index/MemIndex.h | 36 +++--- clangd/index/Merge.cpp | 34 +++--- clangd/index/Merge.h | 4 +- clangd/index/SymbolCollector.cpp | 25 ++-- clangd/index/SymbolCollector.h | 24 ++-- clangd/index/dex/DexIndex.cpp | 7 +- clangd/index/dex/DexIndex.h | 5 +- clangd/tool/ClangdMain.cpp | 3 +- unittests/clangd/CodeCompleteTests.cpp | 8 +- unittests/clangd/FileIndexTests.cpp | 84 ++++++------- unittests/clangd/IndexTests.cpp | 77 ++++++------ unittests/clangd/SymbolCollectorTests.cpp | 74 ++++++------ unittests/clangd/TestTU.cpp | 4 +- 19 files changed, 328 insertions(+), 388 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 30bef3ca7..5bd2a910d 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -106,7 +106,7 @@ class ClangdServer::DynamicIndex { // Contains information from each file's preamble only. // These are large, but update fairly infrequently (preambles are stable). // Missing information: - // - symbol occurrences (these are always "from the main file") + // - symbol refs (these are always "from the main file") // - definition locations in the main file // // FIXME: Because the preambles for different TUs have large overlap and @@ -118,7 +118,7 @@ class ClangdServer::DynamicIndex { // Contains information from each file's main AST. // These are updated frequently (on file change), but are relatively small. // Mostly contains: - // - occurrences of symbols declared in the preamble and referenced from main + // - refs to symbols declared in the preamble and referenced from main // - symbols declared both in the main file and the preamble // (Note that symbols *only* in the main file are not indexed). FileIndex MainFileIdx; diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 105c7fdbe..1b430e66a 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -16,7 +16,7 @@ namespace clang { namespace clangd { -std::pair +std::pair indexAST(ASTContext &AST, std::shared_ptr PP, llvm::Optional> TopLevelDecls, llvm::ArrayRef URISchemes) { @@ -45,12 +45,12 @@ indexAST(ASTContext &AST, std::shared_ptr PP, DeclsToIndex.assign(AST.getTranslationUnitDecl()->decls().begin(), AST.getTranslationUnitDecl()->decls().end()); - // We only collect occurrences when indexing main AST. + // We only collect refs when indexing main AST. // FIXME: this is a hacky way to detect whether we are indexing preamble AST // or main AST, we should make it explicitly. bool IsIndexMainAST = TopLevelDecls.hasValue(); if (IsIndexMainAST) - CollectorOpts.OccurrenceFilter = AllOccurrenceKinds; + CollectorOpts.RefFilter = RefKind::All; SymbolCollector Collector(std::move(CollectorOpts)); Collector.setPreprocessor(PP); @@ -61,13 +61,13 @@ indexAST(ASTContext &AST, std::shared_ptr PP, std::string FileName = MainFileEntry ? MainFileEntry->getName() : ""; auto Syms = Collector.takeSymbols(); - auto Occurrences = Collector.takeOccurrences(); + auto Refs = Collector.takeRefs(); vlog("index {0}AST for {1}: \n" " symbol slab: {2} symbols, {3} bytes\n" - " occurrence slab: {4} symbols, {5} bytes", + " ref slab: {4} symbols, {5} bytes", IsIndexMainAST ? "Main" : "Preamble", FileName, Syms.size(), - Syms.bytes(), Occurrences.size(), Occurrences.bytes()); - return {std::move(Syms), std::move(Occurrences)}; + Syms.bytes(), Refs.size(), Refs.bytes()); + return {std::move(Syms), std::move(Refs)}; } FileIndex::FileIndex(std::vector URISchemes) @@ -75,45 +75,63 @@ FileIndex::FileIndex(std::vector URISchemes) reset(FSymbols.buildMemIndex()); } -void FileSymbols::update(PathRef Path, std::unique_ptr Slab, - std::unique_ptr Occurrences) { +void FileSymbols::update(PathRef Path, std::unique_ptr Symbols, + std::unique_ptr Refs) { std::lock_guard Lock(Mutex); - if (!Slab) - FileToSlabs.erase(Path); + if (!Symbols) + FileToSymbols.erase(Path); else - FileToSlabs[Path] = std::move(Slab); - if (!Occurrences) - FileToOccurrenceSlabs.erase(Path); + FileToSymbols[Path] = std::move(Symbols); + if (!Refs) + FileToRefs.erase(Path); else - FileToOccurrenceSlabs[Path] = std::move(Occurrences); + FileToRefs[Path] = std::move(Refs); } std::unique_ptr FileSymbols::buildMemIndex() { - std::vector> Slabs; - std::vector> OccurrenceSlabs; + std::vector> SymbolSlabs; + std::vector> RefSlabs; { std::lock_guard Lock(Mutex); - for (const auto &FileAndSlab : FileToSlabs) - Slabs.push_back(FileAndSlab.second); - for (const auto &FileAndOccurrenceSlab : FileToOccurrenceSlabs) - OccurrenceSlabs.push_back(FileAndOccurrenceSlab.second); + for (const auto &FileAndSymbols : FileToSymbols) + SymbolSlabs.push_back(FileAndSymbols.second); + for (const auto &FileAndRefs : FileToRefs) + RefSlabs.push_back(FileAndRefs.second); } std::vector AllSymbols; - for (const auto &Slab : Slabs) + for (const auto &Slab : SymbolSlabs) for (const auto &Sym : *Slab) AllSymbols.push_back(&Sym); - MemIndex::OccurrenceMap AllOccurrences; - for (const auto &OccurrenceSlab : OccurrenceSlabs) - for (const auto &Sym : *OccurrenceSlab) { - auto &Entry = AllOccurrences[Sym.first]; - for (const auto &Occ : Sym.second) - Entry.push_back(&Occ); + + std::vector RefsStorage; // Contiguous ranges for each SymbolID. + llvm::DenseMap> AllRefs; + { + llvm::DenseMap> MergedRefs; + size_t Count = 0; + for (const auto &RefSlab : RefSlabs) + for (const auto &Sym : *RefSlab) { + MergedRefs[Sym.first].append(Sym.second.begin(), Sym.second.end()); + Count += Sym.second.size(); + } + RefsStorage.reserve(Count); + AllRefs.reserve(MergedRefs.size()); + for (auto &Sym : MergedRefs) { + auto &SymRefs = Sym.second; + // Sorting isn't required, but yields more stable results over rebuilds. + std::sort(SymRefs.begin(), SymRefs.end()); + std::copy(SymRefs.begin(), SymRefs.end(), back_inserter(RefsStorage)); + AllRefs.try_emplace( + Sym.first, + ArrayRef(&RefsStorage[RefsStorage.size() - SymRefs.size()], + SymRefs.size())); } + } - // Index must keep the slabs alive. + // Index must keep the slabs and contiguous ranges alive. return llvm::make_unique( - llvm::make_pointee_range(AllSymbols), std::move(AllOccurrences), - std::make_pair(std::move(Slabs), std::move(OccurrenceSlabs))); + llvm::make_pointee_range(AllSymbols), std::move(AllRefs), + std::make_tuple(std::move(SymbolSlabs), std::move(RefSlabs), + std::move(RefsStorage))); } void FileIndex::update(PathRef Path, ASTContext *AST, @@ -123,11 +141,10 @@ void FileIndex::update(PathRef Path, ASTContext *AST, FSymbols.update(Path, nullptr, nullptr); } else { assert(PP); - auto Slab = llvm::make_unique(); - auto OccurrenceSlab = llvm::make_unique(); - std::tie(*Slab, *OccurrenceSlab) = - indexAST(*AST, PP, TopLevelDecls, URISchemes); - FSymbols.update(Path, std::move(Slab), std::move(OccurrenceSlab)); + auto Contents = indexAST(*AST, PP, TopLevelDecls, URISchemes); + FSymbols.update(Path, + llvm::make_unique(std::move(Contents.first)), + llvm::make_unique(std::move(Contents.second))); } reset(FSymbols.buildMemIndex()); } diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index 471fa4c9e..9c616231e 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -39,10 +39,10 @@ namespace clangd { /// locking when we swap or obtain references to snapshots. class FileSymbols { public: - /// Updates all symbols and occurrences in a file. + /// Updates all symbols and refs in a file. /// If either is nullptr, corresponding data for \p Path will be removed. void update(PathRef Path, std::unique_ptr Slab, - std::unique_ptr Occurrences); + std::unique_ptr Refs); // The index keeps the symbols alive. std::unique_ptr buildMemIndex(); @@ -50,10 +50,10 @@ class FileSymbols { private: mutable std::mutex Mutex; - /// Stores the latest snapshots for all active files. - llvm::StringMap> FileToSlabs; - /// Stores the latest occurrence slabs for all active files. - llvm::StringMap> FileToOccurrenceSlabs; + /// Stores the latest symbol snapshots for all active files. + llvm::StringMap> FileToSymbols; + /// Stores the latest ref snapshots for all active files. + llvm::StringMap> FileToRefs; }; /// This manages symbols from files and an in-memory index on all symbols. @@ -81,12 +81,12 @@ class FileIndex : public SwapIndex { std::vector URISchemes; }; -/// Retrieves symbols and symbol occurrences in \p AST. +/// Retrieves symbols and refs in \p AST. /// Exposed to assist in unit tests. /// If URISchemes is empty, the default schemes in SymbolCollector will be used. /// If \p TopLevelDecls is set, only these decls are indexed. Otherwise, all top /// level decls obtained from \p AST are indexed. -std::pair +std::pair indexAST(ASTContext &AST, std::shared_ptr PP, llvm::Optional> TopLevelDecls = llvm::None, llvm::ArrayRef URISchemes = {}); diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index eaa287ef0..52eb42799 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -122,8 +122,8 @@ SymbolSlab SymbolSlab::Builder::build() && { return SymbolSlab(std::move(NewArena), std::move(Symbols)); } -raw_ostream &operator<<(raw_ostream &OS, SymbolOccurrenceKind K) { - if (K == SymbolOccurrenceKind::Unknown) +raw_ostream &operator<<(raw_ostream &OS, RefKind K) { + if (K == RefKind::Unknown) return OS << "Unknown"; static const std::vector Messages = {"Decl", "Def", "Ref"}; bool VisitedOnce = false; @@ -138,31 +138,32 @@ raw_ostream &operator<<(raw_ostream &OS, SymbolOccurrenceKind K) { return OS; } -llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const SymbolOccurrence &Occurrence) { - OS << Occurrence.Location << ":" << Occurrence.Kind; - return OS; +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Ref &R) { + return OS << R.Location << ":" << R.Kind; } -void SymbolOccurrenceSlab::insert(const SymbolID &SymID, - const SymbolOccurrence &Occurrence) { - assert(!Frozen && - "Can't insert a symbol occurrence after the slab has been frozen!"); - auto &SymOccurrences = Occurrences[SymID]; - SymOccurrences.push_back(Occurrence); - SymOccurrences.back().Location.FileURI = - UniqueStrings.save(Occurrence.Location.FileURI); +void RefSlab::Builder::insert(const SymbolID &ID, const Ref &S) { + auto &M = Refs[ID]; + M.push_back(S); + M.back().Location.FileURI = UniqueStrings.save(M.back().Location.FileURI); } -void SymbolOccurrenceSlab::freeze() { - // Deduplicate symbol occurrences. - for (auto &IDAndOccurrence : Occurrences) { - auto &Occurrence = IDAndOccurrence.getSecond(); - std::sort(Occurrence.begin(), Occurrence.end()); - Occurrence.erase(std::unique(Occurrence.begin(), Occurrence.end()), - Occurrence.end()); +RefSlab RefSlab::Builder::build() && { + // We can reuse the arena, as it only has unique strings and we need them all. + // Reallocate refs on the arena to reduce waste and indirections when reading. + std::vector>> Result; + Result.reserve(Refs.size()); + for (auto &Sym : Refs) { + auto &SymRefs = Sym.second; + std::sort(SymRefs.begin(), SymRefs.end()); + // TODO: do we really need to dedup? + SymRefs.erase(std::unique(SymRefs.begin(), SymRefs.end()), SymRefs.end()); + + auto *Array = Arena.Allocate(SymRefs.size()); + std::uninitialized_copy(SymRefs.begin(), SymRefs.end(), Array); + Result.emplace_back(Sym.first, ArrayRef(Array, SymRefs.size())); } - Frozen = true; + return RefSlab(std::move(Result), std::move(Arena)); } void SwapIndex::reset(std::unique_ptr Index) { @@ -187,10 +188,9 @@ void SwapIndex::lookup(const LookupRequest &R, llvm::function_ref CB) const { return snapshot()->lookup(R, CB); } -void SwapIndex::findOccurrences( - const OccurrencesRequest &R, - llvm::function_ref CB) const { - return snapshot()->findOccurrences(R, CB); +void SwapIndex::refs(const RefsRequest &R, + llvm::function_ref CB) const { + return snapshot()->refs(R, CB); } size_t SwapIndex::estimateMemoryUsage() const { return snapshot()->estimateMemoryUsage(); diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 771261a8a..c1778e189 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -242,7 +242,6 @@ struct Symbol { /// any definition. llvm::SmallVector IncludeHeaders; - // FIXME: add all occurrences support. // FIXME: add extra fields for index scoring signals. }; llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Symbol &S); @@ -309,109 +308,86 @@ class SymbolSlab { std::vector Symbols; // Sorted by SymbolID to allow lookup. }; -// Describes the kind of a symbol occurrence. +// Describes the kind of a cross-reference. // // This is a bitfield which can be combined from different kinds. -enum class SymbolOccurrenceKind : uint8_t { +enum class RefKind : uint8_t { Unknown = 0, Declaration = static_cast(index::SymbolRole::Declaration), Definition = static_cast(index::SymbolRole::Definition), Reference = static_cast(index::SymbolRole::Reference), + All = Declaration | Definition | Reference, }; -inline SymbolOccurrenceKind operator|(SymbolOccurrenceKind L, - SymbolOccurrenceKind R) { - return static_cast(static_cast(L) | - static_cast(R)); +inline RefKind operator|(RefKind L, RefKind R) { + return static_cast(static_cast(L) | + static_cast(R)); } -inline SymbolOccurrenceKind &operator|=(SymbolOccurrenceKind &L, - SymbolOccurrenceKind R) { - return L = L | R; +inline RefKind &operator|=(RefKind &L, RefKind R) { return L = L | R; } +inline RefKind operator&(RefKind A, RefKind B) { + return static_cast(static_cast(A) & + static_cast(B)); } -inline SymbolOccurrenceKind operator&(SymbolOccurrenceKind A, - SymbolOccurrenceKind B) { - return static_cast(static_cast(A) & - static_cast(B)); -} -llvm::raw_ostream &operator<<(llvm::raw_ostream &, SymbolOccurrenceKind); -static const SymbolOccurrenceKind AllOccurrenceKinds = - SymbolOccurrenceKind::Declaration | SymbolOccurrenceKind::Definition | - SymbolOccurrenceKind::Reference; +llvm::raw_ostream &operator<<(llvm::raw_ostream &, RefKind); -// Represents a symbol occurrence in the source file. It could be a -// declaration/definition/reference occurrence. +// Represents a symbol occurrence in the source file. +// Despite the name, it could be a declaration/definition/reference. // // WARNING: Location does not own the underlying data - Copies are shallow. -struct SymbolOccurrence { - // The location of the occurrence. +struct Ref { + // The source location where the symbol is named. SymbolLocation Location; - SymbolOccurrenceKind Kind = SymbolOccurrenceKind::Unknown; + RefKind Kind = RefKind::Unknown; }; -inline bool operator<(const SymbolOccurrence &L, const SymbolOccurrence &R) { +inline bool operator<(const Ref &L, const Ref &R) { return std::tie(L.Location, L.Kind) < std::tie(R.Location, R.Kind); } -inline bool operator==(const SymbolOccurrence &L, const SymbolOccurrence &R) { +inline bool operator==(const Ref &L, const Ref &R) { return std::tie(L.Location, L.Kind) == std::tie(R.Location, R.Kind); } -llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const SymbolOccurrence &Occurrence); +llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Ref &); -// An efficient structure of storing large set of symbol occurrences in memory. +// An efficient structure of storing large set of symbol references in memory. // Filenames are deduplicated. -class SymbolOccurrenceSlab { +class RefSlab { public: - using const_iterator = - llvm::DenseMap>::const_iterator; + using value_type = std::pair>; + using const_iterator = std::vector::const_iterator; using iterator = const_iterator; - SymbolOccurrenceSlab() : UniqueStrings(Arena) {} - - static SymbolOccurrenceSlab createEmpty() { - SymbolOccurrenceSlab Empty; - Empty.freeze(); - return Empty; - } - - // Define move semantics for the slab, allowing assignment from an rvalue. - // Implicit move assignment is deleted by the compiler because - // StringSaver has a reference type member. - SymbolOccurrenceSlab(SymbolOccurrenceSlab &&Slab) = default; - SymbolOccurrenceSlab &operator=(SymbolOccurrenceSlab &&RHS) { - assert(RHS.Frozen && - "SymbolOcucrrenceSlab must be frozen when move assigned!"); - Arena = std::move(RHS.Arena); - Frozen = true; - Occurrences = std::move(RHS.Occurrences); - return *this; - } + RefSlab() = default; + RefSlab(RefSlab &&Slab) = default; + RefSlab &operator=(RefSlab &&RHS) = default; - const_iterator begin() const { return Occurrences.begin(); } - const_iterator end() const { return Occurrences.end(); } + const_iterator begin() const { return Refs.begin(); } + const_iterator end() const { return Refs.end(); } + size_t size() const { return Refs.size(); } size_t bytes() const { - return sizeof(*this) + Arena.getTotalMemory() + Occurrences.getMemorySize(); + return sizeof(*this) + Arena.getTotalMemory() + + sizeof(value_type) * Refs.size(); } - size_t size() const { return Occurrences.size(); } - - // Adds a symbol occurrence. - // This is a deep copy: underlying FileURI will be owned by the slab. - void insert(const SymbolID &SymID, const SymbolOccurrence &Occurrence); - - llvm::ArrayRef find(const SymbolID &ID) const { - assert(Frozen && "SymbolOccurrenceSlab must be frozen before looking up!"); - auto It = Occurrences.find(ID); - if (It == Occurrences.end()) - return {}; - return It->second; - } + // RefSlab::Builder is a mutable container that can 'freeze' to RefSlab. + class Builder { + public: + Builder() : UniqueStrings(Arena) {} + // Adds a ref to the slab. Deep copy: Strings will be owned by the slab. + void insert(const SymbolID &ID, const Ref &S); + // Consumes the builder to finalize the slab. + RefSlab build() &&; - void freeze(); + private: + llvm::BumpPtrAllocator Arena; + llvm::UniqueStringSaver UniqueStrings; // Contents on the arena. + llvm::DenseMap> Refs; + }; private: - bool Frozen = false; + RefSlab(std::vector Refs, llvm::BumpPtrAllocator Arena) + : Arena(std::move(Arena)), Refs(std::move(Refs)) {} + llvm::BumpPtrAllocator Arena; - llvm::UniqueStringSaver UniqueStrings; - llvm::DenseMap> Occurrences; + std::vector Refs; }; struct FuzzyFindRequest { @@ -447,9 +423,9 @@ struct LookupRequest { llvm::DenseSet IDs; }; -struct OccurrencesRequest { +struct RefsRequest { llvm::DenseSet IDs; - SymbolOccurrenceKind Filter = AllOccurrenceKinds; + RefKind Filter = RefKind::All; }; /// Interface for symbol indexes that can be used for searching or @@ -474,15 +450,13 @@ class SymbolIndex { lookup(const LookupRequest &Req, llvm::function_ref Callback) const = 0; - /// CrossReference finds all symbol occurrences (e.g. references, - /// declarations, definitions) and applies \p Callback on each result. - /// - /// Results are returned in arbitrary order. + /// Finds all occurrences (e.g. references, declarations, definitions) of a + /// symbol and applies \p Callback on each result. /// + /// Results should be returned in arbitrary order. /// The returned result must be deep-copied if it's used outside Callback. - virtual void findOccurrences( - const OccurrencesRequest &Req, - llvm::function_ref Callback) const = 0; + virtual void refs(const RefsRequest &Req, + llvm::function_ref Callback) const = 0; /// Returns estimated size of index (in bytes). // FIXME(kbobyrev): Currently, this only returns the size of index itself @@ -505,9 +479,8 @@ class SwapIndex : public SymbolIndex { llvm::function_ref) const override; void lookup(const LookupRequest &, llvm::function_ref) const override; - void findOccurrences( - const OccurrencesRequest &, - llvm::function_ref) const override; + void refs(const RefsRequest &, + llvm::function_ref) const override; size_t estimateMemoryUsage() const override; private: diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index 43629f486..2a7cd601b 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -15,16 +15,9 @@ namespace clang { namespace clangd { -std::unique_ptr MemIndex::build(SymbolSlab Slab, - SymbolOccurrenceSlab Occurrences) { - OccurrenceMap M; - for (const auto &SymbolAndOccurrences : Occurrences) { - auto &Entry = M[SymbolAndOccurrences.first]; - for (const auto &Occurrence : SymbolAndOccurrences.second) - Entry.push_back(&Occurrence); - } - auto Data = std::make_pair(std::move(Slab), std::move(Occurrences)); - return llvm::make_unique(Data.first, std::move(M), std::move(Data)); +std::unique_ptr MemIndex::build(SymbolSlab Slab, RefSlab Refs) { + auto Data = std::make_pair(std::move(Slab), std::move(Refs)); + return llvm::make_unique(Data.first, Data.second, std::move(Data)); } bool MemIndex::fuzzyFind( @@ -67,17 +60,15 @@ void MemIndex::lookup(const LookupRequest &Req, } } -void MemIndex::findOccurrences( - const OccurrencesRequest &Req, - llvm::function_ref Callback) const { +void MemIndex::refs(const RefsRequest &Req, + llvm::function_ref Callback) const { for (const auto &ReqID : Req.IDs) { - auto FoundOccurrences = Occurrences.find(ReqID); - if (FoundOccurrences == Occurrences.end()) + auto SymRefs = Refs.find(ReqID); + if (SymRefs == Refs.end()) continue; - for (const auto *O : FoundOccurrences->second) { - if (static_cast(Req.Filter & O->Kind)) - Callback(*O); - } + for (const auto &O : SymRefs->second) + if (static_cast(Req.Filter & O.Kind)) + Callback(O); } } diff --git a/clangd/index/MemIndex.h b/clangd/index/MemIndex.h index 3f1dcdfed..add458962 100644 --- a/clangd/index/MemIndex.h +++ b/clangd/index/MemIndex.h @@ -19,31 +19,26 @@ namespace clangd { /// MemIndex is a naive in-memory index suitable for a small set of symbols. class MemIndex : public SymbolIndex { public: - /// Maps from a symbol ID to all corresponding symbol occurrences. - /// The map doesn't own occurrence objects. - using OccurrenceMap = - llvm::DenseMap>; - MemIndex() = default; - // All symbols and occurrences must outlive this index. - // TODO: find a better type for Occurrences here. - template - MemIndex(SymbolRange &&Symbols, OccurrenceMap Occurrences) - : Occurrences(std::move(Occurrences)) { + // All symbols and refs must outlive this index. + template + MemIndex(SymbolRange &&Symbols, RefRange &&Refs) { for (const Symbol &S : Symbols) Index[S.ID] = &S; + for (const std::pair> &R : Refs) + this->Refs.try_emplace(R.first, R.second.begin(), R.second.end()); } // Symbols are owned by BackingData, Index takes ownership. - template - MemIndex(Range &&Symbols, OccurrenceMap Occurrences, Payload &&BackingData) - : MemIndex(std::forward(Symbols), std::move(Occurrences)) { + template + MemIndex(SymbolRange &&Symbols, RefRange &&Refs, Payload &&BackingData) + : MemIndex(std::forward(Symbols), + std::forward(Refs)) { KeepAlive = std::shared_ptr( std::make_shared(std::move(BackingData)), nullptr); } - /// Builds an index from a slab. The index takes ownership of the data. - static std::unique_ptr build(SymbolSlab Slab, - SymbolOccurrenceSlab Occurrences); + /// Builds an index from slabs. The index takes ownership of the data. + static std::unique_ptr build(SymbolSlab Symbols, RefSlab Refs); bool fuzzyFind(const FuzzyFindRequest &Req, @@ -52,17 +47,16 @@ class MemIndex : public SymbolIndex { void lookup(const LookupRequest &Req, llvm::function_ref Callback) const override; - void findOccurrences(const OccurrencesRequest &Req, - llvm::function_ref - Callback) const override; + void refs(const RefsRequest &Req, + llvm::function_ref Callback) const override; size_t estimateMemoryUsage() const override; private: // Index is a set of symbols that are deduplicated by symbol IDs. llvm::DenseMap Index; - // A map from symbol ID to symbol occurrences, support query by IDs. - OccurrenceMap Occurrences; + // A map from symbol ID to symbol refs, support query by IDs. + llvm::DenseMap> Refs; std::shared_ptr KeepAlive; // poor man's move-only std::any }; diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index 6c384bfd6..5f79cffa5 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -7,12 +7,12 @@ // //===----------------------------------------------------------------------===// -#include - #include "Merge.h" #include "../Logger.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/raw_ostream.h" +#include namespace clang { namespace clangd { @@ -78,28 +78,24 @@ class MergedIndex : public SymbolIndex { Callback(*Sym); } - void findOccurrences(const OccurrencesRequest &Req, - llvm::function_ref - Callback) const override { - // We don't want duplicated occurrences from the static/dynamic indexes, - // and we can't reliably duplicate them because occurrence offsets may - // differ slightly. - // We consider the dynamic index authoritative and report all its - // occurrences, and only report static index occurrences from other files. + void refs(const RefsRequest &Req, + llvm::function_ref Callback) const override { + // We don't want duplicated refs from the static/dynamic indexes, + // and we can't reliably duplicate them because offsets may differ slightly. + // We consider the dynamic index authoritative and report all its refs, + // and only report static index refs from other files. // // FIXME: The heuristic fails if the dynamic index contains a file, but all - // occurrences were removed (we will report stale ones from the static - // index). Ultimately we should explicit check which index has the file - // instead. - std::set DynamicIndexFileURIs; - Dynamic->findOccurrences(Req, [&](const SymbolOccurrence &O) { + // refs were removed (we will report stale ones from the static index). + // Ultimately we should explicit check which index has the file instead. + llvm::StringSet<> DynamicIndexFileURIs; + Dynamic->refs(Req, [&](const Ref &O) { DynamicIndexFileURIs.insert(O.Location.FileURI); Callback(O); }); - Static->findOccurrences(Req, [&](const SymbolOccurrence &O) { - if (DynamicIndexFileURIs.count(O.Location.FileURI)) - return; - Callback(O); + Static->refs(Req, [&](const Ref &O) { + if (!DynamicIndexFileURIs.count(O.Location.FileURI)) + Callback(O); }); } diff --git a/clangd/index/Merge.h b/clangd/index/Merge.h index acf9ce1d2..ca496c2bf 100644 --- a/clangd/index/Merge.h +++ b/clangd/index/Merge.h @@ -26,8 +26,8 @@ Symbol mergeSymbol(const Symbol &L, const Symbol &R); // The returned index attempts to combine results, and avoid duplicates. // // FIXME: We don't have a mechanism in Index to track deleted symbols and -// occurrences in dirty files, so the merged index may return stale symbols -// and occurrences from Static index. +// refs in dirty files, so the merged index may return stale symbols +// and refs from Static index. std::unique_ptr mergeIndex(const SymbolIndex *Dynamic, const SymbolIndex *Static); diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp index 912c65085..7a83243f8 100644 --- a/clangd/index/SymbolCollector.cpp +++ b/clangd/index/SymbolCollector.cpp @@ -231,9 +231,8 @@ bool isPreferredDeclaration(const NamedDecl &ND, index::SymbolRoleSet Roles) { match(decl(isExpansionInMainFile()), ND, ND.getASTContext()).empty(); } -SymbolOccurrenceKind toOccurrenceKind(index::SymbolRoleSet Roles) { - return static_cast( - static_cast(AllOccurrenceKinds) & Roles); +RefKind toRefKind(index::SymbolRoleSet Roles) { + return static_cast(static_cast(RefKind::All) & Roles); } } // namespace @@ -326,9 +325,9 @@ bool SymbolCollector::handleDeclOccurence( SM.getFileID(SpellingLoc) == SM.getMainFileID()) ReferencedDecls.insert(ND); - if ((static_cast(Opts.OccurrenceFilter) & Roles) && + if ((static_cast(Opts.RefFilter) & Roles) && SM.getFileID(SpellingLoc) == SM.getMainFileID()) - DeclOccurrences[ND].emplace_back(SpellingLoc, Roles); + DeclRefs[ND].emplace_back(SpellingLoc, Roles); // Don't continue indexing if this is a mere reference. if (!(Roles & static_cast(index::SymbolRole::Declaration) || @@ -459,18 +458,18 @@ void SymbolCollector::finish() { if (auto MainFileURI = toURI(SM, MainFileEntry->getName(), Opts)) { std::string MainURI = *MainFileURI; - for (const auto &It : DeclOccurrences) { + for (const auto &It : DeclRefs) { if (auto ID = getSymbolID(It.first)) { if (Symbols.find(*ID)) { for (const auto &LocAndRole : It.second) { - SymbolOccurrence Occurrence; + Ref R; auto Range = getTokenRange(LocAndRole.first, SM, ASTCtx->getLangOpts()); - Occurrence.Location.Start = Range.first; - Occurrence.Location.End = Range.second; - Occurrence.Location.FileURI = MainURI; - Occurrence.Kind = toOccurrenceKind(LocAndRole.second); - SymbolOccurrences.insert(*ID, Occurrence); + R.Location.Start = Range.first; + R.Location.End = Range.second; + R.Location.FileURI = MainURI; + R.Kind = toRefKind(LocAndRole.second); + Refs.insert(*ID, R); } } } @@ -481,7 +480,7 @@ void SymbolCollector::finish() { ReferencedDecls.clear(); ReferencedMacros.clear(); - DeclOccurrences.clear(); + DeclRefs.clear(); } const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, diff --git a/clangd/index/SymbolCollector.h b/clangd/index/SymbolCollector.h index ab88c066d..db0e3074e 100644 --- a/clangd/index/SymbolCollector.h +++ b/clangd/index/SymbolCollector.h @@ -52,10 +52,9 @@ class SymbolCollector : public index::IndexDataConsumer { const CanonicalIncludes *Includes = nullptr; // Populate the Symbol.References field. bool CountReferences = false; - /// The symbol occurrence kind that will be collected. - /// If not set (Unknown), SymbolCollector will not collect any symbol - /// occurrences. - SymbolOccurrenceKind OccurrenceFilter = SymbolOccurrenceKind::Unknown; + /// The symbol ref kinds that will be collected. + /// If not set, SymbolCollector will not collect refs. + RefKind RefFilter = RefKind::Unknown; // Every symbol collected will be stamped with this origin. SymbolOrigin Origin = SymbolOrigin::Unknown; /// Collect macros. @@ -89,11 +88,7 @@ class SymbolCollector : public index::IndexDataConsumer { SourceLocation Loc) override; SymbolSlab takeSymbols() { return std::move(Symbols).build(); } - - SymbolOccurrenceSlab takeOccurrences() { - SymbolOccurrences.freeze(); - return std::move(SymbolOccurrences); - } + RefSlab takeRefs() { return std::move(Refs).build(); } void finish() override; @@ -103,21 +98,20 @@ class SymbolCollector : public index::IndexDataConsumer { // All Symbols collected from the AST. SymbolSlab::Builder Symbols; - // All symbol occurrences collected from the AST. - // Only symbols declared in preamble (from #include) and references from the + // All refs collected from the AST. + // Only symbols declared in preamble (from #include) and referenced from the // main file will be included. - SymbolOccurrenceSlab SymbolOccurrences; + RefSlab::Builder Refs; ASTContext *ASTCtx; std::shared_ptr PP; std::shared_ptr CompletionAllocator; std::unique_ptr CompletionTUInfo; Options Opts; - using DeclOccurrence = std::pair; + using DeclRef = std::pair; // Symbols referenced from the current TU, flushed on finish(). llvm::DenseSet ReferencedDecls; llvm::DenseSet ReferencedMacros; - llvm::DenseMap> - DeclOccurrences; + llvm::DenseMap> DeclRefs; // Maps canonical declaration provided by clang to canonical declaration for // an index symbol, if clangd prefers a different declaration than that // provided by clang. For example, friend declaration might be considered diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/DexIndex.cpp index 3275cb7bb..ed1f60b9b 100644 --- a/clangd/index/dex/DexIndex.cpp +++ b/clangd/index/dex/DexIndex.cpp @@ -144,10 +144,9 @@ void DexIndex::lookup(const LookupRequest &Req, } } -void DexIndex::findOccurrences( - const OccurrencesRequest &Req, - llvm::function_ref Callback) const { - log("findOccurrences is not implemented."); +void DexIndex::refs(const RefsRequest &Req, + llvm::function_ref Callback) const { + log("refs is not implemented."); } size_t DexIndex::estimateMemoryUsage() const { diff --git a/clangd/index/dex/DexIndex.h b/clangd/index/dex/DexIndex.h index ad379136b..3244e62ec 100644 --- a/clangd/index/dex/DexIndex.h +++ b/clangd/index/dex/DexIndex.h @@ -64,9 +64,8 @@ class DexIndex : public SymbolIndex { void lookup(const LookupRequest &Req, llvm::function_ref Callback) const override; - void findOccurrences(const OccurrencesRequest &Req, - llvm::function_ref - Callback) const override; + void refs(const RefsRequest &Req, + llvm::function_ref Callback) const override; size_t estimateMemoryUsage() const override; diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index cf5c7fac0..c9ceb9aa8 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -54,8 +54,7 @@ std::unique_ptr buildStaticIndex(llvm::StringRef YamlSymbolFile) { SymsBuilder.insert(Sym); return UseDex ? dex::DexIndex::build(std::move(SymsBuilder).build()) - : MemIndex::build(std::move(SymsBuilder).build(), - SymbolOccurrenceSlab::createEmpty()); + : MemIndex::build(std::move(SymsBuilder).build(), RefSlab()); } } // namespace diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index ca2f449bf..3a6fe9ba3 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -82,8 +82,7 @@ std::unique_ptr memIndex(std::vector Symbols) { SymbolSlab::Builder Slab; for (const auto &Sym : Symbols) Slab.insert(Sym); - return MemIndex::build(std::move(Slab).build(), - SymbolOccurrenceSlab::createEmpty()); + return MemIndex::build(std::move(Slab).build(), RefSlab()); } CodeCompleteResult completions(ClangdServer &Server, StringRef TestCode, @@ -974,9 +973,8 @@ class IndexRequestCollector : public SymbolIndex { void lookup(const LookupRequest &, llvm::function_ref) const override {} - void findOccurrences(const OccurrencesRequest &Req, - llvm::function_ref - Callback) const override {} + void refs(const RefsRequest &, + llvm::function_ref) const override {} // This is incorrect, but IndexRequestCollector is not an actual index and it // isn't used in production code. diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index debca6ac6..096ccd9e6 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -19,10 +19,13 @@ #include "clang/Tooling/CompilationDatabase.h" #include "gtest/gtest.h" -using testing::UnorderedElementsAre; +using testing::_; using testing::AllOf; +using testing::ElementsAre; +using testing::Pair; +using testing::UnorderedElementsAre; -MATCHER_P(OccurrenceRange, Range, "") { +MATCHER_P(RefRange, Range, "") { return std::tie(arg.Location.Start.Line, arg.Location.Start.Column, arg.Location.End.Line, arg.Location.End.Column) == std::tie(Range.start.line, Range.start.character, Range.end.line, @@ -32,8 +35,11 @@ MATCHER_P(FileURI, F, "") { return arg.Location.FileURI == F; } namespace clang { namespace clangd { - namespace { +testing::Matcher +RefsAre(std::vector> Matchers) { + return ElementsAre(testing::Pair(_, UnorderedElementsAreArray(Matchers))); +} Symbol symbol(llvm::StringRef ID) { Symbol Sym; @@ -49,14 +55,13 @@ std::unique_ptr numSlab(int Begin, int End) { return llvm::make_unique(std::move(Slab).build()); } -std::unique_ptr occurrenceSlab(const SymbolID &ID, - llvm::StringRef Path) { - auto Slab = llvm::make_unique(); - SymbolOccurrence Occurrence; - Occurrence.Location.FileURI = Path; - Occurrence.Kind = SymbolOccurrenceKind::Reference; - Slab->insert(ID, Occurrence); - return Slab; +std::unique_ptr refSlab(const SymbolID &ID, llvm::StringRef Path) { + RefSlab::Builder Slab; + Ref R; + R.Location.FileURI = Path; + R.Kind = RefKind::Reference; + Slab.insert(ID, R); + return llvm::make_unique(std::move(Slab).build()); } std::vector getSymbolNames(const SymbolIndex &I, @@ -68,37 +73,29 @@ std::vector getSymbolNames(const SymbolIndex &I, return Names; } -std::vector getOccurrencePaths(const SymbolIndex &I, SymbolID ID) { - OccurrencesRequest Req; +RefSlab getRefs(const SymbolIndex &I, SymbolID ID) { + RefsRequest Req; Req.IDs = {ID}; - std::vector Paths; - I.findOccurrences(Req, [&](const SymbolOccurrence &S) { - Paths.push_back(S.Location.FileURI); - }); - return Paths; -} - -std::unique_ptr emptyOccurrence() { - auto EmptySlab = llvm::make_unique(); - EmptySlab->freeze(); - return EmptySlab; + RefSlab::Builder Slab; + I.refs(Req, [&](const Ref &S) { Slab.insert(ID, S); }); + return std::move(Slab).build(); } TEST(FileSymbolsTest, UpdateAndGet) { FileSymbols FS; EXPECT_THAT(getSymbolNames(*FS.buildMemIndex()), UnorderedElementsAre()); - FS.update("f1", numSlab(1, 3), occurrenceSlab(SymbolID("1"), "f1.cc")); + FS.update("f1", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cc")); EXPECT_THAT(getSymbolNames(*FS.buildMemIndex()), UnorderedElementsAre("1", "2", "3")); - EXPECT_THAT(getOccurrencePaths(*FS.buildMemIndex(), SymbolID("1")), - UnorderedElementsAre("f1.cc")); + EXPECT_THAT(getRefs(*FS.buildMemIndex(), SymbolID("1")), + RefsAre({FileURI("f1.cc")})); } TEST(FileSymbolsTest, Overlap) { FileSymbols FS; - FS.update("f1", numSlab(1, 3), emptyOccurrence()); - FS.update("f2", numSlab(3, 5), emptyOccurrence()); + FS.update("f1", numSlab(1, 3), nullptr); + FS.update("f2", numSlab(3, 5), nullptr); EXPECT_THAT(getSymbolNames(*FS.buildMemIndex()), UnorderedElementsAre("1", "2", "3", "4", "5")); } @@ -107,19 +104,19 @@ TEST(FileSymbolsTest, SnapshotAliveAfterRemove) { FileSymbols FS; SymbolID ID("1"); - FS.update("f1", numSlab(1, 3), occurrenceSlab(ID, "f1.cc")); + FS.update("f1", numSlab(1, 3), refSlab(ID, "f1.cc")); auto Symbols = FS.buildMemIndex(); EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3")); - EXPECT_THAT(getOccurrencePaths(*Symbols, ID), UnorderedElementsAre("f1.cc")); + EXPECT_THAT(getRefs(*Symbols, ID), RefsAre({FileURI("f1.cc")})); FS.update("f1", nullptr, nullptr); auto Empty = FS.buildMemIndex(); EXPECT_THAT(getSymbolNames(*Empty), UnorderedElementsAre()); - EXPECT_THAT(getOccurrencePaths(*Empty, ID), UnorderedElementsAre()); + EXPECT_THAT(getRefs(*Empty, ID), ElementsAre()); EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3")); - EXPECT_THAT(getOccurrencePaths(*Symbols, ID), UnorderedElementsAre("f1.cc")); + EXPECT_THAT(getRefs(*Symbols, ID), RefsAre({FileURI("f1.cc")})); } std::vector match(const SymbolIndex &I, @@ -314,7 +311,7 @@ TEST(FileIndexTest, RebuildWithPreamble) { UnorderedElementsAre("ns_in_header", "ns_in_header::func_in_header")); } -TEST(FileIndexTest, Occurrences) { +TEST(FileIndexTest, Refs) { const char *HeaderCode = "class Foo {};"; Annotations MainCode(R"cpp( void f() { @@ -325,11 +322,8 @@ TEST(FileIndexTest, Occurrences) { auto Foo = findSymbol(TestTU::withHeaderCode(HeaderCode).headerSymbols(), "Foo"); - OccurrencesRequest Request; + RefsRequest Request; Request.IDs = {Foo.ID}; - Request.Filter = SymbolOccurrenceKind::Declaration | - SymbolOccurrenceKind::Definition | - SymbolOccurrenceKind::Reference; FileIndex Index(/*URISchemes*/ {"unittest"}); // Add test.cc @@ -349,15 +343,11 @@ TEST(FileIndexTest, Occurrences) { Index.update(Test2.Filename, &AST.getASTContext(), AST.getPreprocessorPtr(), AST.getLocalTopLevelDecls()); - std::vector Results; - Index.findOccurrences( - Request, [&Results](const SymbolOccurrence &O) { Results.push_back(O); }); - - EXPECT_THAT(Results, - UnorderedElementsAre(AllOf(OccurrenceRange(MainCode.range("foo")), - FileURI("unittest:///test.cc")), - AllOf(OccurrenceRange(MainCode.range("foo")), - FileURI("unittest:///test2.cc")))); + EXPECT_THAT(getRefs(Index, Foo.ID), + RefsAre({AllOf(RefRange(MainCode.range("foo")), + FileURI("unittest:///test.cc")), + AllOf(RefRange(MainCode.range("foo")), + FileURI("unittest:///test2.cc"))})); } } // namespace diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp index 181475f2d..7742e5201 100644 --- a/unittests/clangd/IndexTests.cpp +++ b/unittests/clangd/IndexTests.cpp @@ -17,8 +17,10 @@ #include "index/Merge.h" #include "gtest/gtest.h" +using testing::_; using testing::AllOf; using testing::ElementsAre; +using testing::Pair; using testing::Pointee; using testing::UnorderedElementsAre; using namespace llvm; @@ -28,7 +30,7 @@ namespace clangd { namespace { MATCHER_P(Named, N, "") { return arg.Name == N; } -MATCHER_P(OccurrenceRange, Range, "") { +MATCHER_P(RefRange, Range, "") { return std::tie(arg.Location.Start.Line, arg.Location.Start.Column, arg.Location.End.Line, arg.Location.End.Column) == std::tie(Range.start.line, Range.start.character, Range.end.line, @@ -56,8 +58,8 @@ TEST(SwapIndexTest, OldIndexRecycled) { auto Token = std::make_shared(); std::weak_ptr WeakToken = Token; - SwapIndex S(llvm::make_unique( - SymbolSlab(), MemIndex::OccurrenceMap(), std::move(Token))); + SwapIndex S( + llvm::make_unique(SymbolSlab(), RefSlab(), std::move(Token))); EXPECT_FALSE(WeakToken.expired()); // Current MemIndex keeps it alive. S.reset(llvm::make_unique()); // Now the MemIndex is destroyed. EXPECT_TRUE(WeakToken.expired()); // So the token is too. @@ -68,12 +70,12 @@ TEST(MemIndexTest, MemIndexDeduplicate) { symbol("2") /* duplicate */}; FuzzyFindRequest Req; Req.Query = "2"; - MemIndex I(Symbols, MemIndex::OccurrenceMap()); + MemIndex I(Symbols, RefSlab()); EXPECT_THAT(match(I, Req), ElementsAre("2")); } TEST(MemIndexTest, MemIndexLimitedNumMatches) { - auto I = MemIndex::build(generateNumSymbols(0, 100), SymbolOccurrenceSlab()); + auto I = MemIndex::build(generateNumSymbols(0, 100), RefSlab()); FuzzyFindRequest Req; Req.Query = "5"; Req.MaxCandidateCount = 3; @@ -86,7 +88,7 @@ TEST(MemIndexTest, MemIndexLimitedNumMatches) { TEST(MemIndexTest, FuzzyMatch) { auto I = MemIndex::build( generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}), - SymbolOccurrenceSlab()); + RefSlab()); FuzzyFindRequest Req; Req.Query = "lol"; Req.MaxCandidateCount = 2; @@ -95,16 +97,16 @@ TEST(MemIndexTest, FuzzyMatch) { } TEST(MemIndexTest, MatchQualifiedNamesWithoutSpecificScope) { - auto I = MemIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), - SymbolOccurrenceSlab()); + auto I = + MemIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab()); FuzzyFindRequest Req; Req.Query = "y"; EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3")); } TEST(MemIndexTest, MatchQualifiedNamesWithGlobalScope) { - auto I = MemIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), - SymbolOccurrenceSlab()); + auto I = + MemIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {""}; @@ -113,8 +115,7 @@ TEST(MemIndexTest, MatchQualifiedNamesWithGlobalScope) { TEST(MemIndexTest, MatchQualifiedNamesWithOneScope) { auto I = MemIndex::build( - generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}), - SymbolOccurrenceSlab()); + generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}), RefSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; @@ -123,8 +124,7 @@ TEST(MemIndexTest, MatchQualifiedNamesWithOneScope) { TEST(MemIndexTest, MatchQualifiedNamesWithMultipleScopes) { auto I = MemIndex::build( - generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}), - SymbolOccurrenceSlab()); + generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}), RefSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::", "b::"}; @@ -132,8 +132,7 @@ TEST(MemIndexTest, MatchQualifiedNamesWithMultipleScopes) { } TEST(MemIndexTest, NoMatchNestedScopes) { - auto I = MemIndex::build(generateSymbols({"a::y1", "a::b::y2"}), - SymbolOccurrenceSlab()); + auto I = MemIndex::build(generateSymbols({"a::y1", "a::b::y2"}), RefSlab()); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; @@ -141,8 +140,7 @@ TEST(MemIndexTest, NoMatchNestedScopes) { } TEST(MemIndexTest, IgnoreCases) { - auto I = MemIndex::build(generateSymbols({"ns::ABC", "ns::abc"}), - SymbolOccurrenceSlab()); + auto I = MemIndex::build(generateSymbols({"ns::ABC", "ns::abc"}), RefSlab()); FuzzyFindRequest Req; Req.Query = "AB"; Req.Scopes = {"ns::"}; @@ -150,8 +148,7 @@ TEST(MemIndexTest, IgnoreCases) { } TEST(MemIndexTest, Lookup) { - auto I = MemIndex::build(generateSymbols({"ns::abc", "ns::xyz"}), - SymbolOccurrenceSlab()); + auto I = MemIndex::build(generateSymbols({"ns::abc", "ns::xyz"}), RefSlab()); EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::abc", "ns::xyz")); @@ -161,10 +158,8 @@ TEST(MemIndexTest, Lookup) { } TEST(MergeIndexTest, Lookup) { - auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), - SymbolOccurrenceSlab()), - J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), - SymbolOccurrenceSlab()); + auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), RefSlab()), + J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), RefSlab()); auto M = mergeIndex(I.get(), J.get()); EXPECT_THAT(lookup(*M, SymbolID("ns::A")), UnorderedElementsAre("ns::A")); EXPECT_THAT(lookup(*M, SymbolID("ns::B")), UnorderedElementsAre("ns::B")); @@ -178,10 +173,8 @@ TEST(MergeIndexTest, Lookup) { } TEST(MergeIndexTest, FuzzyFind) { - auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), - SymbolOccurrenceSlab()), - J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), - SymbolOccurrenceSlab()); + auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), RefSlab()), + J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), RefSlab()); FuzzyFindRequest Req; Req.Scopes = {"ns::"}; EXPECT_THAT(match(*mergeIndex(I.get(), J.get()), Req), @@ -234,7 +227,7 @@ TEST(MergeTest, PreferSymbolWithDefn) { EXPECT_EQ(M.Name, "right"); } -TEST(MergeIndexTest, FindOccurrences) { +TEST(MergeIndexTest, Refs) { FileIndex Dyn({"unittest"}); FileIndex StaticIndex({"unittest"}); auto MergedIndex = mergeIndex(&Dyn, &StaticIndex); @@ -258,12 +251,12 @@ TEST(MergeIndexTest, FindOccurrences) { Test.Code = "// static\nclass Foo {};"; Test.Filename = "test.cc"; auto StaticAST = Test.build(); - // Add stale occurrences for test.cc. + // Add stale refs for test.cc. StaticIndex.update(Test.Filename, &StaticAST.getASTContext(), StaticAST.getPreprocessorPtr(), StaticAST.getLocalTopLevelDecls()); - // Add occcurrences for test2.cc + // Add refs for test2.cc Annotations Test2Code(R"(class $Foo[[Foo]] {};)"); TestTU Test2; Test2.HeaderCode = HeaderCode; @@ -274,18 +267,18 @@ TEST(MergeIndexTest, FindOccurrences) { StaticAST.getPreprocessorPtr(), StaticAST.getLocalTopLevelDecls()); - OccurrencesRequest Request; + RefsRequest Request; Request.IDs = {Foo.ID}; - Request.Filter = AllOccurrenceKinds; - std::vector Results; - MergedIndex->findOccurrences( - Request, [&](const SymbolOccurrence &O) { Results.push_back(O); }); - - EXPECT_THAT(Results, UnorderedElementsAre( - AllOf(OccurrenceRange(Test1Code.range("Foo")), - FileURI("unittest:///test.cc")), - AllOf(OccurrenceRange(Test2Code.range("Foo")), - FileURI("unittest:///test2.cc")))); + RefSlab::Builder Results; + MergedIndex->refs(Request, [&](const Ref &O) { Results.insert(Foo.ID, O); }); + + EXPECT_THAT( + std::move(Results).build(), + ElementsAre(Pair( + _, UnorderedElementsAre(AllOf(RefRange(Test1Code.range("Foo")), + FileURI("unittest:///test.cc")), + AllOf(RefRange(Test2Code.range("Foo")), + FileURI("unittest:///test2.cc")))))); } MATCHER_P2(IncludeHeaderWithRef, IncludeHeader, References, "") { diff --git a/unittests/clangd/SymbolCollectorTests.cpp b/unittests/clangd/SymbolCollectorTests.cpp index b9efa5ba4..cc3c7d186 100644 --- a/unittests/clangd/SymbolCollectorTests.cpp +++ b/unittests/clangd/SymbolCollectorTests.cpp @@ -33,11 +33,14 @@ namespace clangd { namespace { +using testing::_; using testing::AllOf; +using testing::Contains; using testing::Eq; using testing::Field; using testing::IsEmpty; using testing::Not; +using testing::Pair; using testing::UnorderedElementsAre; using testing::UnorderedElementsAreArray; @@ -76,21 +79,21 @@ MATCHER_P(DefRange, Pos, "") { std::tie(Pos.start.line, Pos.start.character, Pos.end.line, Pos.end.character); } -MATCHER_P(Refs, R, "") { return int(arg.References) == R; } +MATCHER_P(RefCount, R, "") { return int(arg.References) == R; } MATCHER_P(ForCodeCompletion, IsIndexedForCodeCompletion, "") { return arg.IsIndexedForCodeCompletion == IsIndexedForCodeCompletion; } -MATCHER(OccurrenceRange, "") { - const SymbolOccurrence &Pos = testing::get<0>(arg); +MATCHER(RefRange, "") { + const Ref &Pos = testing::get<0>(arg); const Range &Range = testing::get<1>(arg); return std::tie(Pos.Location.Start.Line, Pos.Location.Start.Column, Pos.Location.End.Line, Pos.Location.End.Column) == std::tie(Range.start.line, Range.start.character, Range.end.line, Range.end.character); } -testing::Matcher &> +testing::Matcher &> HaveRanges(const std::vector Ranges) { - return testing::UnorderedPointwise(OccurrenceRange(), Ranges); + return testing::UnorderedPointwise(RefRange(), Ranges); } class ShouldCollectSymbolTest : public ::testing::Test { @@ -250,7 +253,7 @@ class SymbolCollectorTest : public ::testing::Test { llvm::MemoryBuffer::getMemBuffer(MainCode)); Invocation.run(); Symbols = Factory->Collector->takeSymbols(); - SymbolOccurrences = Factory->Collector->takeOccurrences(); + Refs = Factory->Collector->takeRefs(); return true; } @@ -261,7 +264,7 @@ class SymbolCollectorTest : public ::testing::Test { std::string TestFileName; std::string TestFileURI; SymbolSlab Symbols; - SymbolOccurrenceSlab SymbolOccurrences; + RefSlab Refs; SymbolCollector::Options CollectorOpts; std::unique_ptr PragmaHandler; }; @@ -428,7 +431,7 @@ o]](); )); } -TEST_F(SymbolCollectorTest, Occurrences) { +TEST_F(SymbolCollectorTest, Refs) { Annotations Header(R"( class $foo[[Foo]] { public: @@ -457,28 +460,23 @@ TEST_F(SymbolCollectorTest, Occurrences) { static const int c = 0; class d {}; )"); - CollectorOpts.OccurrenceFilter = AllOccurrenceKinds; + CollectorOpts.RefFilter = RefKind::All; runSymbolCollector(Header.code(), (Main.code() + SymbolsOnlyInMainCode.code()).str()); auto HeaderSymbols = TestTU::withHeaderCode(Header.code()).headerSymbols(); - EXPECT_THAT(SymbolOccurrences.find(findSymbol(Symbols, "Foo").ID), - HaveRanges(Main.ranges("foo"))); - EXPECT_THAT(SymbolOccurrences.find(findSymbol(Symbols, "Bar").ID), - HaveRanges(Main.ranges("bar"))); - EXPECT_THAT(SymbolOccurrences.find(findSymbol(Symbols, "func").ID), - HaveRanges(Main.ranges("func"))); - - // Retrieve IDs for symbols *only* in the main file, and verify these symbols - // are not collected. + EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Foo").ID, + HaveRanges(Main.ranges("foo"))))); + EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "Bar").ID, + HaveRanges(Main.ranges("bar"))))); + EXPECT_THAT(Refs, Contains(Pair(findSymbol(Symbols, "func").ID, + HaveRanges(Main.ranges("func"))))); + // Symbols *only* in the main file (a, b, c) had no refs collected. auto MainSymbols = TestTU::withHeaderCode(SymbolsOnlyInMainCode.code()).headerSymbols(); - EXPECT_THAT(SymbolOccurrences.find(findSymbol(MainSymbols, "a").ID), - IsEmpty()); - EXPECT_THAT(SymbolOccurrences.find(findSymbol(MainSymbols, "b").ID), - IsEmpty()); - EXPECT_THAT(SymbolOccurrences.find(findSymbol(MainSymbols, "c").ID), - IsEmpty()); + EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "a").ID, _)))); + EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "b").ID, _)))); + EXPECT_THAT(Refs, Not(Contains(Pair(findSymbol(MainSymbols, "c").ID, _)))); } TEST_F(SymbolCollectorTest, References) { @@ -502,10 +500,10 @@ TEST_F(SymbolCollectorTest, References) { CollectorOpts.CountReferences = true; runSymbolCollector(Header, Main); EXPECT_THAT(Symbols, - UnorderedElementsAre(AllOf(QName("W"), Refs(1)), - AllOf(QName("X"), Refs(1)), - AllOf(QName("Y"), Refs(0)), - AllOf(QName("Z"), Refs(0)), QName("y"))); + UnorderedElementsAre(AllOf(QName("W"), RefCount(1)), + AllOf(QName("X"), RefCount(1)), + AllOf(QName("Y"), RefCount(0)), + AllOf(QName("Z"), RefCount(0)), QName("y"))); } TEST_F(SymbolCollectorTest, SymbolRelativeNoFallback) { @@ -1058,8 +1056,8 @@ TEST_F(SymbolCollectorTest, ReferencesInFriendDecl) { )"; CollectorOpts.CountReferences = true; runSymbolCollector(Header, Main); - EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), Refs(1)), - AllOf(QName("Y"), Refs(1)))); + EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(QName("X"), RefCount(1)), + AllOf(QName("Y"), RefCount(1)))); } TEST_F(SymbolCollectorTest, Origin) { @@ -1085,14 +1083,14 @@ TEST_F(SymbolCollectorTest, CollectMacros) { CollectorOpts.CountReferences = true; CollectorOpts.CollectMacro = true; runSymbolCollector(Header.code(), Main); - EXPECT_THAT( - Symbols, - UnorderedElementsAre( - QName("p"), - AllOf(QName("X"), DeclURI(TestHeaderURI), - IncludeHeader(TestHeaderURI)), - AllOf(Labeled("MAC(x)"), Refs(0), DeclRange(Header.range("mac"))), - AllOf(Labeled("USED(y)"), Refs(1), DeclRange(Header.range("used"))))); + EXPECT_THAT(Symbols, + UnorderedElementsAre(QName("p"), + AllOf(QName("X"), DeclURI(TestHeaderURI), + IncludeHeader(TestHeaderURI)), + AllOf(Labeled("MAC(x)"), RefCount(0), + DeclRange(Header.range("mac"))), + AllOf(Labeled("USED(y)"), RefCount(1), + DeclRange(Header.range("used"))))); } } // namespace diff --git a/unittests/clangd/TestTU.cpp b/unittests/clangd/TestTU.cpp index 9fd38a816..d4b442cb5 100644 --- a/unittests/clangd/TestTU.cpp +++ b/unittests/clangd/TestTU.cpp @@ -49,8 +49,8 @@ SymbolSlab TestTU::headerSymbols() const { } std::unique_ptr TestTU::index() const { - // FIXME: we should generate proper occurrences for TestTU. - return MemIndex::build(headerSymbols(), SymbolOccurrenceSlab::createEmpty()); + // FIXME: we should generate proper refs for TestTU. + return MemIndex::build(headerSymbols(), RefSlab()); } const Symbol &findSymbol(const SymbolSlab &Slab, llvm::StringRef QName) { From 8e7ab7aee54c88f38be76af3ea3e127bbd515041 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 4 Sep 2018 15:10:40 +0000 Subject: [PATCH 130/686] [clangd] Move buildStaticIndex() to SymbolYAML `buildStaticIndex()` is used by two other tools that I'm building, now it's useful outside of `tool/ClangdMain.cpp`. Also, slightly refactor the code while moving it to the different source file. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D51626 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341369 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/SymbolYAML.cpp | 22 ++++++++++++++++++---- clangd/index/SymbolYAML.h | 6 ++++++ clangd/tool/ClangdMain.cpp | 20 +------------------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/SymbolYAML.cpp index d3947fa14..8db4845f1 100644 --- a/clangd/index/SymbolYAML.cpp +++ b/clangd/index/SymbolYAML.cpp @@ -9,6 +9,7 @@ #include "SymbolYAML.h" #include "Index.h" +#include "dex/DexIndex.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Errc.h" @@ -25,18 +26,18 @@ using clang::clangd::Symbol; using clang::clangd::SymbolID; using clang::clangd::SymbolLocation; using clang::index::SymbolInfo; -using clang::index::SymbolLanguage; using clang::index::SymbolKind; +using clang::index::SymbolLanguage; // Helper to (de)serialize the SymbolID. We serialize it as a hex string. struct NormalizedSymbolID { NormalizedSymbolID(IO &) {} - NormalizedSymbolID(IO &, const SymbolID& ID) { + NormalizedSymbolID(IO &, const SymbolID &ID) { llvm::raw_string_ostream OS(HexString); OS << ID; } - SymbolID denormalize(IO&) { + SymbolID denormalize(IO &) { SymbolID ID; HexString >> ID; return ID; @@ -167,7 +168,7 @@ Symbol SymbolFromYAML(llvm::yaml::Input &Input) { return S; } -void SymbolsToYAML(const SymbolSlab& Symbols, llvm::raw_ostream &OS) { +void SymbolsToYAML(const SymbolSlab &Symbols, llvm::raw_ostream &OS) { llvm::yaml::Output Yout(OS); for (Symbol S : Symbols) // copy: Yout<< requires mutability. Yout << S; @@ -181,5 +182,18 @@ std::string SymbolToYAML(Symbol Sym) { return OS.str(); } +std::unique_ptr loadIndex(llvm::StringRef SymbolFile, + bool UseDex) { + auto Buffer = llvm::MemoryBuffer::getFile(SymbolFile); + if (!Buffer) { + llvm::errs() << "Can't open " << SymbolFile << "\n"; + return nullptr; + } + auto Slab = symbolsFromYAML(Buffer.get()->getBuffer()); + + return UseDex ? dex::DexIndex::build(std::move(Slab)) + : MemIndex::build(std::move(Slab), RefSlab()); +} + } // namespace clangd } // namespace clang diff --git a/clangd/index/SymbolYAML.h b/clangd/index/SymbolYAML.h index 897b1b7c6..1af51c075 100644 --- a/clangd/index/SymbolYAML.h +++ b/clangd/index/SymbolYAML.h @@ -41,6 +41,12 @@ std::string SymbolToYAML(Symbol Sym); // The YAML result is safe to concatenate if you have multiple symbol slabs. void SymbolsToYAML(const SymbolSlab &Symbols, llvm::raw_ostream &OS); +// Build an in-memory static index for global symbols from a symbol file. +// The size of global symbols should be relatively small, so that all symbols +// can be managed in memory. +std::unique_ptr loadIndex(llvm::StringRef SymbolFile, + bool UseDex = true); + } // namespace clangd } // namespace clang diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index c9ceb9aa8..a71f0b6f8 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -39,24 +39,6 @@ namespace { enum class PCHStorageFlag { Disk, Memory }; -// Build an in-memory static index for global symbols from a YAML-format file. -// The size of global symbols should be relatively small, so that all symbols -// can be managed in memory. -std::unique_ptr buildStaticIndex(llvm::StringRef YamlSymbolFile) { - auto Buffer = llvm::MemoryBuffer::getFile(YamlSymbolFile); - if (!Buffer) { - llvm::errs() << "Can't open " << YamlSymbolFile << "\n"; - return nullptr; - } - auto Slab = symbolsFromYAML(Buffer.get()->getBuffer()); - SymbolSlab::Builder SymsBuilder; - for (auto Sym : Slab) - SymsBuilder.insert(Sym); - - return UseDex ? dex::DexIndex::build(std::move(SymsBuilder).build()) - : MemIndex::build(std::move(SymsBuilder).build(), RefSlab()); -} - } // namespace static llvm::cl::opt CompileCommandsDir( @@ -298,7 +280,7 @@ int main(int argc, char *argv[]) { Opts.BuildDynamicSymbolIndex = EnableIndex; std::unique_ptr StaticIdx; if (EnableIndex && !YamlSymbolFile.empty()) { - StaticIdx = buildStaticIndex(YamlSymbolFile); + StaticIdx = loadIndex(YamlSymbolFile, UseDex); Opts.StaticIndex = StaticIdx.get(); } Opts.AsyncThreadsCount = WorkerThreadsCount; From ad8fac9e4ac16fd5fc37e53d14f0e9bd4903ac75 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 4 Sep 2018 15:45:56 +0000 Subject: [PATCH 131/686] [clangd] NFC: Change quality type to float Reviewed by: sammccall Differential Revision: https://reviews.llvm.org/D51636 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341374 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Index.cpp | 2 +- clangd/index/Index.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index 52eb42799..7cbc92057 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -59,7 +59,7 @@ raw_ostream &operator<<(raw_ostream &OS, const Symbol &S) { return OS << S.Scope << S.Name; } -double quality(const Symbol &S) { +float quality(const Symbol &S) { // This avoids a sharp gradient for tail symbols, and also neatly avoids the // question of whether 0 references means a bad symbol or missing data. if (S.References < 3) diff --git a/clangd/index/Index.h b/clangd/index/Index.h index c1778e189..bd21ead18 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -250,7 +250,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Symbol &S); // This currently falls in the range [1, ln(#indexed documents)]. // FIXME: this should probably be split into symbol -> signals // and signals -> score, so it can be reused for Sema completions. -double quality(const Symbol &S); +float quality(const Symbol &S); // An immutable symbol container that stores a set of symbols. // The container will maintain the lifetime of the symbols. From c892e428fdfaccb589cae764b79115537021b9d8 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 4 Sep 2018 16:16:50 +0000 Subject: [PATCH 132/686] [clangd] Define a compact binary serialization fomat for symbol slab/index. Summary: This is intended to replace the current YAML format for general use. It's ~10x more compact than YAML, and ~40% more compact than gzipped YAML: llvmidx.riff = 20M, llvmidx.yaml = 272M, llvmidx.yaml.gz = 32M It's also simpler/faster to read and write. The format is a RIFF container (chunks of (type, size, data)) with: - a compressed string table - simple binary encoding of symbols (with varints for compactness) It can be extended to include occurrences, Dex posting lists, etc. There's no rich backwards-compatibility scheme, but a version number is included so we can detect incompatible files and do ad-hoc back-compat. Alternatives considered: - compressed YAML or JSON: bulky and slow to load - llvm bitstream: confusing model and libraries are hard to use. My attempt produced slightly larger files, and the code was longer and slower. - protobuf or similar: would be really nice (esp for back-compat) but the dependency is a big hassle - ad-hoc binary format without a container: it seems clear we're going to add posting lists and occurrences here, and that they will benefit from sharing a string table. The container makes it easy to debug these pieces in isolation, and make them optional. Reviewers: ioeric Subscribers: mgorny, ilya-biryukov, MaskRay, jkorous, mgrang, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51585 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341375 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 2 + clangd/RIFF.cpp | 88 +++++ clangd/RIFF.h | 81 ++++ .../GlobalSymbolBuilderMain.cpp | 32 +- clangd/index/Index.cpp | 46 +-- clangd/index/Index.h | 41 +- clangd/index/Serialization.cpp | 366 ++++++++++++++++++ clangd/index/Serialization.h | 48 +++ clangd/index/SymbolYAML.cpp | 17 +- clangd/tool/ClangdMain.cpp | 1 + unittests/clangd/CMakeLists.txt | 2 + unittests/clangd/RIFFTests.cpp | 39 ++ unittests/clangd/SerializationTests.cpp | 138 +++++++ unittests/clangd/SymbolCollectorTests.cpp | 79 ---- 14 files changed, 848 insertions(+), 132 deletions(-) create mode 100644 clangd/RIFF.cpp create mode 100644 clangd/RIFF.h create mode 100644 clangd/index/Serialization.cpp create mode 100644 clangd/index/Serialization.h create mode 100644 unittests/clangd/RIFFTests.cpp create mode 100644 unittests/clangd/SerializationTests.cpp diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index ef332baf7..df859b45b 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -29,6 +29,7 @@ add_clang_library(clangDaemon Protocol.cpp ProtocolHandlers.cpp Quality.cpp + RIFF.cpp SourceCode.cpp Threading.cpp Trace.cpp @@ -41,6 +42,7 @@ add_clang_library(clangDaemon index/Index.cpp index/MemIndex.cpp index/Merge.cpp + index/Serialization.cpp index/SymbolCollector.cpp index/SymbolYAML.cpp diff --git a/clangd/RIFF.cpp b/clangd/RIFF.cpp new file mode 100644 index 000000000..cb832db87 --- /dev/null +++ b/clangd/RIFF.cpp @@ -0,0 +1,88 @@ +//===--- RIFF.cpp - Binary container file format --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RIFF.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +namespace clang { +namespace clangd { +namespace riff { + +static Error makeError(const char *Msg) { + return createStringError(inconvertibleErrorCode(), Msg); +} + +Expected readChunk(StringRef &Stream) { + if (Stream.size() < 8) + return makeError("incomplete chunk header"); + Chunk C; + std::copy(Stream.begin(), Stream.begin() + 4, C.ID.begin()); + Stream = Stream.drop_front(4); + uint32_t Len = support::endian::read32le(Stream.take_front(4).begin()); + Stream = Stream.drop_front(4); + if (Stream.size() < Len) + return makeError("truncated chunk"); + C.Data = Stream.take_front(Len); + Stream = Stream.drop_front(Len); + if (Len % 2 & !Stream.empty()) { // Skip padding byte. + if (Stream.front()) + return makeError("nonzero padding byte"); + Stream = Stream.drop_front(); + } + return C; +}; + +raw_ostream &operator<<(raw_ostream &OS, const Chunk &C) { + OS.write(C.ID.begin(), C.ID.size()); + char Size[4]; + llvm::support::endian::write32le(Size, C.Data.size()); + OS.write(Size, sizeof(Size)); + OS << C.Data; + if (C.Data.size() % 2) + OS.write(0); + return OS; +} + +llvm::Expected readFile(llvm::StringRef Stream) { + auto RIFF = readChunk(Stream); + if (!RIFF) + return RIFF.takeError(); + if (RIFF->ID != fourCC("RIFF")) + return makeError("not a RIFF container"); + if (RIFF->Data.size() < 4) + return makeError("RIFF chunk too short"); + File F; + std::copy(RIFF->Data.begin(), RIFF->Data.begin() + 4, F.Type.begin()); + for (llvm::StringRef Body = RIFF->Data.drop_front(4); !Body.empty();) + if (auto Chunk = readChunk(Body)) { + F.Chunks.push_back(*Chunk); + } else + return Chunk.takeError(); + return F; +} + +raw_ostream &operator<<(raw_ostream &OS, const File &F) { + // To avoid copies, we serialize the outer RIFF chunk "by hand". + size_t DataLen = 4; // Predict length of RIFF chunk data. + for (const auto &C : F.Chunks) + DataLen += 4 + 4 + C.Data.size() + (C.Data.size() % 2); + OS << "RIFF"; + char Size[4]; + llvm::support::endian::write32le(Size, DataLen); + OS.write(Size, sizeof(Size)); + OS.write(F.Type.begin(), F.Type.size()); + for (const auto &C : F.Chunks) + OS << C; + return OS; +} + +} // namespace riff +} // namespace clangd +} // namespace clang diff --git a/clangd/RIFF.h b/clangd/RIFF.h new file mode 100644 index 000000000..f56d08798 --- /dev/null +++ b/clangd/RIFF.h @@ -0,0 +1,81 @@ +//===--- RIFF.h - Binary container file format -------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tools for reading and writing data in RIFF containers. +// +// A chunk consists of: +// - ID : char[4] +// - Length : uint32 +// - Data : byte[Length] +// - Padding : byte[Length % 2] +// The semantics of a chunk's Data are determined by its ID. +// The format makes it easy to skip over uninteresting or unknown chunks. +// +// A RIFF file is a single chunk with ID "RIFF". Its Data is: +// - Type : char[4] +// - Chunks : chunk[] +// +// This means that a RIFF file consists of: +// - "RIFF" : char[4] +// - File length - 8 : uint32 +// - File type : char[4] +// - Chunks : chunk[] +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_RIFF_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_RIFF_H +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ScopedPrinter.h" +#include + +namespace clang { +namespace clangd { +namespace riff { + +// A FourCC identifies a chunk in a file, or the type of file itself. +using FourCC = std::array; +// Get a FourCC from a string literal, e.g. fourCC("RIFF"). +inline constexpr FourCC fourCC(const char (&Literal)[5]) { + return FourCC{{Literal[0], Literal[1], Literal[2], Literal[3]}}; +} +// A chunk is a section in a RIFF container. +struct Chunk { + FourCC ID; + llvm::StringRef Data; +}; +inline bool operator==(const Chunk &L, const Chunk &R) { + return std::tie(L.ID, L.Data) == std::tie(R.ID, R.Data); +} +// A File is a RIFF container, which is a typed chunk sequence. +struct File { + FourCC Type; + std::vector Chunks; +}; +inline bool operator==(const File &L, const File &R) { + return std::tie(L.Type, L.Chunks) == std::tie(R.Type, R.Chunks); +} + +// Reads a single chunk from the start of Stream. +// Stream is updated to exclude the consumed chunk. +llvm::Expected readChunk(llvm::StringRef &Stream); + +// Serialize a single chunk to OS. +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Chunk &); + +// Parses a RIFF file consisting of a single RIFF chunk. +llvm::Expected readFile(llvm::StringRef Stream); + +// Serialize a RIFF file (i.e. a single RIFF chunk) to OS. +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const File &); + +} // namespace riff +} // namespace clangd +} // namespace clang +#endif diff --git a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp b/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp index 84211c6cb..4fbc0a7e5 100644 --- a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp +++ b/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp @@ -7,15 +7,16 @@ // //===----------------------------------------------------------------------===// // -// GlobalSymbolBuilder is a tool to generate YAML-format symbols across the -// whole project. This tools is for **experimental** only. Don't use it in -// production code. +// GlobalSymbolBuilder is a tool to extract symbols from a whole project. +// This tool is **experimental** only. Don't use it in production code. // //===----------------------------------------------------------------------===// +#include "RIFF.h" #include "index/CanonicalIncludes.h" #include "index/Index.h" #include "index/Merge.h" +#include "index/Serialization.h" #include "index/SymbolCollector.h" #include "index/SymbolYAML.h" #include "clang/Frontend/CompilerInstance.h" @@ -59,6 +60,14 @@ static llvm::cl::opt MergeOnTheFly( "MapReduce."), llvm::cl::init(true), llvm::cl::Hidden); +enum class Format { YAML, Binary }; +static llvm::cl::opt + Format("format", llvm::cl::desc("Format of the index to be written"), + llvm::cl::values( + clEnumValN(Format::YAML, "yaml", "human-readable YAML format"), + clEnumValN(Format::Binary, "binary", "binary RIFF format")), + llvm::cl::init(Format::YAML)); + /// Responsible for aggregating symbols from each processed file and producing /// the final results. All methods in this class must be thread-safe, /// 'consumeSymbols' may be called from multiple threads. @@ -210,8 +219,8 @@ int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); const char *Overview = R"( - This is an **experimental** tool to generate YAML-format project-wide symbols - for clangd (global code completion). It would be changed and deprecated + This is an **experimental** tool to extract symbols from a whole project + for clangd (global code completion). It will be changed and deprecated eventually. Don't use it in production code! Example usage for building index for the whole project using CMake compile @@ -262,7 +271,16 @@ int main(int argc, const char **argv) { } // Reduce phase: combine symbols with the same IDs. auto UniqueSymbols = Consumer->mergeResults(); - // Output phase: emit YAML for result symbols. - SymbolsToYAML(UniqueSymbols, llvm::outs()); + // Output phase: emit result symbols. + switch (clang::clangd::Format) { + case clang::clangd::Format::YAML: + SymbolsToYAML(UniqueSymbols, llvm::outs()); + break; + case clang::clangd::Format::Binary: { + clang::clangd::IndexFileOut Out; + Out.Symbols = &UniqueSymbols; + llvm::outs() << Out; + } + } return 0; } diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index 7cbc92057..48a7c688d 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -10,6 +10,7 @@ #include "Index.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" #include "llvm/Support/SHA1.h" #include "llvm/Support/raw_ostream.h" @@ -28,21 +29,20 @@ SymbolID::SymbolID(StringRef USR) : HashValue(SHA1::hash(arrayRefFromStringRef(USR))) {} raw_ostream &operator<<(raw_ostream &OS, const SymbolID &ID) { - OS << toHex(toStringRef(ID.HashValue)); - return OS; + return OS << toHex(ID.raw()); } -std::string SymbolID::str() const { - std::string ID; - llvm::raw_string_ostream OS(ID); - OS << *this; - return OS.str(); +SymbolID SymbolID::fromRaw(llvm::StringRef Raw) { + SymbolID ID; + assert(Raw.size() == RawSize); + memcpy(ID.HashValue.data(), Raw.data(), RawSize); + return ID; } +std::string SymbolID::str() const { return toHex(raw()); } + void operator>>(StringRef Str, SymbolID &ID) { - std::string HexString = fromHex(Str); - assert(HexString.size() == ID.HashValue.size()); - std::copy(HexString.begin(), HexString.end(), ID.HashValue.begin()); + ID = SymbolID::fromRaw(fromHex(Str)); } raw_ostream &operator<<(raw_ostream &OS, SymbolOrigin O) { @@ -78,34 +78,18 @@ SymbolSlab::const_iterator SymbolSlab::find(const SymbolID &ID) const { } // Copy the underlying data of the symbol into the owned arena. -static void own(Symbol &S, llvm::UniqueStringSaver &Strings, - BumpPtrAllocator &Arena) { - // Intern replaces V with a reference to the same string owned by the arena. - auto Intern = [&](StringRef &V) { V = Strings.save(V); }; - - // We need to copy every StringRef field onto the arena. - Intern(S.Name); - Intern(S.Scope); - Intern(S.CanonicalDeclaration.FileURI); - Intern(S.Definition.FileURI); - - Intern(S.Signature); - Intern(S.CompletionSnippetSuffix); - - Intern(S.Documentation); - Intern(S.ReturnType); - for (auto &I : S.IncludeHeaders) - Intern(I.IncludeHeader); +static void own(Symbol &S, llvm::UniqueStringSaver &Strings) { + visitStrings(S, [&](StringRef &V) { V = Strings.save(V); }); } void SymbolSlab::Builder::insert(const Symbol &S) { auto R = SymbolIndex.try_emplace(S.ID, Symbols.size()); if (R.second) { Symbols.push_back(S); - own(Symbols.back(), UniqueStrings, Arena); + own(Symbols.back(), UniqueStrings); } else { auto &Copy = Symbols[R.first->second] = S; - own(Copy, UniqueStrings, Arena); + own(Copy, UniqueStrings); } } @@ -118,7 +102,7 @@ SymbolSlab SymbolSlab::Builder::build() && { BumpPtrAllocator NewArena; llvm::UniqueStringSaver Strings(NewArena); for (auto &S : Symbols) - own(S, Strings, NewArena); + own(S, Strings); return SymbolSlab(std::move(NewArena), std::move(Symbols)); } diff --git a/clangd/index/Index.h b/clangd/index/Index.h index bd21ead18..249237052 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -84,26 +84,28 @@ class SymbolID { return HashValue < Sym.HashValue; } + constexpr static size_t RawSize = 20; + llvm::StringRef raw() const { + return StringRef(reinterpret_cast(HashValue.data()), RawSize); + } + static SymbolID fromRaw(llvm::StringRef); // Returns a 40-bytes hex encoded string. std::string str() const; private: - static constexpr unsigned HashByteLength = 20; - - friend llvm::hash_code hash_value(const SymbolID &ID) { - // We already have a good hash, just return the first bytes. - static_assert(sizeof(size_t) <= HashByteLength, "size_t longer than SHA1!"); - size_t Result; - memcpy(&Result, ID.HashValue.data(), sizeof(size_t)); - return llvm::hash_code(Result); - } - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const SymbolID &ID); friend void operator>>(llvm::StringRef Str, SymbolID &ID); - std::array HashValue; + std::array HashValue; }; +inline llvm::hash_code hash_value(const SymbolID &ID) { + // We already have a good hash, just return the first bytes. + assert(sizeof(size_t) <= SymbolID::RawSize && "size_t longer than SHA1!"); + size_t Result; + memcpy(&Result, ID.raw().data(), sizeof(size_t)); + return llvm::hash_code(Result); +} + // Write SymbolID into the given stream. SymbolID is encoded as a 40-bytes // hex string. llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SymbolID &ID); @@ -246,6 +248,21 @@ struct Symbol { }; llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Symbol &S); +// Invokes Callback with each StringRef& contained in the Symbol. +// Useful for deduplicating backing strings. +template void visitStrings(Symbol &S, const Callback &CB) { + CB(S.Name); + CB(S.Scope); + CB(S.CanonicalDeclaration.FileURI); + CB(S.Definition.FileURI); + CB(S.Signature); + CB(S.CompletionSnippetSuffix); + CB(S.Documentation); + CB(S.ReturnType); + for (auto &Include : S.IncludeHeaders) + CB(Include.IncludeHeader); +} + // Computes query-independent quality score for a Symbol. // This currently falls in the range [1, ln(#indexed documents)]. // FIXME: this should probably be split into symbol -> signals diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp new file mode 100644 index 000000000..df88142d1 --- /dev/null +++ b/clangd/index/Serialization.cpp @@ -0,0 +1,366 @@ +//===-- Serialization.cpp - Binary serialization of index data ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "Serialization.h" +#include "../RIFF.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" + +using namespace llvm; +namespace clang { +namespace clangd { +namespace { +Error makeError(const Twine &Msg) { + return make_error(Msg, inconvertibleErrorCode()); +} + +// IO PRIMITIVES +// We use little-endian 32 bit ints, sometimes with variable-length encoding. + +StringRef consume(StringRef &Data, int N) { + StringRef Ret = Data.take_front(N); + Data = Data.drop_front(N); + return Ret; +} + +uint8_t consume8(StringRef &Data) { + uint8_t Ret = Data.front(); + Data = Data.drop_front(); + return Ret; +} + +uint32_t consume32(StringRef &Data) { + auto Ret = support::endian::read32le(Data.bytes_begin()); + Data = Data.drop_front(4); + return Ret; +} + +void write32(uint32_t I, raw_ostream &OS) { + char buf[4]; + support::endian::write32le(buf, I); + OS.write(buf, sizeof(buf)); +} + +// Variable-length int encoding (varint) uses the bottom 7 bits of each byte +// to encode the number, and the top bit to indicate whether more bytes follow. +// e.g. 9a 2f means [0x1a and keep reading, 0x2f and stop]. +// This represents 0x1a | 0x2f<<7 = 6042. +// A 32-bit integer takes 1-5 bytes to encode; small numbers are more compact. +void writeVar(uint32_t I, raw_ostream &OS) { + constexpr static uint8_t More = 1 << 7; + if (LLVM_LIKELY(I < 1 << 7)) { + OS.write(I); + return; + } + for (;;) { + OS.write(I | More); + I >>= 7; + if (I < 1 << 7) { + OS.write(I); + return; + } + } +} + +uint32_t consumeVar(StringRef &Data) { + constexpr static uint8_t More = 1 << 7; + uint8_t B = consume8(Data); + if (LLVM_LIKELY(!(B & More))) + return B; + uint32_t Val = B & ~More; + for (int Shift = 7; B & More && Shift < 32; Shift += 7) { + B = consume8(Data); + Val |= (B & ~More) << Shift; + } + return Val; +} + +// STRING TABLE ENCODING +// Index data has many string fields, and many strings are identical. +// We store each string once, and refer to them by index. +// +// The string table's format is: +// - UncompressedSize : uint32 +// - CompressedData : byte[CompressedSize] +// +// CompressedData is a zlib-compressed byte[UncompressedSize]. +// It contains a sequence of null-terminated strings, e.g. "foo\0bar\0". +// These are sorted to improve compression. + +// Maps each string to a canonical representation. +// Strings remain owned externally (e.g. by SymbolSlab). +class StringTableOut { + DenseSet Unique; + std::vector Sorted; + // Since strings are interned, look up can be by pointer. + DenseMap, unsigned> Index; + +public: + // Add a string to the table. Overwrites S if an identical string exists. + void intern(StringRef &S) { S = *Unique.insert(S).first; }; + // Finalize the table and write it to OS. No more strings may be added. + void finalize(raw_ostream &OS) { + Sorted = {Unique.begin(), Unique.end()}; + std::sort(Sorted.begin(), Sorted.end()); + for (unsigned I = 0; I < Sorted.size(); ++I) + Index.try_emplace({Sorted[I].data(), Sorted[I].size()}, I); + + std::string RawTable; + for (StringRef S : Sorted) { + RawTable.append(S); + RawTable.push_back(0); + } + SmallString<1> Compressed; + cantFail(zlib::compress(RawTable, Compressed)); + write32(RawTable.size(), OS); + OS << Compressed; + } + // Get the ID of an string, which must be interned. Table must be finalized. + unsigned index(StringRef S) const { + assert(!Sorted.empty() && "table not finalized"); + assert(Index.count({S.data(), S.size()}) && "string not interned"); + return Index.find({S.data(), S.size()})->second; + } +}; + +struct StringTableIn { + BumpPtrAllocator Arena; + std::vector Strings; +}; + +Expected readStringTable(StringRef Data) { + if (Data.size() < 4) + return makeError("Bad string table: not enough metadata"); + size_t UncompressedSize = consume32(Data); + SmallString<1> Uncompressed; + if (Error E = llvm::zlib::uncompress(Data, Uncompressed, UncompressedSize)) + return std::move(E); + + StringTableIn Table; + StringSaver Saver(Table.Arena); + for (StringRef Rest = Uncompressed; !Rest.empty();) { + auto Len = Rest.find(0); + if (Len == StringRef::npos) + return makeError("Bad string table: not null terminated"); + Table.Strings.push_back(Saver.save(consume(Rest, Len))); + Rest = Rest.drop_front(); + } + return Table; +} + +// SYMBOL ENCODING +// Each field of clangd::Symbol is encoded in turn (see implementation). +// - StringRef fields encode as varint (index into the string table) +// - enums encode as the underlying type +// - most numbers encode as varint + +// It's useful to the implementation to assume symbols have a bounded size. +constexpr size_t SymbolSizeBound = 512; +// To ensure the bounded size, restrict the number of include headers stored. +constexpr unsigned MaxIncludes = 50; + +void writeSymbol(const Symbol &Sym, const StringTableOut &Strings, + raw_ostream &OS) { + auto StartOffset = OS.tell(); + OS << Sym.ID.raw(); // TODO: once we start writing xrefs and posting lists, + // symbol IDs should probably be in a string table. + OS.write(static_cast(Sym.SymInfo.Kind)); + OS.write(static_cast(Sym.SymInfo.Lang)); + writeVar(Strings.index(Sym.Name), OS); + writeVar(Strings.index(Sym.Scope), OS); + for (const auto &Loc : {Sym.Definition, Sym.CanonicalDeclaration}) { + writeVar(Strings.index(Loc.FileURI), OS); + for (const auto &Endpoint : {Loc.Start, Loc.End}) { + writeVar(Endpoint.Line, OS); + writeVar(Endpoint.Column, OS); + } + } + writeVar(Sym.References, OS); + OS.write(Sym.IsIndexedForCodeCompletion); + OS.write(static_cast(Sym.Origin)); + writeVar(Strings.index(Sym.Signature), OS); + writeVar(Strings.index(Sym.CompletionSnippetSuffix), OS); + writeVar(Strings.index(Sym.Documentation), OS); + writeVar(Strings.index(Sym.ReturnType), OS); + + auto WriteInclude = [&](const Symbol::IncludeHeaderWithReferences &Include) { + writeVar(Strings.index(Include.IncludeHeader), OS); + writeVar(Include.References, OS); + }; + // There are almost certainly few includes, so we can just write them. + if (LLVM_LIKELY(Sym.IncludeHeaders.size() <= MaxIncludes)) { + writeVar(Sym.IncludeHeaders.size(), OS); + for (const auto &Include : Sym.IncludeHeaders) + WriteInclude(Include); + } else { + // If there are too many, make sure we truncate the least important. + using Pointer = const Symbol::IncludeHeaderWithReferences *; + std::vector Pointers; + for (const auto &Include : Sym.IncludeHeaders) + Pointers.push_back(&Include); + std::sort(Pointers.begin(), Pointers.end(), [](Pointer L, Pointer R) { + return L->References > R->References; + }); + Pointers.resize(MaxIncludes); + + writeVar(MaxIncludes, OS); + for (Pointer P : Pointers) + WriteInclude(*P); + } + + assert(OS.tell() - StartOffset < SymbolSizeBound && "Symbol length unsafe!"); + (void)StartOffset; // Unused in NDEBUG; +} + +Expected readSymbol(StringRef &Data, const StringTableIn &Strings) { + // Usually we can skip bounds checks because the buffer is huge. + // Near the end of the buffer, this would be unsafe. In this rare case, copy + // the data into a bigger buffer so we can again skip the checks. + if (LLVM_UNLIKELY(Data.size() < SymbolSizeBound)) { + std::string Buf(Data); + Buf.resize(SymbolSizeBound); + StringRef ExtendedData = Buf; + auto Ret = readSymbol(ExtendedData, Strings); + unsigned BytesRead = Buf.size() - ExtendedData.size(); + if (BytesRead > Data.size()) + return makeError("read past end of data"); + Data = Data.drop_front(BytesRead); + return Ret; + } + +#define READ_STRING(Field) \ + do { \ + auto StringIndex = consumeVar(Data); \ + if (LLVM_UNLIKELY(StringIndex >= Strings.Strings.size())) \ + return makeError("Bad string index"); \ + Field = Strings.Strings[StringIndex]; \ + } while (0) + + Symbol Sym; + Sym.ID = SymbolID::fromRaw(consume(Data, 20)); + Sym.SymInfo.Kind = static_cast(consume8(Data)); + Sym.SymInfo.Lang = static_cast(consume8(Data)); + READ_STRING(Sym.Name); + READ_STRING(Sym.Scope); + for (SymbolLocation *Loc : {&Sym.Definition, &Sym.CanonicalDeclaration}) { + READ_STRING(Loc->FileURI); + for (auto &Endpoint : {&Loc->Start, &Loc->End}) { + Endpoint->Line = consumeVar(Data); + Endpoint->Column = consumeVar(Data); + } + } + Sym.References = consumeVar(Data); + Sym.IsIndexedForCodeCompletion = consume8(Data); + Sym.Origin = static_cast(consume8(Data)); + READ_STRING(Sym.Signature); + READ_STRING(Sym.CompletionSnippetSuffix); + READ_STRING(Sym.Documentation); + READ_STRING(Sym.ReturnType); + unsigned IncludeHeaderN = consumeVar(Data); + if (IncludeHeaderN > MaxIncludes) + return makeError("too many IncludeHeaders"); + Sym.IncludeHeaders.resize(IncludeHeaderN); + for (auto &I : Sym.IncludeHeaders) { + READ_STRING(I.IncludeHeader); + I.References = consumeVar(Data); + } + +#undef READ_STRING + return Sym; +} + +} // namespace + +// FILE ENCODING +// A file is a RIFF chunk with type 'CdIx'. +// It contains the sections: +// - meta: version number +// - stri: string table +// - symb: symbols + +// The current versioning scheme is simple - non-current versions are rejected. +// This allows arbitrary format changes, which invalidate stored data. +// Later we may want to support some backward compatibility. +constexpr static uint32_t Version = 1; + +Expected readIndexFile(StringRef Data) { + auto RIFF = riff::readFile(Data); + if (!RIFF) + return RIFF.takeError(); + if (RIFF->Type != riff::fourCC("CdIx")) + return makeError("wrong RIFF type"); + StringMap Chunks; + for (const auto &Chunk : RIFF->Chunks) + Chunks.try_emplace(StringRef(Chunk.ID.data(), Chunk.ID.size()), Chunk.Data); + + for (StringRef RequiredChunk : {"meta", "stri"}) + if (!Chunks.count(RequiredChunk)) + return makeError("missing required chunk " + RequiredChunk); + + StringRef Meta = Chunks.lookup("meta"); + if (Meta.size() < 4 || consume32(Meta) != Version) + return makeError("wrong version"); + + auto Strings = readStringTable(Chunks.lookup("stri")); + if (!Strings) + return Strings.takeError(); + + IndexFileIn Result; + if (Chunks.count("symb")) { + StringRef SymbolData = Chunks.lookup("symb"); + SymbolSlab::Builder Symbols; + while (!SymbolData.empty()) + if (auto Sym = readSymbol(SymbolData, *Strings)) + Symbols.insert(*Sym); + else + return Sym.takeError(); + Result.Symbols = std::move(Symbols).build(); + } + return Result; +} + +raw_ostream &operator<<(raw_ostream &OS, const IndexFileOut &Data) { + assert(Data.Symbols && "An index file without symbols makes no sense!"); + riff::File RIFF; + RIFF.Type = riff::fourCC("CdIx"); + + SmallString<4> Meta; + { + raw_svector_ostream MetaOS(Meta); + write32(Version, MetaOS); + } + RIFF.Chunks.push_back({riff::fourCC("meta"), Meta}); + + StringTableOut Strings; + std::vector Symbols; + for (const auto &Sym : *Data.Symbols) { + Symbols.emplace_back(Sym); + visitStrings(Symbols.back(), [&](StringRef &S) { Strings.intern(S); }); + } + + std::string StringSection; + { + raw_string_ostream StringOS(StringSection); + Strings.finalize(StringOS); + } + RIFF.Chunks.push_back({riff::fourCC("stri"), StringSection}); + + std::string SymbolSection; + { + raw_string_ostream SymbolOS(SymbolSection); + for (const auto &Sym : Symbols) + writeSymbol(Sym, Strings, SymbolOS); + } + RIFF.Chunks.push_back({riff::fourCC("symb"), SymbolSection}); + + return OS << RIFF; +} + +} // namespace clangd +} // namespace clang diff --git a/clangd/index/Serialization.h b/clangd/index/Serialization.h new file mode 100644 index 000000000..7ad867f94 --- /dev/null +++ b/clangd/index/Serialization.h @@ -0,0 +1,48 @@ +//===--- Serialization.h - Binary serialization of index data ----*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides a compact binary serialization of indexed symbols. +// +// It writes two sections: +// - a string table (which is compressed) +// - lists of encoded symbols +// +// The format has a simple versioning scheme: the version is embedded in the +// data and non-current versions are rejected when reading. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_RIFF_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_RIFF_H +#include "Index.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace clangd { + +// Specifies the contents of an index file to be written. +struct IndexFileOut { + const SymbolSlab *Symbols; + // TODO: Support serializing symbol occurrences. + // TODO: Support serializing Dex posting lists. +}; +// Serializes an index file. (This is a RIFF container chunk). +llvm::raw_ostream &operator<<(llvm::raw_ostream &, const IndexFileOut &); + +// Holds the contents of an index file that was read. +struct IndexFileIn { + llvm::Optional Symbols; +}; +// Parse an index file. The input must be a RIFF container chunk. +llvm::Expected readIndexFile(llvm::StringRef); + +} // namespace clangd +} // namespace clang + +#endif diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/SymbolYAML.cpp index 8db4845f1..d90f4b0c4 100644 --- a/clangd/index/SymbolYAML.cpp +++ b/clangd/index/SymbolYAML.cpp @@ -9,6 +9,7 @@ #include "SymbolYAML.h" #include "Index.h" +#include "Serialization.h" #include "dex/DexIndex.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" @@ -189,10 +190,20 @@ std::unique_ptr loadIndex(llvm::StringRef SymbolFile, llvm::errs() << "Can't open " << SymbolFile << "\n"; return nullptr; } - auto Slab = symbolsFromYAML(Buffer.get()->getBuffer()); + StringRef Data = Buffer->get()->getBuffer(); + + llvm::Optional Slab; + if (Data.startswith("RIFF")) { // Magic for binary index file. + if (auto RIFF = readIndexFile(Data)) + Slab = std::move(RIFF->Symbols); + else + llvm::errs() << "Bad RIFF: " << llvm::toString(RIFF.takeError()) << "\n"; + } else { + Slab = symbolsFromYAML(Data); + } - return UseDex ? dex::DexIndex::build(std::move(Slab)) - : MemIndex::build(std::move(Slab), RefSlab()); + return UseDex ? dex::DexIndex::build(std::move(*Slab)) + : MemIndex::build(std::move(*Slab), RefSlab()); } } // namespace clangd diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index a71f0b6f8..ffc1fec59 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -10,6 +10,7 @@ #include "ClangdLSPServer.h" #include "JSONRPCDispatcher.h" #include "Path.h" +#include "RIFF.h" #include "Trace.h" #include "index/SymbolYAML.h" #include "index/dex/DexIndex.h" diff --git a/unittests/clangd/CMakeLists.txt b/unittests/clangd/CMakeLists.txt index 92f8b963f..dee2e4e22 100644 --- a/unittests/clangd/CMakeLists.txt +++ b/unittests/clangd/CMakeLists.txt @@ -26,6 +26,8 @@ add_extra_unittest(ClangdTests HeadersTests.cpp IndexTests.cpp QualityTests.cpp + RIFFTests.cpp + SerializationTests.cpp SourceCodeTests.cpp SymbolCollectorTests.cpp SyncAPI.cpp diff --git a/unittests/clangd/RIFFTests.cpp b/unittests/clangd/RIFFTests.cpp new file mode 100644 index 000000000..d252edf8e --- /dev/null +++ b/unittests/clangd/RIFFTests.cpp @@ -0,0 +1,39 @@ +//===-- RIFFTests.cpp - Binary container unit tests -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RIFF.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { +using namespace llvm; +using ::testing::ElementsAre; + +TEST(RIFFTest, File) { + riff::File File{riff::fourCC("test"), + { + {riff::fourCC("even"), "abcd"}, + {riff::fourCC("oddd"), "abcde"}, + }}; + StringRef Serialized = StringRef("RIFF\x1e\0\0\0test" + "even\x04\0\0\0abcd" + "oddd\x05\0\0\0abcde\0", + 38); + + EXPECT_EQ(llvm::to_string(File), Serialized); + auto Parsed = riff::readFile(Serialized); + ASSERT_TRUE(bool(Parsed)) << Parsed.takeError(); + EXPECT_EQ(*Parsed, File); +} + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/unittests/clangd/SerializationTests.cpp b/unittests/clangd/SerializationTests.cpp new file mode 100644 index 000000000..cc430963c --- /dev/null +++ b/unittests/clangd/SerializationTests.cpp @@ -0,0 +1,138 @@ +//===-- SerializationTests.cpp - Binary and YAML serialization unit tests -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "index/Serialization.h" +#include "index/SymbolYAML.h" +#include "llvm/Support/ScopedPrinter.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using testing::UnorderedElementsAre; +using testing::UnorderedElementsAreArray; +namespace clang { +namespace clangd { +namespace { + +const char *YAML1 = R"( +--- +ID: 057557CEBF6E6B2DD437FBF60CC58F352D1DF856 +Name: 'Foo1' +Scope: 'clang::' +SymInfo: + Kind: Function + Lang: Cpp +CanonicalDeclaration: + FileURI: file:///path/foo.h + Start: + Line: 1 + Column: 0 + End: + Line: 1 + Column: 1 +IsIndexedForCodeCompletion: true +Documentation: 'Foo doc' +ReturnType: 'int' +IncludeHeaders: + - Header: 'include1' + References: 7 + - Header: 'include2' + References: 3 +... +)"; + +const char *YAML2 = R"( +--- +ID: 057557CEBF6E6B2DD437FBF60CC58F352D1DF858 +Name: 'Foo2' +Scope: 'clang::' +SymInfo: + Kind: Function + Lang: Cpp +CanonicalDeclaration: + FileURI: file:///path/bar.h + Start: + Line: 1 + Column: 0 + End: + Line: 1 + Column: 1 +IsIndexedForCodeCompletion: false +Signature: '-sig' +CompletionSnippetSuffix: '-snippet' +... +)"; + +MATCHER_P(QName, Name, "") { return (arg.Scope + arg.Name).str() == Name; } +MATCHER_P2(IncludeHeaderWithRef, IncludeHeader, References, "") { + return (arg.IncludeHeader == IncludeHeader) && (arg.References == References); +} + +TEST(SerializationTest, YAMLConversions) { + auto Symbols1 = symbolsFromYAML(YAML1); + ASSERT_EQ(Symbols1.size(), 1u); + const auto &Sym1 = *Symbols1.begin(); + EXPECT_THAT(Sym1, QName("clang::Foo1")); + EXPECT_EQ(Sym1.Signature, ""); + EXPECT_EQ(Sym1.Documentation, "Foo doc"); + EXPECT_EQ(Sym1.ReturnType, "int"); + EXPECT_EQ(Sym1.CanonicalDeclaration.FileURI, "file:///path/foo.h"); + EXPECT_TRUE(Sym1.IsIndexedForCodeCompletion); + EXPECT_THAT(Sym1.IncludeHeaders, + UnorderedElementsAre(IncludeHeaderWithRef("include1", 7u), + IncludeHeaderWithRef("include2", 3u))); + + auto Symbols2 = symbolsFromYAML(YAML2); + ASSERT_EQ(Symbols2.size(), 1u); + const auto &Sym2 = *Symbols2.begin(); + EXPECT_THAT(Sym2, QName("clang::Foo2")); + EXPECT_EQ(Sym2.Signature, "-sig"); + EXPECT_EQ(Sym2.ReturnType, ""); + EXPECT_EQ(Sym2.CanonicalDeclaration.FileURI, "file:///path/bar.h"); + EXPECT_FALSE(Sym2.IsIndexedForCodeCompletion); + + std::string ConcatenatedYAML; + { + llvm::raw_string_ostream OS(ConcatenatedYAML); + SymbolsToYAML(Symbols1, OS); + SymbolsToYAML(Symbols2, OS); + } + auto ConcatenatedSymbols = symbolsFromYAML(ConcatenatedYAML); + EXPECT_THAT(ConcatenatedSymbols, + UnorderedElementsAre(QName("clang::Foo1"), QName("clang::Foo2"))); +} + +std::vector YAMLFromSymbols(const SymbolSlab &Slab) { + std::vector Result; + for (const auto &Sym : Slab) + Result.push_back(SymbolToYAML(Sym)); + return Result; +} + +TEST(SerializationTest, BinaryConversions) { + // We reuse the test symbols from YAML. + auto Slab = symbolsFromYAML(std::string(YAML1) + YAML2); + ASSERT_EQ(Slab.size(), 2u); + + // Write to binary format, and parse again. + IndexFileOut Out; + Out.Symbols = &Slab; + std::string Serialized = llvm::to_string(Out); + + auto In = readIndexFile(Serialized); + ASSERT_TRUE(bool(In)) << In.takeError(); + ASSERT_TRUE(In->Symbols); + + // Assert the YAML serializations match, for nice comparisons and diffs. + EXPECT_THAT(YAMLFromSymbols(*In->Symbols), + UnorderedElementsAreArray(YAMLFromSymbols(Slab))); +} + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/unittests/clangd/SymbolCollectorTests.cpp b/unittests/clangd/SymbolCollectorTests.cpp index cc3c7d186..6688daee1 100644 --- a/unittests/clangd/SymbolCollectorTests.cpp +++ b/unittests/clangd/SymbolCollectorTests.cpp @@ -48,7 +48,6 @@ using testing::UnorderedElementsAreArray; MATCHER_P(Labeled, Label, "") { return (arg.Name + arg.Signature).str() == Label; } -MATCHER(HasReturnType, "") { return !arg.ReturnType.empty(); } MATCHER_P(ReturnType, D, "") { return arg.ReturnType == D; } MATCHER_P(Doc, D, "") { return arg.Documentation == D; } MATCHER_P(Snippet, S, "") { @@ -744,84 +743,6 @@ TEST_F(SymbolCollectorTest, Snippet) { Snippet("ff(${1:int x}, ${2:double y})")))); } -TEST_F(SymbolCollectorTest, YAMLConversions) { - const std::string YAML1 = R"( ---- -ID: 057557CEBF6E6B2DD437FBF60CC58F352D1DF856 -Name: 'Foo1' -Scope: 'clang::' -SymInfo: - Kind: Function - Lang: Cpp -CanonicalDeclaration: - FileURI: file:///path/foo.h - Start: - Line: 1 - Column: 0 - End: - Line: 1 - Column: 1 -IsIndexedForCodeCompletion: true -Documentation: 'Foo doc' -ReturnType: 'int' -IncludeHeaders: - - Header: 'include1' - References: 7 - - Header: 'include2' - References: 3 -... -)"; - const std::string YAML2 = R"( ---- -ID: 057557CEBF6E6B2DD437FBF60CC58F352D1DF858 -Name: 'Foo2' -Scope: 'clang::' -SymInfo: - Kind: Function - Lang: Cpp -CanonicalDeclaration: - FileURI: file:///path/bar.h - Start: - Line: 1 - Column: 0 - End: - Line: 1 - Column: 1 -IsIndexedForCodeCompletion: false -Signature: '-sig' -CompletionSnippetSuffix: '-snippet' -... -)"; - - auto Symbols1 = symbolsFromYAML(YAML1); - - EXPECT_THAT(Symbols1, - UnorderedElementsAre(AllOf(QName("clang::Foo1"), Labeled("Foo1"), - Doc("Foo doc"), ReturnType("int"), - DeclURI("file:///path/foo.h"), - ForCodeCompletion(true)))); - auto &Sym1 = *Symbols1.begin(); - EXPECT_THAT(Sym1.IncludeHeaders, - UnorderedElementsAre(IncludeHeaderWithRef("include1", 7u), - IncludeHeaderWithRef("include2", 3u))); - auto Symbols2 = symbolsFromYAML(YAML2); - EXPECT_THAT(Symbols2, UnorderedElementsAre(AllOf( - QName("clang::Foo2"), Labeled("Foo2-sig"), - Not(HasReturnType()), DeclURI("file:///path/bar.h"), - ForCodeCompletion(false)))); - - std::string ConcatenatedYAML; - { - llvm::raw_string_ostream OS(ConcatenatedYAML); - SymbolsToYAML(Symbols1, OS); - SymbolsToYAML(Symbols2, OS); - } - auto ConcatenatedSymbols = symbolsFromYAML(ConcatenatedYAML); - EXPECT_THAT(ConcatenatedSymbols, - UnorderedElementsAre(QName("clang::Foo1"), - QName("clang::Foo2"))); -} - TEST_F(SymbolCollectorTest, IncludeHeaderSameAsFileURI) { CollectorOpts.CollectIncludePath = true; runSymbolCollector("class Foo {};", /*Main=*/""); From e30d4f1251a4a8028f5c703d3aeb62691f479c62 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 4 Sep 2018 16:19:40 +0000 Subject: [PATCH 133/686] [clangd] Load static index asynchronously, add tracing. Summary: Like D51475 but simplified based on recent patches. While here, clarify that loadIndex() takes a filename, not file content. Reviewers: ioeric Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51638 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341376 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/SymbolYAML.cpp | 13 ++++++++++--- clangd/index/SymbolYAML.h | 2 +- clangd/tool/ClangdMain.cpp | 10 ++++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/SymbolYAML.cpp index d90f4b0c4..78e13aefb 100644 --- a/clangd/index/SymbolYAML.cpp +++ b/clangd/index/SymbolYAML.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "SymbolYAML.h" +#include "../Trace.h" #include "Index.h" #include "Serialization.h" #include "dex/DexIndex.h" @@ -183,25 +184,31 @@ std::string SymbolToYAML(Symbol Sym) { return OS.str(); } -std::unique_ptr loadIndex(llvm::StringRef SymbolFile, +std::unique_ptr loadIndex(llvm::StringRef SymbolFilename, bool UseDex) { - auto Buffer = llvm::MemoryBuffer::getFile(SymbolFile); + trace::Span OverallTracer("LoadIndex"); + auto Buffer = llvm::MemoryBuffer::getFile(SymbolFilename); if (!Buffer) { - llvm::errs() << "Can't open " << SymbolFile << "\n"; + llvm::errs() << "Can't open " << SymbolFilename << "\n"; return nullptr; } StringRef Data = Buffer->get()->getBuffer(); llvm::Optional Slab; if (Data.startswith("RIFF")) { // Magic for binary index file. + trace::Span Tracer("ParseRIFF"); if (auto RIFF = readIndexFile(Data)) Slab = std::move(RIFF->Symbols); else llvm::errs() << "Bad RIFF: " << llvm::toString(RIFF.takeError()) << "\n"; } else { + trace::Span Tracer("ParseYAML"); Slab = symbolsFromYAML(Data); } + if (!Slab) + return nullptr; + trace::Span Tracer("BuildIndex"); return UseDex ? dex::DexIndex::build(std::move(*Slab)) : MemIndex::build(std::move(*Slab), RefSlab()); } diff --git a/clangd/index/SymbolYAML.h b/clangd/index/SymbolYAML.h index 1af51c075..3f75492f2 100644 --- a/clangd/index/SymbolYAML.h +++ b/clangd/index/SymbolYAML.h @@ -44,7 +44,7 @@ void SymbolsToYAML(const SymbolSlab &Symbols, llvm::raw_ostream &OS); // Build an in-memory static index for global symbols from a symbol file. // The size of global symbols should be relatively small, so that all symbols // can be managed in memory. -std::unique_ptr loadIndex(llvm::StringRef SymbolFile, +std::unique_ptr loadIndex(llvm::StringRef SymbolFilename, bool UseDex = true); } // namespace clangd diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index ffc1fec59..c7f499833 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -281,9 +281,15 @@ int main(int argc, char *argv[]) { Opts.BuildDynamicSymbolIndex = EnableIndex; std::unique_ptr StaticIdx; if (EnableIndex && !YamlSymbolFile.empty()) { - StaticIdx = loadIndex(YamlSymbolFile, UseDex); - Opts.StaticIndex = StaticIdx.get(); + // Load the index asynchronously. Meanwhile SwapIndex returns no results. + SwapIndex *Placeholder; + StaticIdx.reset(Placeholder = new SwapIndex(llvm::make_unique())); + runAsync([Placeholder] { + if (auto Idx = loadIndex(YamlSymbolFile)) + Placeholder->reset(std::move(Idx)); + }); } + Opts.StaticIndex = StaticIdx.get(); Opts.AsyncThreadsCount = WorkerThreadsCount; clangd::CodeCompleteOptions CCOpts; From f96ceb56d0be0f6d54d069d44cf8c42fae31cbc6 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Wed, 5 Sep 2018 07:40:38 +0000 Subject: [PATCH 134/686] [clangd] Tune macro quality scoring for code completion. x0.2 seems to be too much penalty, macros might be wanted in some cases; changing to 0.5x instead. The tuning didn't affect ranking for non-macro completions. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341449 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/Quality.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/Quality.cpp b/clangd/Quality.cpp index 5a9fcd2d4..7e0e8372b 100644 --- a/clangd/Quality.cpp +++ b/clangd/Quality.cpp @@ -221,7 +221,7 @@ float SymbolQualitySignals::evaluate() const { Score *= 0.8f; break; case Macro: - Score *= 0.2f; + Score *= 0.5f; break; case Unknown: case Constructor: // No boost constructors so they are after class types. From 264606343159dec6d09e5c51657d0171e3bf4f50 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Wed, 5 Sep 2018 07:52:49 +0000 Subject: [PATCH 135/686] [clangd] Fix buildbot failures on older compilers from r341375 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341451 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/RIFF.cpp | 8 ++++---- clangd/index/Serialization.cpp | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clangd/RIFF.cpp b/clangd/RIFF.cpp index cb832db87..28c5ae6b9 100644 --- a/clangd/RIFF.cpp +++ b/clangd/RIFF.cpp @@ -36,11 +36,11 @@ Expected readChunk(StringRef &Stream) { return makeError("nonzero padding byte"); Stream = Stream.drop_front(); } - return C; + return std::move(C); }; raw_ostream &operator<<(raw_ostream &OS, const Chunk &C) { - OS.write(C.ID.begin(), C.ID.size()); + OS.write(C.ID.data(), C.ID.size()); char Size[4]; llvm::support::endian::write32le(Size, C.Data.size()); OS.write(Size, sizeof(Size)); @@ -65,7 +65,7 @@ llvm::Expected readFile(llvm::StringRef Stream) { F.Chunks.push_back(*Chunk); } else return Chunk.takeError(); - return F; + return std::move(F); } raw_ostream &operator<<(raw_ostream &OS, const File &F) { @@ -77,7 +77,7 @@ raw_ostream &operator<<(raw_ostream &OS, const File &F) { char Size[4]; llvm::support::endian::write32le(Size, DataLen); OS.write(Size, sizeof(Size)); - OS.write(F.Type.begin(), F.Type.size()); + OS.write(F.Type.data(), F.Type.size()); for (const auto &C : F.Chunks) OS << C; return OS; diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index df88142d1..5e732e969 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -151,7 +151,7 @@ Expected readStringTable(StringRef Data) { Table.Strings.push_back(Saver.save(consume(Rest, Len))); Rest = Rest.drop_front(); } - return Table; + return std::move(Table); } // SYMBOL ENCODING @@ -272,7 +272,7 @@ Expected readSymbol(StringRef &Data, const StringTableIn &Strings) { } #undef READ_STRING - return Sym; + return std::move(Sym); } } // namespace @@ -322,7 +322,7 @@ Expected readIndexFile(StringRef Data) { return Sym.takeError(); Result.Symbols = std::move(Symbols).build(); } - return Result; + return std::move(Result); } raw_ostream &operator<<(raw_ostream &OS, const IndexFileOut &Data) { From b6a12633f414268de3bc7e2d5465be6b9a66467f Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 5 Sep 2018 08:01:37 +0000 Subject: [PATCH 136/686] [clangd] Fix typo. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341452 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/Cancellation.h | 2 +- clangd/CodeComplete.cpp | 2 +- clangd/FindSymbols.cpp | 2 +- clangd/JSONRPCDispatcher.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clangd/Cancellation.h b/clangd/Cancellation.h index d7868e756..58f845691 100644 --- a/clangd/Cancellation.h +++ b/clangd/Cancellation.h @@ -93,7 +93,7 @@ class Task { /// extra lookups in the Context. bool isCancelled() const { return CT; } - /// Creates a task handle that can be used by an asyn task to check for + /// Creates a task handle that can be used by an async task to check for /// information that can change during it's runtime, like Cancellation. static std::shared_ptr createHandle() { return std::shared_ptr(new Task()); diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 89e8acbac..73357d8db 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -1629,7 +1629,7 @@ CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const { LSP.sortText = sortText(Score.Total, Name); LSP.filterText = Name; LSP.textEdit = {CompletionTokenRange, RequiredQualifier + Name}; - // Merge continious additionalTextEdits into main edit. The main motivation + // Merge continuous additionalTextEdits into main edit. The main motivation // behind this is to help LSP clients, it seems most of them are confused when // they are provided with additionalTextEdits that are consecutive to main // edit. diff --git a/clangd/FindSymbols.cpp b/clangd/FindSymbols.cpp index cc7f084f2..b808e374e 100644 --- a/clangd/FindSymbols.cpp +++ b/clangd/FindSymbols.cpp @@ -226,7 +226,7 @@ class DocumentSymbolsConsumer : public index::IndexDataConsumer { // We should be only be looking at "local" decls in the main file. if (!SourceMgr.isWrittenInMainFile(NameLoc)) { // Even thought we are visiting only local (non-preamble) decls, - // we can get here when in the presense of "extern" decls. + // we can get here when in the presence of "extern" decls. return true; } const NamedDecl *ND = llvm::dyn_cast(ASTNode.OrigD); diff --git a/clangd/JSONRPCDispatcher.cpp b/clangd/JSONRPCDispatcher.cpp index 9dea83ae8..a3dd9ac4a 100644 --- a/clangd/JSONRPCDispatcher.cpp +++ b/clangd/JSONRPCDispatcher.cpp @@ -28,7 +28,7 @@ namespace { static Key RequestID; static Key RequestOut; -// When tracing, we trace a request and attach the repsonse in reply(). +// When tracing, we trace a request and attach the response in reply(). // Because the Span isn't available, we find the current request using Context. class RequestSpan { RequestSpan(llvm::json::Object *Args) : Args(Args) {} From 68bf5fb1280a62ae6f8f64e7f3b8a5a4ca9ef0fb Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Wed, 5 Sep 2018 10:33:36 +0000 Subject: [PATCH 137/686] [clangd] Implement findReferences function clangd will use findReferences to provide LSP's reference feature. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341458 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/XRefs.cpp | 201 +++++++++++++++++++------------- clangd/XRefs.h | 4 + unittests/clangd/TestTU.cpp | 6 +- unittests/clangd/XRefsTests.cpp | 137 ++++++++++++++++++++++ 4 files changed, 268 insertions(+), 80 deletions(-) diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index a4cbf549c..1f11fca4b 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -174,30 +174,27 @@ IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos) { return {DeclMacrosFinder.takeDecls(), DeclMacrosFinder.takeMacroInfos()}; } -llvm::Optional -makeLocation(ParsedAST &AST, const SourceRange &ValSourceRange) { +Range getTokenRange(ParsedAST &AST, SourceLocation TokLoc) { const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); - const LangOptions &LangOpts = AST.getASTContext().getLangOpts(); - SourceLocation LocStart = ValSourceRange.getBegin(); + SourceLocation LocEnd = Lexer::getLocForEndOfToken( + TokLoc, 0, SourceMgr, AST.getASTContext().getLangOpts()); + return {sourceLocToPosition(SourceMgr, TokLoc), + sourceLocToPosition(SourceMgr, LocEnd)}; +} - const FileEntry *F = - SourceMgr.getFileEntryForID(SourceMgr.getFileID(LocStart)); +llvm::Optional makeLocation(ParsedAST &AST, SourceLocation TokLoc) { + const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); + const FileEntry *F = SourceMgr.getFileEntryForID(SourceMgr.getFileID(TokLoc)); if (!F) return llvm::None; - SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(), 0, - SourceMgr, LangOpts); - Position Begin = sourceLocToPosition(SourceMgr, LocStart); - Position End = sourceLocToPosition(SourceMgr, LocEnd); - Range R = {Begin, End}; - Location L; - auto FilePath = getRealPath(F, SourceMgr); if (!FilePath) { log("failed to get path!"); return llvm::None; } + Location L; L.uri = URIForFile(*FilePath); - L.range = R; + L.range = getTokenRange(AST, TokLoc); return L; } @@ -223,7 +220,7 @@ std::vector findDefinitions(ParsedAST &AST, Position Pos, for (auto Item : Symbols.Macros) { auto Loc = Item.Info->getDefinitionLoc(); - auto L = makeLocation(AST, SourceRange(Loc, Loc)); + auto L = makeLocation(AST, Loc); if (L) Result.push_back(*L); } @@ -266,7 +263,7 @@ std::vector findDefinitions(ParsedAST &AST, Position Pos, auto &Candidate = ResultCandidates[Key]; auto Loc = findNameLoc(D); - auto L = makeLocation(AST, SourceRange(Loc, Loc)); + auto L = makeLocation(AST, Loc); // The declaration in the identified symbols is a definition if possible // otherwise it is declaration. bool IsDef = getDefinition(D) == D; @@ -316,24 +313,36 @@ std::vector findDefinitions(ParsedAST &AST, Position Pos, namespace { -/// Finds document highlights that a given list of declarations refers to. -class DocumentHighlightsFinder : public index::IndexDataConsumer { - std::vector &Decls; - std::vector DocumentHighlights; - const ASTContext &AST; - +/// Collects references to symbols within the main file. +class ReferenceFinder : public index::IndexDataConsumer { public: - DocumentHighlightsFinder(ASTContext &AST, Preprocessor &PP, - std::vector &Decls) - : Decls(Decls), AST(AST) {} - std::vector takeHighlights() { - // Don't keep the same highlight multiple times. - // This can happen when nodes in the AST are visited twice. - std::sort(DocumentHighlights.begin(), DocumentHighlights.end()); - auto Last = - std::unique(DocumentHighlights.begin(), DocumentHighlights.end()); - DocumentHighlights.erase(Last, DocumentHighlights.end()); - return std::move(DocumentHighlights); + struct Reference { + const Decl *Target; + SourceLocation Loc; + index::SymbolRoleSet Role; + }; + + ReferenceFinder(ASTContext &AST, Preprocessor &PP, + const std::vector &TargetDecls) + : AST(AST) { + for (const Decl *D : TargetDecls) + Targets.insert(D); + } + + std::vector take() && { + std::sort(References.begin(), References.end(), + [](const Reference &L, const Reference &R) { + return std::tie(L.Loc, L.Target, L.Role) < + std::tie(R.Loc, R.Target, R.Role); + }); + // We sometimes see duplicates when parts of the AST get traversed twice. + References.erase(std::unique(References.begin(), References.end(), + [](const Reference &L, const Reference &R) { + return std::tie(L.Target, L.Loc, L.Role) == + std::tie(R.Target, R.Loc, R.Role); + }), + References.end()); + return std::move(References); } bool @@ -341,63 +350,53 @@ class DocumentHighlightsFinder : public index::IndexDataConsumer { ArrayRef Relations, SourceLocation Loc, index::IndexDataConsumer::ASTNodeInfo ASTNode) override { - const SourceManager &SourceMgr = AST.getSourceManager(); - SourceLocation HighlightStartLoc = SourceMgr.getFileLoc(Loc); - if (SourceMgr.getMainFileID() != SourceMgr.getFileID(HighlightStartLoc) || - std::find(Decls.begin(), Decls.end(), D) == Decls.end()) { - return true; - } - SourceLocation End; - const LangOptions &LangOpts = AST.getLangOpts(); - End = Lexer::getLocForEndOfToken(HighlightStartLoc, 0, SourceMgr, LangOpts); - SourceRange SR(HighlightStartLoc, End); - - DocumentHighlightKind Kind = DocumentHighlightKind::Text; - if (static_cast(index::SymbolRole::Write) & Roles) - Kind = DocumentHighlightKind::Write; - else if (static_cast(index::SymbolRole::Read) & Roles) - Kind = DocumentHighlightKind::Read; - - DocumentHighlights.push_back(getDocumentHighlight(SR, Kind)); + const SourceManager &SM = AST.getSourceManager(); + Loc = SM.getFileLoc(Loc); + if (SM.isWrittenInMainFile(Loc) && Targets.count(D)) + References.push_back({D, Loc, Roles}); return true; } private: - DocumentHighlight getDocumentHighlight(SourceRange SR, - DocumentHighlightKind Kind) { - const SourceManager &SourceMgr = AST.getSourceManager(); - Position Begin = sourceLocToPosition(SourceMgr, SR.getBegin()); - Position End = sourceLocToPosition(SourceMgr, SR.getEnd()); - Range R = {Begin, End}; - DocumentHighlight DH; - DH.range = R; - DH.kind = Kind; - return DH; - } + llvm::SmallSet Targets; + std::vector References; + const ASTContext &AST; }; -} // namespace - -std::vector findDocumentHighlights(ParsedAST &AST, - Position Pos) { - const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); - SourceLocation SourceLocationBeg = - getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID()); - - auto Symbols = getSymbolAtPosition(AST, SourceLocationBeg); - std::vector SelectedDecls = Symbols.Decls; - - DocumentHighlightsFinder DocHighlightsFinder( - AST.getASTContext(), AST.getPreprocessor(), SelectedDecls); - +std::vector +findRefs(const std::vector &Decls, ParsedAST &AST) { + ReferenceFinder RefFinder(AST.getASTContext(), AST.getPreprocessor(), Decls); index::IndexingOptions IndexOpts; IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All; IndexOpts.IndexFunctionLocals = true; indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(), - DocHighlightsFinder, IndexOpts); + RefFinder, IndexOpts); + return std::move(RefFinder).take(); +} + +} // namespace + +std::vector findDocumentHighlights(ParsedAST &AST, + Position Pos) { + const SourceManager &SM = AST.getASTContext().getSourceManager(); + auto Symbols = getSymbolAtPosition( + AST, getBeginningOfIdentifier(AST, Pos, SM.getMainFileID())); + auto References = findRefs(Symbols.Decls, AST); - return DocHighlightsFinder.takeHighlights(); + std::vector Result; + for (const auto &Ref : References) { + DocumentHighlight DH; + DH.range = getTokenRange(AST, Ref.Loc); + if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Write)) + DH.kind = DocumentHighlightKind::Write; + else if (Ref.Role & index::SymbolRoleSet(index::SymbolRole::Read)) + DH.kind = DocumentHighlightKind::Read; + else + DH.kind = DocumentHighlightKind::Text; + Result.push_back(std::move(DH)); + } + return Result; } static PrintingPolicy printingPolicyForDecls(PrintingPolicy Base) { @@ -659,5 +658,51 @@ Optional getHover(ParsedAST &AST, Position Pos) { return None; } +std::vector findReferences(ParsedAST &AST, Position Pos, + const SymbolIndex *Index) { + std::vector Results; + const SourceManager &SM = AST.getASTContext().getSourceManager(); + auto MainFilePath = getRealPath(SM.getFileEntryForID(SM.getMainFileID()), SM); + if (!MainFilePath) { + elog("Failed to get a path for the main file, so no references"); + return Results; + } + auto Loc = getBeginningOfIdentifier(AST, Pos, SM.getMainFileID()); + auto Symbols = getSymbolAtPosition(AST, Loc); + + // We traverse the AST to find references in the main file. + // TODO: should we handle macros, too? + auto MainFileRefs = findRefs(Symbols.Decls, AST); + for (const auto &Ref : MainFileRefs) { + Location Result; + Result.range = getTokenRange(AST, Ref.Loc); + Result.uri = URIForFile(*MainFilePath); + Results.push_back(std::move(Result)); + } + + // Now query the index for references from other files. + if (!Index) + return Results; + RefsRequest Req; + for (const Decl *D : Symbols.Decls) { + // Not all symbols can be referenced from outside (e.g. function-locals). + // TODO: we could skip TU-scoped symbols here (e.g. static functions) if + // we know this file isn't a header. The details might be tricky. + if (D->getParentFunctionOrMethod()) + continue; + if (auto ID = getSymbolID(D)) + Req.IDs.insert(*ID); + } + if (Req.IDs.empty()) + return Results; + Index->refs(Req, [&](const Ref &R) { + auto LSPLoc = toLSPLocation(R.Location, /*HintPath=*/*MainFilePath); + // Avoid indexed results for the main file - the AST is authoritative. + if (LSPLoc && LSPLoc->uri.file() != *MainFilePath) + Results.push_back(std::move(*LSPLoc)); + }); + return Results; +} + } // namespace clangd } // namespace clang diff --git a/clangd/XRefs.h b/clangd/XRefs.h index 30b531951..0b7b4191d 100644 --- a/clangd/XRefs.h +++ b/clangd/XRefs.h @@ -34,6 +34,10 @@ std::vector findDocumentHighlights(ParsedAST &AST, /// Get the hover information when hovering at \p Pos. llvm::Optional getHover(ParsedAST &AST, Position Pos); +/// Returns reference locations of the symbol at a specified \p Pos. +std::vector findReferences(ParsedAST &AST, Position Pos, + const SymbolIndex *Index = nullptr); + } // namespace clangd } // namespace clang diff --git a/unittests/clangd/TestTU.cpp b/unittests/clangd/TestTU.cpp index d4b442cb5..e181f689c 100644 --- a/unittests/clangd/TestTU.cpp +++ b/unittests/clangd/TestTU.cpp @@ -49,8 +49,10 @@ SymbolSlab TestTU::headerSymbols() const { } std::unique_ptr TestTU::index() const { - // FIXME: we should generate proper refs for TestTU. - return MemIndex::build(headerSymbols(), RefSlab()); + auto AST = build(); + auto Content = indexAST(AST.getASTContext(), AST.getPreprocessorPtr(), + AST.getLocalTopLevelDecls()); + return MemIndex::build(std::move(Content.first), std::move(Content.second)); } const Symbol &findSymbol(const SymbolSlab &Slab, llvm::StringRef QName) { diff --git a/unittests/clangd/XRefsTests.cpp b/unittests/clangd/XRefsTests.cpp index 47af72561..3bf09722f 100644 --- a/unittests/clangd/XRefsTests.cpp +++ b/unittests/clangd/XRefsTests.cpp @@ -1068,6 +1068,143 @@ TEST(GoToDefinition, WithPreamble) { ElementsAre(Location{FooCppUri, FooWithoutHeader.range()})); } +TEST(FindReferences, WithinAST) { + const char *Tests[] = { + R"cpp(// Local variable + int main() { + int $foo[[foo]]; + $foo[[^foo]] = 2; + int test1 = $foo[[foo]]; + } + )cpp", + + R"cpp(// Struct + namespace ns1 { + struct $foo[[Foo]] {}; + } // namespace ns1 + int main() { + ns1::$foo[[Fo^o]]* Params; + } + )cpp", + + R"cpp(// Function + int $foo[[foo]](int) {} + int main() { + auto *X = &$foo[[^foo]]; + $foo[[foo]](42) + } + )cpp", + + R"cpp(// Field + struct Foo { + int $foo[[foo]]; + Foo() : $foo[[foo]](0) {} + }; + int main() { + Foo f; + f.$foo[[f^oo]] = 1; + } + )cpp", + + R"cpp(// Method call + struct Foo { int [[foo]](); }; + int Foo::[[foo]]() {} + int main() { + Foo f; + f.^foo(); + } + )cpp", + + R"cpp(// Typedef + typedef int $foo[[Foo]]; + int main() { + $foo[[^Foo]] bar; + } + )cpp", + + R"cpp(// Namespace + namespace $foo[[ns]] { + struct Foo {}; + } // namespace ns + int main() { $foo[[^ns]]::Foo foo; } + )cpp", + }; + for (const char *Test : Tests) { + Annotations T(Test); + auto AST = TestTU::withCode(T.code()).build(); + std::vector> ExpectedLocations; + for (const auto &R : T.ranges("foo")) + ExpectedLocations.push_back(RangeIs(R)); + EXPECT_THAT(findReferences(AST, T.point()), + ElementsAreArray(ExpectedLocations)) + << Test; + } +} + +TEST(FindReferences, NeedsIndex) { + const char *Header = "int foo();"; + Annotations Main("int main() { [[f^oo]](); }"); + TestTU TU; + TU.Code = Main.code(); + TU.HeaderCode = Header; + auto AST = TU.build(); + + // References in main file are returned without index. + EXPECT_THAT(findReferences(AST, Main.point(), /*Index=*/nullptr), + ElementsAre(RangeIs(Main.range()))); + Annotations IndexedMain(R"cpp( + int main() { [[f^oo]](); } + )cpp"); + + // References from indexed files are included. + TestTU IndexedTU; + IndexedTU.Code = IndexedMain.code(); + IndexedTU.Filename = "Indexed.cpp"; + IndexedTU.HeaderCode = Header; + EXPECT_THAT(findReferences(AST, Main.point(), IndexedTU.index().get()), + ElementsAre(RangeIs(Main.range()), RangeIs(IndexedMain.range()))); + + // If the main file is in the index, we don't return duplicates. + // (even if the references are in a different location) + TU.Code = ("\n\n" + Main.code()).str(); + EXPECT_THAT(findReferences(AST, Main.point(), TU.index().get()), + ElementsAre(RangeIs(Main.range()))); +}; + +TEST(FindReferences, NoQueryForLocalSymbols) { + struct RecordingIndex : public MemIndex { + mutable Optional> RefIDs; + void refs(const RefsRequest &Req, + llvm::function_ref) const override { + RefIDs = Req.IDs; + } + }; + + struct Test { + StringRef AnnotatedCode; + bool WantQuery; + } Tests[] = { + {"int ^x;", true}, + // For now we don't assume header structure which would allow skipping. + {"namespace { int ^x; }", true}, + {"static int ^x;", true}, + // Anything in a function certainly can't be referenced though. + {"void foo() { int ^x; }", false}, + {"void foo() { struct ^x{}; }", false}, + {"auto lambda = []{ int ^x; };", false}, + }; + for (Test T : Tests) { + Annotations File(T.AnnotatedCode); + RecordingIndex Rec; + auto AST = TestTU::withCode(File.code()).build(); + findReferences(AST, File.point(), &Rec); + if (T.WantQuery) + EXPECT_NE(Rec.RefIDs, llvm::None) << T.AnnotatedCode; + else + EXPECT_EQ(Rec.RefIDs, llvm::None) << T.AnnotatedCode; + } +}; + } // namespace } // namespace clangd } // namespace clang From f78449bedc46cbed30f6d1bd7d2e397a8da09b73 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Wed, 5 Sep 2018 10:39:58 +0000 Subject: [PATCH 138/686] [clangd] Avoid enum class+enumValN to avoid GCC bug(?), and use consistent style. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341459 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../GlobalSymbolBuilderMain.cpp | 2 +- clangd/tool/ClangdMain.cpp | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp b/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp index 4fbc0a7e5..eda8c12d4 100644 --- a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp +++ b/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp @@ -60,7 +60,7 @@ static llvm::cl::opt MergeOnTheFly( "MapReduce."), llvm::cl::init(true), llvm::cl::Hidden); -enum class Format { YAML, Binary }; +enum Format { YAML, Binary }; static llvm::cl::opt Format("format", llvm::cl::desc("Format of the index to be written"), llvm::cl::values( diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index c7f499833..39dd0833f 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -36,12 +36,6 @@ static llvm::cl::opt llvm::cl::desc("Use experimental Dex static index."), llvm::cl::init(true), llvm::cl::Hidden); -namespace { - -enum class PCHStorageFlag { Disk, Memory }; - -} // namespace - static llvm::cl::opt CompileCommandsDir( "compile-commands-dir", llvm::cl::desc("Specify a path to look for compile_commands.json. If path " @@ -54,10 +48,7 @@ static llvm::cl::opt llvm::cl::init(getDefaultAsyncThreadsCount())); // FIXME: also support "plain" style where signatures are always omitted. -enum CompletionStyleFlag { - Detailed, - Bundled, -}; +enum CompletionStyleFlag { Detailed, Bundled }; static llvm::cl::opt CompletionStyle( "completion-style", llvm::cl::desc("Granularity of code completion suggestions"), @@ -106,6 +97,7 @@ static llvm::cl::opt Test( "Intended to simplify lit tests."), llvm::cl::init(false), llvm::cl::Hidden); +enum PCHStorageFlag { Disk, Memory }; static llvm::cl::opt PCHStorage( "pch-storage", llvm::cl::desc("Storing PCHs in memory increases memory usages, but may " @@ -167,7 +159,6 @@ static llvm::cl::opt YamlSymbolFile( llvm::cl::init(""), llvm::cl::Hidden); enum CompileArgsFrom { LSPCompileArgs, FilesystemCompileArgs }; - static llvm::cl::opt CompileArgsFrom( "compile_args_from", llvm::cl::desc("The source of compile commands"), llvm::cl::values(clEnumValN(LSPCompileArgs, "lsp", From c40e187a82ca06797296b6af0e1f0820c90e86cd Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Wed, 5 Sep 2018 11:53:07 +0000 Subject: [PATCH 139/686] [clangd] Add xrefs LSP boilerplate implementation. Reviewers: ilya-biryukov, ioeric Subscribers: MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50896 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341462 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 12 +++++++ clangd/ClangdLSPServer.h | 1 + clangd/ClangdServer.cpp | 12 +++++++ clangd/ClangdServer.h | 4 +++ clangd/Protocol.cpp | 5 +++ clangd/Protocol.h | 5 +++ clangd/ProtocolHandlers.cpp | 1 + clangd/ProtocolHandlers.h | 1 + clangd/XRefs.cpp | 1 + test/clangd/initialize-params-invalid.test | 36 +------------------ test/clangd/initialize-params.test | 1 + test/clangd/references.test | 40 ++++++++++++++++++++++ 12 files changed, 84 insertions(+), 35 deletions(-) create mode 100644 test/clangd/references.test diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index 98c50d1fe..53dc8aced 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -129,6 +129,7 @@ void ClangdLSPServer::onInitialize(InitializeParams &Params) { {"renameProvider", true}, {"documentSymbolProvider", true}, {"workspaceSymbolProvider", true}, + {"referencesProvider", true}, {"executeCommandProvider", json::Object{ {"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}}, @@ -449,6 +450,17 @@ void ClangdLSPServer::onChangeConfiguration( applyConfiguration(Params.settings); } +void ClangdLSPServer::onReference(ReferenceParams &Params) { + Server.findReferences(Params.textDocument.uri.file(), Params.position, + [](llvm::Expected> Locations) { + if (!Locations) + return replyError( + ErrorCode::InternalError, + llvm::toString(Locations.takeError())); + reply(llvm::json::Array(*Locations)); + }); +} + ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts, llvm::Optional CompileCommandsDir, diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h index 0a714491a..d717de56c 100644 --- a/clangd/ClangdLSPServer.h +++ b/clangd/ClangdLSPServer.h @@ -67,6 +67,7 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { void onCompletion(TextDocumentPositionParams &Params) override; void onSignatureHelp(TextDocumentPositionParams &Params) override; void onGoToDefinition(TextDocumentPositionParams &Params) override; + void onReference(ReferenceParams &Params) override; void onSwitchSourceHeader(TextDocumentIdentifier &Params) override; void onDocumentHighlight(TextDocumentPositionParams &Params) override; void onFileEvent(DidChangeWatchedFilesParams &Params) override; diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 5bd2a910d..0a8e748b2 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -560,6 +560,18 @@ void ClangdServer::documentSymbols( Bind(Action, std::move(CB))); } +void ClangdServer::findReferences(PathRef File, Position Pos, + Callback> CB) { + auto Action = [Pos, this](Callback> CB, + llvm::Expected InpAST) { + if (!InpAST) + return CB(InpAST.takeError()); + CB(clangd::findReferences(InpAST->AST, Pos, Index)); + }; + + WorkScheduler.runWithAST("References", File, Bind(Action, std::move(CB))); +} + std::vector> ClangdServer::getUsedBytesPerFile() const { return WorkScheduler.getUsedBytesPerFile(); diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h index 50a1aea58..e459fccee 100644 --- a/clangd/ClangdServer.h +++ b/clangd/ClangdServer.h @@ -157,6 +157,10 @@ class ClangdServer { void documentSymbols(StringRef File, Callback> CB); + /// Retrieve locations for symbol references. + void findReferences(PathRef File, Position Pos, + Callback> CB); + /// Run formatting for \p Rng inside \p File with content \p Code. llvm::Expected formatRange(StringRef Code, PathRef File, Range Rng); diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp index 609cfa621..42093797b 100644 --- a/clangd/Protocol.cpp +++ b/clangd/Protocol.cpp @@ -616,6 +616,11 @@ bool fromJSON(const json::Value &Params, O.map("compilationDatabaseChanges", CCPC.compilationDatabaseChanges); } +bool fromJSON(const json::Value &Params, ReferenceParams &R) { + TextDocumentPositionParams &Base = R; + return fromJSON(Params, Base); +} + json::Value toJSON(const CancelParams &CP) { return json::Object{{"id", CP.ID}}; } diff --git a/clangd/Protocol.h b/clangd/Protocol.h index 2e291bd11..d97713bea 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -878,6 +878,11 @@ struct DocumentHighlight { llvm::json::Value toJSON(const DocumentHighlight &DH); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DocumentHighlight &); +struct ReferenceParams : public TextDocumentPositionParams { + // For now, no options like context.includeDeclaration are supported. +}; +bool fromJSON(const llvm::json::Value &, ReferenceParams &); + struct CancelParams { /// The request id to cancel. /// This can be either a number or string, if it is a number simply print it diff --git a/clangd/ProtocolHandlers.cpp b/clangd/ProtocolHandlers.cpp index 5cf370d44..9bb21e3e3 100644 --- a/clangd/ProtocolHandlers.cpp +++ b/clangd/ProtocolHandlers.cpp @@ -63,6 +63,7 @@ void clangd::registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, Register("textDocument/completion", &ProtocolCallbacks::onCompletion); Register("textDocument/signatureHelp", &ProtocolCallbacks::onSignatureHelp); Register("textDocument/definition", &ProtocolCallbacks::onGoToDefinition); + Register("textDocument/references", &ProtocolCallbacks::onReference); Register("textDocument/switchSourceHeader", &ProtocolCallbacks::onSwitchSourceHeader); Register("textDocument/rename", &ProtocolCallbacks::onRename); diff --git a/clangd/ProtocolHandlers.h b/clangd/ProtocolHandlers.h index cfbac5252..72ecd2b26 100644 --- a/clangd/ProtocolHandlers.h +++ b/clangd/ProtocolHandlers.h @@ -47,6 +47,7 @@ class ProtocolCallbacks { virtual void onCompletion(TextDocumentPositionParams &Params) = 0; virtual void onSignatureHelp(TextDocumentPositionParams &Params) = 0; virtual void onGoToDefinition(TextDocumentPositionParams &Params) = 0; + virtual void onReference(ReferenceParams &Params) = 0; virtual void onSwitchSourceHeader(TextDocumentIdentifier &Params) = 0; virtual void onFileEvent(DidChangeWatchedFilesParams &Params) = 0; virtual void onCommand(ExecuteCommandParams &Params) = 0; diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index 1f11fca4b..efb19714d 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -17,6 +17,7 @@ #include "clang/Index/IndexingAction.h" #include "clang/Index/USRGeneration.h" #include "llvm/Support/Path.h" + namespace clang { namespace clangd { using namespace llvm; diff --git a/test/clangd/initialize-params-invalid.test b/test/clangd/initialize-params-invalid.test index 99c04c623..f51a7279c 100644 --- a/test/clangd/initialize-params-invalid.test +++ b/test/clangd/initialize-params-invalid.test @@ -5,41 +5,7 @@ # CHECK-NEXT: "jsonrpc": "2.0", # CHECK-NEXT: "result": { # CHECK-NEXT: "capabilities": { -# CHECK-NEXT: "codeActionProvider": true, -# CHECK-NEXT: "completionProvider": { -# CHECK-NEXT: "resolveProvider": false, -# CHECK-NEXT: "triggerCharacters": [ -# CHECK-NEXT: ".", -# CHECK-NEXT: ">", -# CHECK-NEXT: ":" -# CHECK-NEXT: ] -# CHECK-NEXT: }, -# CHECK-NEXT: "definitionProvider": true, -# CHECK-NEXT: "documentFormattingProvider": true, -# CHECK-NEXT: "documentHighlightProvider": true, -# CHECK-NEXT: "documentOnTypeFormattingProvider": { -# CHECK-NEXT: "firstTriggerCharacter": "}", -# CHECK-NEXT: "moreTriggerCharacter": [] -# CHECK-NEXT: }, -# CHECK-NEXT: "documentRangeFormattingProvider": true, -# CHECK-NEXT: "documentSymbolProvider": true, -# CHECK-NEXT: "executeCommandProvider": { -# CHECK-NEXT: "commands": [ -# CHECK-NEXT: "clangd.applyFix" -# CHECK-NEXT: ] -# CHECK-NEXT: }, -# CHECK-NEXT: "hoverProvider": true, -# CHECK-NEXT: "renameProvider": true, -# CHECK-NEXT: "signatureHelpProvider": { -# CHECK-NEXT: "triggerCharacters": [ -# CHECK-NEXT: "(", -# CHECK-NEXT: "," -# CHECK-NEXT: ] -# CHECK-NEXT: }, -# CHECK-NEXT: "textDocumentSync": 2, -# CHECK-NEXT: "workspaceSymbolProvider": true -# CHECK-NEXT: } -# CHECK-NEXT: } +# ... --- {"jsonrpc":"2.0","id":3,"method":"shutdown"} --- diff --git a/test/clangd/initialize-params.test b/test/clangd/initialize-params.test index d22bf80ef..62f7b41ad 100644 --- a/test/clangd/initialize-params.test +++ b/test/clangd/initialize-params.test @@ -29,6 +29,7 @@ # CHECK-NEXT: ] # CHECK-NEXT: }, # CHECK-NEXT: "hoverProvider": true, +# CHECK-NEXT: "referencesProvider": true, # CHECK-NEXT: "renameProvider": true, # CHECK-NEXT: "signatureHelpProvider": { # CHECK-NEXT: "triggerCharacters": [ diff --git a/test/clangd/references.test b/test/clangd/references.test new file mode 100644 index 000000000..e6294d9b0 --- /dev/null +++ b/test/clangd/references.test @@ -0,0 +1,40 @@ +# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///main.cpp","languageId":"cpp","version":1,"text":"int x; int y = x;"}}} +--- +{"jsonrpc":"2.0","id":1,"method":"textDocument/references","params":{"textDocument":{"uri":"test:///main.cpp"},"position":{"line":0,"character":4}}} +# CHECK: "id": 1 +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": [ +# CHECK-NEXT: { +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 5, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 4, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "uri": "test:///main.cpp" +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 16, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 15, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "uri": "test:///main.cpp" +# CHECK-NEXT: }, +# CHECK-NEXT: ] +--- +{"jsonrpc":"2.0","id":3,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} From a4bce51e372fc67f68efdb316a901dba021a8544 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Wed, 5 Sep 2018 12:00:15 +0000 Subject: [PATCH 140/686] [clangd] Sort GoToDefinition results. Summary: GoToDefinition returns all declaration results (implicit/explicit) that are in the same location, and the results are returned in arbitrary order. Some LSP clients defaultly take the first result as the final result, which might present a bad result (implicit decl) to users. This patch ranks the result based on whether the declarations are referenced explicitly/implicitly. We put explicit declarations first. This also improves the "hover" (which just take the first result) feature in some cases. Reviewers: ilya-biryukov Subscribers: kadircet, ioeric, MaskRay, jkorous, mgrang, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D50438 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341463 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/XRefs.cpp | 119 +++++++++++++++++++++++--------- unittests/clangd/XRefsTests.cpp | 41 +++++++++++ 2 files changed, 127 insertions(+), 33 deletions(-) diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index efb19714d..33181576a 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -69,10 +69,20 @@ struct MacroDecl { const MacroInfo *Info; }; +struct DeclInfo { + const Decl *D; + // Indicates the declaration is referenced by an explicit AST node. + bool IsReferencedExplicitly = false; +}; + /// Finds declarations locations that a given source location refers to. class DeclarationAndMacrosFinder : public index::IndexDataConsumer { - std::vector Decls; std::vector MacroInfos; + // The value of the map indicates whether the declaration has been referenced + // explicitly in the code. + // True means the declaration is explicitly referenced at least once; false + // otherwise. + llvm::DenseMap Decls; const SourceLocation &SearchedLocation; const ASTContext &AST; Preprocessor &PP; @@ -82,13 +92,25 @@ class DeclarationAndMacrosFinder : public index::IndexDataConsumer { ASTContext &AST, Preprocessor &PP) : SearchedLocation(SearchedLocation), AST(AST), PP(PP) {} - std::vector takeDecls() { - // Don't keep the same declaration multiple times. - // This can happen when nodes in the AST are visited twice. - std::sort(Decls.begin(), Decls.end()); - auto Last = std::unique(Decls.begin(), Decls.end()); - Decls.erase(Last, Decls.end()); - return std::move(Decls); + // Get all DeclInfo of the found declarations. + // The results are sorted by "IsReferencedExplicitly" and declaration + // location. + std::vector getFoundDecls() const { + std::vector Result; + for (auto It : Decls) { + Result.emplace_back(); + Result.back().D = It.first; + Result.back().IsReferencedExplicitly = It.second; + } + + // Sort results. Declarations being referenced explicitly come first. + std::sort(Result.begin(), Result.end(), + [](const DeclInfo &L, const DeclInfo &R) { + if (L.IsReferencedExplicitly != R.IsReferencedExplicitly) + return L.IsReferencedExplicitly > R.IsReferencedExplicitly; + return L.D->getBeginLoc() < R.D->getBeginLoc(); + }); + return Result; } std::vector takeMacroInfos() { @@ -112,15 +134,30 @@ class DeclarationAndMacrosFinder : public index::IndexDataConsumer { SourceLocation Loc, index::IndexDataConsumer::ASTNodeInfo ASTNode) override { if (Loc == SearchedLocation) { + // Check whether the E has an implicit AST node (e.g. ImplicitCastExpr). + auto hasImplicitExpr = [](const Expr *E) { + if (!E || E->child_begin() == E->child_end()) + return false; + // Use the first child is good enough for most cases -- normally the + // expression returned by handleDeclOccurence contains exactly one + // child expression. + const auto *FirstChild = *E->child_begin(); + return llvm::isa(FirstChild) || + llvm::isa(FirstChild) || + llvm::isa(FirstChild) || + llvm::isa(FirstChild); + }; + + bool IsExplicit = !hasImplicitExpr(ASTNode.OrigE); // Find and add definition declarations (for GoToDefinition). // We don't use parameter `D`, as Parameter `D` is the canonical // declaration, which is the first declaration of a redeclarable // declaration, and it could be a forward declaration. if (const auto *Def = getDefinition(D)) { - Decls.push_back(Def); + Decls[Def] |= IsExplicit; } else { // Couldn't find a definition, fall back to use `D`. - Decls.push_back(D); + Decls[D] |= IsExplicit; } } return true; @@ -158,7 +195,7 @@ class DeclarationAndMacrosFinder : public index::IndexDataConsumer { }; struct IdentifiedSymbol { - std::vector Decls; + std::vector Decls; std::vector Macros; }; @@ -172,7 +209,7 @@ IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos) { indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(), DeclMacrosFinder, IndexOpts); - return {DeclMacrosFinder.takeDecls(), DeclMacrosFinder.takeMacroInfos()}; + return {DeclMacrosFinder.getFoundDecls(), DeclMacrosFinder.takeMacroInfos()}; } Range getTokenRange(ParsedAST &AST, SourceLocation TokLoc) { @@ -250,10 +287,13 @@ std::vector findDefinitions(ParsedAST &AST, Position Pos, llvm::Optional Def; llvm::Optional Decl; }; - llvm::DenseMap ResultCandidates; + // We respect the order in Symbols.Decls. + llvm::SmallVector ResultCandidates; + llvm::DenseMap CandidatesIndex; // Emit all symbol locations (declaration or definition) from AST. - for (const auto *D : Symbols.Decls) { + for (const DeclInfo &DI : Symbols.Decls) { + const Decl *D = DI.D; // Fake key for symbols don't have USR (no SymbolID). // Ideally, there should be a USR for each identified symbols. Symbols // without USR are rare and unimportant cases, we use the a fake holder to @@ -262,7 +302,11 @@ std::vector findDefinitions(ParsedAST &AST, Position Pos, if (auto ID = getSymbolID(D)) Key = *ID; - auto &Candidate = ResultCandidates[Key]; + auto R = CandidatesIndex.try_emplace(Key, ResultCandidates.size()); + if (R.second) // new entry + ResultCandidates.emplace_back(); + auto &Candidate = ResultCandidates[R.first->second]; + auto Loc = findNameLoc(D); auto L = makeLocation(AST, Loc); // The declaration in the identified symbols is a definition if possible @@ -278,7 +322,7 @@ std::vector findDefinitions(ParsedAST &AST, Position Pos, if (Index) { LookupRequest QueryRequest; // Build request for index query, using SymbolID. - for (auto It : ResultCandidates) + for (auto It : CandidatesIndex) QueryRequest.IDs.insert(It.first); std::string HintPath; const FileEntry *FE = @@ -286,22 +330,21 @@ std::vector findDefinitions(ParsedAST &AST, Position Pos, if (auto Path = getRealPath(FE, SourceMgr)) HintPath = *Path; // Query the index and populate the empty slot. - Index->lookup( - QueryRequest, [&HintPath, &ResultCandidates](const Symbol &Sym) { - auto It = ResultCandidates.find(Sym.ID); - assert(It != ResultCandidates.end()); - auto &Value = It->second; - - if (!Value.Def) - Value.Def = toLSPLocation(Sym.Definition, HintPath); - if (!Value.Decl) - Value.Decl = toLSPLocation(Sym.CanonicalDeclaration, HintPath); - }); + Index->lookup(QueryRequest, [&HintPath, &ResultCandidates, + &CandidatesIndex](const Symbol &Sym) { + auto It = CandidatesIndex.find(Sym.ID); + assert(It != CandidatesIndex.end()); + auto &Value = ResultCandidates[It->second]; + + if (!Value.Def) + Value.Def = toLSPLocation(Sym.Definition, HintPath); + if (!Value.Decl) + Value.Decl = toLSPLocation(Sym.CanonicalDeclaration, HintPath); + }); } // Populate the results, definition first. - for (auto It : ResultCandidates) { - const auto &Candidate = It.second; + for (const auto &Candidate : ResultCandidates) { if (Candidate.Def) Result.push_back(*Candidate.Def); if (Candidate.Decl && @@ -383,7 +426,11 @@ std::vector findDocumentHighlights(ParsedAST &AST, const SourceManager &SM = AST.getASTContext().getSourceManager(); auto Symbols = getSymbolAtPosition( AST, getBeginningOfIdentifier(AST, Pos, SM.getMainFileID())); - auto References = findRefs(Symbols.Decls, AST); + std::vector TargetDecls; + for (const DeclInfo &DI : Symbols.Decls) { + TargetDecls.push_back(DI.D); + } + auto References = findRefs(TargetDecls, AST); std::vector Result; for (const auto &Ref : References) { @@ -650,7 +697,7 @@ Optional getHover(ParsedAST &AST, Position Pos) { return getHoverContents(Symbols.Macros[0].Name); if (!Symbols.Decls.empty()) - return getHoverContents(Symbols.Decls[0]); + return getHoverContents(Symbols.Decls[0].D); auto DeducedType = getDeducedType(AST, SourceLocationBeg); if (DeducedType && !DeducedType->isNull()) @@ -671,9 +718,15 @@ std::vector findReferences(ParsedAST &AST, Position Pos, auto Loc = getBeginningOfIdentifier(AST, Pos, SM.getMainFileID()); auto Symbols = getSymbolAtPosition(AST, Loc); + std::vector TargetDecls; + for (const DeclInfo &DI : Symbols.Decls) { + if (DI.IsReferencedExplicitly) + TargetDecls.push_back(DI.D); + } + // We traverse the AST to find references in the main file. // TODO: should we handle macros, too? - auto MainFileRefs = findRefs(Symbols.Decls, AST); + auto MainFileRefs = findRefs(TargetDecls, AST); for (const auto &Ref : MainFileRefs) { Location Result; Result.range = getTokenRange(AST, Ref.Loc); @@ -685,7 +738,7 @@ std::vector findReferences(ParsedAST &AST, Position Pos, if (!Index) return Results; RefsRequest Req; - for (const Decl *D : Symbols.Decls) { + for (const Decl *D : TargetDecls) { // Not all symbols can be referenced from outside (e.g. function-locals). // TODO: we could skip TU-scoped symbols here (e.g. static functions) if // we know this file isn't a header. The details might be tricky. diff --git a/unittests/clangd/XRefsTests.cpp b/unittests/clangd/XRefsTests.cpp index 3bf09722f..b24c1fec3 100644 --- a/unittests/clangd/XRefsTests.cpp +++ b/unittests/clangd/XRefsTests.cpp @@ -311,6 +311,47 @@ TEST(GoToDefinition, All) { } } +TEST(GoToDefinition, Rank) { + auto T = Annotations(R"cpp( + struct $foo1[[Foo]] { + $foo2[[Foo]](); + $foo3[[Foo]](Foo&&); + $foo4[[Foo]](const char*); + }; + + Foo $f[[f]](); + + void $g[[g]](Foo foo); + + void call() { + const char* $str[[str]] = "123"; + Foo a = $1^str; + Foo b = Foo($2^str); + Foo c = $3^f(); + $4^g($5^f()); + g($6^str); + } + )cpp"); + auto AST = TestTU::withCode(T.code()).build(); + EXPECT_THAT(findDefinitions(AST, T.point("1")), + ElementsAre(RangeIs(T.range("str")), RangeIs(T.range("foo4")))); + EXPECT_THAT(findDefinitions(AST, T.point("2")), + ElementsAre(RangeIs(T.range("str")))); + EXPECT_THAT(findDefinitions(AST, T.point("3")), + ElementsAre(RangeIs(T.range("f")), RangeIs(T.range("foo3")))); + EXPECT_THAT(findDefinitions(AST, T.point("4")), + ElementsAre(RangeIs(T.range("g")))); + EXPECT_THAT(findDefinitions(AST, T.point("5")), + ElementsAre(RangeIs(T.range("f")), RangeIs(T.range("foo3")))); + + auto DefinitionAtPoint6 = findDefinitions(AST, T.point("6")); + EXPECT_EQ(3ul, DefinitionAtPoint6.size()); + EXPECT_THAT(DefinitionAtPoint6, HasSubsequence(RangeIs(T.range("str")), + RangeIs(T.range("foo4")))); + EXPECT_THAT(DefinitionAtPoint6, HasSubsequence(RangeIs(T.range("str")), + RangeIs(T.range("foo3")))); +} + TEST(GoToDefinition, RelPathsInCompileCommand) { // The source is in "/clangd-test/src". // We build in "/clangd-test/build". From ed8a01ac15a0347a0760f848585060b61a2d7a5e Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Wed, 5 Sep 2018 13:17:47 +0000 Subject: [PATCH 141/686] [clangd] make zlib compression optional for binary format git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341465 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Serialization.cpp | 40 ++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index 5e732e969..2bc8e7be6 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -86,7 +86,7 @@ uint32_t consumeVar(StringRef &Data) { // We store each string once, and refer to them by index. // // The string table's format is: -// - UncompressedSize : uint32 +// - UncompressedSize : uint32 (or 0 for no compression) // - CompressedData : byte[CompressedSize] // // CompressedData is a zlib-compressed byte[UncompressedSize]. @@ -102,6 +102,11 @@ class StringTableOut { DenseMap, unsigned> Index; public: + StringTableOut() { + // Ensure there's at least one string in the table. + // Table size zero is reserved to indicate no compression. + Unique.insert(""); + } // Add a string to the table. Overwrites S if an identical string exists. void intern(StringRef &S) { S = *Unique.insert(S).first; }; // Finalize the table and write it to OS. No more strings may be added. @@ -116,10 +121,15 @@ class StringTableOut { RawTable.append(S); RawTable.push_back(0); } - SmallString<1> Compressed; - cantFail(zlib::compress(RawTable, Compressed)); - write32(RawTable.size(), OS); - OS << Compressed; + if (zlib::isAvailable()) { + SmallString<1> Compressed; + cantFail(zlib::compress(RawTable, Compressed)); + write32(RawTable.size(), OS); + OS << Compressed; + } else { + write32(0, OS); // No compression. + OS << RawTable; + } } // Get the ID of an string, which must be interned. Table must be finalized. unsigned index(StringRef S) const { @@ -138,9 +148,17 @@ Expected readStringTable(StringRef Data) { if (Data.size() < 4) return makeError("Bad string table: not enough metadata"); size_t UncompressedSize = consume32(Data); - SmallString<1> Uncompressed; - if (Error E = llvm::zlib::uncompress(Data, Uncompressed, UncompressedSize)) - return std::move(E); + + StringRef Uncompressed; + SmallString<1> UncompressedStorage; + if (UncompressedSize == 0) // No compression + Uncompressed = Data; + else { + if (Error E = + llvm::zlib::uncompress(Data, UncompressedStorage, UncompressedSize)) + return std::move(E); + Uncompressed = UncompressedStorage; + } StringTableIn Table; StringSaver Saver(Table.Arena); @@ -285,9 +303,9 @@ Expected readSymbol(StringRef &Data, const StringTableIn &Strings) { // - symb: symbols // The current versioning scheme is simple - non-current versions are rejected. -// This allows arbitrary format changes, which invalidate stored data. -// Later we may want to support some backward compatibility. -constexpr static uint32_t Version = 1; +// If you make a breaking change, bump this version number to invalidate stored +// data. Later we may want to support some backward compatibility. +constexpr static uint32_t Version = 2; Expected readIndexFile(StringRef Data) { auto RIFF = riff::readFile(Data); From f4f51d7ad40255137983afa95479a2d276646d03 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Wed, 5 Sep 2018 13:17:51 +0000 Subject: [PATCH 142/686] [clangd] Fix references.test assertions git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341466 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clangd/references.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/clangd/references.test b/test/clangd/references.test index e6294d9b0..964f5e7f9 100644 --- a/test/clangd/references.test +++ b/test/clangd/references.test @@ -18,7 +18,7 @@ # CHECK-NEXT: "line": 0 # CHECK-NEXT: } # CHECK-NEXT: }, -# CHECK-NEXT: "uri": "test:///main.cpp" +# CHECK-NEXT: "uri": "{{.*}}/main.cpp" # CHECK-NEXT: }, # CHECK-NEXT: { # CHECK-NEXT: "range": { @@ -31,8 +31,8 @@ # CHECK-NEXT: "line": 0 # CHECK-NEXT: } # CHECK-NEXT: }, -# CHECK-NEXT: "uri": "test:///main.cpp" -# CHECK-NEXT: }, +# CHECK-NEXT: "uri": "{{.*}}/main.cpp" +# CHECK-NEXT: } # CHECK-NEXT: ] --- {"jsonrpc":"2.0","id":3,"method":"shutdown"} From a54e725e3a630fc3146c75ffa8d3005fb83d50f2 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Wed, 5 Sep 2018 13:22:11 +0000 Subject: [PATCH 143/686] [clangd] Fix type/variable name conflict on some compilers git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341467 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../GlobalSymbolBuilderMain.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp b/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp index eda8c12d4..e0333f589 100644 --- a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp +++ b/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp @@ -60,13 +60,12 @@ static llvm::cl::opt MergeOnTheFly( "MapReduce."), llvm::cl::init(true), llvm::cl::Hidden); -enum Format { YAML, Binary }; -static llvm::cl::opt - Format("format", llvm::cl::desc("Format of the index to be written"), - llvm::cl::values( - clEnumValN(Format::YAML, "yaml", "human-readable YAML format"), - clEnumValN(Format::Binary, "binary", "binary RIFF format")), - llvm::cl::init(Format::YAML)); +enum IndexFormat { YAML, Binary }; +static llvm::cl::opt Format( + "format", llvm::cl::desc("Format of the index to be written"), + llvm::cl::values(clEnumValN(YAML, "yaml", "human-readable YAML format"), + clEnumValN(Binary, "binary", "binary RIFF format")), + llvm::cl::init(YAML)); /// Responsible for aggregating symbols from each processed file and producing /// the final results. All methods in this class must be thread-safe, @@ -273,10 +272,10 @@ int main(int argc, const char **argv) { auto UniqueSymbols = Consumer->mergeResults(); // Output phase: emit result symbols. switch (clang::clangd::Format) { - case clang::clangd::Format::YAML: + case clang::clangd::IndexFormat::YAML: SymbolsToYAML(UniqueSymbols, llvm::outs()); break; - case clang::clangd::Format::Binary: { + case clang::clangd::IndexFormat::Binary: { clang::clangd::IndexFileOut Out; Out.Symbols = &UniqueSymbols; llvm::outs() << Out; From 074e25eeb529d44b172a46c3d9419475e2dd37ed Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Wed, 5 Sep 2018 19:01:34 +0000 Subject: [PATCH 144/686] [clang-tidy] minor bug fix to AbseilMatcher.h This missing directory is not yet released, but is causing some problems internally. It's gonna be released eventually and received permission to include it here. This matcher will also be periodically updated by my team as we have more releases and or problems internally. Patch by Hugo Gonzalez! Differential Revision: https://reviews.llvm.org/D51699 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341488 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/AbseilMatcher.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/clang-tidy/abseil/AbseilMatcher.h b/clang-tidy/abseil/AbseilMatcher.h index 17b3517e1..5759d4707 100644 --- a/clang-tidy/abseil/AbseilMatcher.h +++ b/clang-tidy/abseil/AbseilMatcher.h @@ -48,10 +48,19 @@ AST_POLYMORPHIC_MATCHER( if (PrefixPosition == StringRef::npos) return false; Path = Path.drop_front(PrefixPosition + AbslPrefix.size()); - static const char *AbseilLibraries[] = { - "algorithm", "base", "container", "debugging", - "memory", "meta", "numeric", "strings", - "synchronization", "time", "types", "utility"}; + static const char *AbseilLibraries[] = {"algorithm", + "base", + "container", + "debugging", + "flags" + "memory", + "meta", + "numeric", + "strings", + "synchronization", + "time", + "types", + "utility"}; return std::any_of( std::begin(AbseilLibraries), std::end(AbseilLibraries), [&](const char *Library) { return Path.startswith(Library); }); From 1161cf5448012cbe2671c194b0aee4cc881e202f Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Thu, 6 Sep 2018 09:59:37 +0000 Subject: [PATCH 145/686] [clangd] Set SymbolID for sema macros so that they can be merged with index macros. Reviewers: sammccall Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51688 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341534 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/AST.cpp | 11 +++++++++++ clangd/AST.h | 11 +++++++++++ clangd/CodeComplete.cpp | 8 +++++--- clangd/index/SymbolCollector.cpp | 16 ++++++---------- unittests/clangd/CodeCompleteTests.cpp | 11 +++++++++++ 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/clangd/AST.cpp b/clangd/AST.cpp index 1344931a9..661c6aaf4 100644 --- a/clangd/AST.cpp +++ b/clangd/AST.cpp @@ -61,5 +61,16 @@ llvm::Optional getSymbolID(const Decl *D) { return SymbolID(USR); } +llvm::Optional getSymbolID(const IdentifierInfo &II, + const MacroInfo *MI, + const SourceManager &SM) { + if (MI == nullptr) + return None; + llvm::SmallString<128> USR; + if (index::generateUSRForMacro(II.getName(), MI->getDefinitionLoc(), SM, USR)) + return None; + return SymbolID(USR); +} + } // namespace clangd } // namespace clang diff --git a/clangd/AST.h b/clangd/AST.h index 9f00f829c..88244f3e9 100644 --- a/clangd/AST.h +++ b/clangd/AST.h @@ -37,6 +37,17 @@ std::string printQualifiedName(const NamedDecl &ND); /// Gets the symbol ID for a declaration, if possible. llvm::Optional getSymbolID(const Decl *D); +/// Gets the symbol ID for a macro, if possible. +/// Currently, this is an encoded USR of the macro, which incorporates macro +/// locations (e.g. file name, offset in file). +/// FIXME: the USR semantics might not be stable enough as the ID for index +/// macro (e.g. a change in definition offset can result in a different USR). We +/// could change these semantics in the future by reimplementing this funcure +/// (e.g. avoid USR for macros). +llvm::Optional getSymbolID(const IdentifierInfo &II, + const MacroInfo *MI, + const SourceManager &SM); + } // namespace clangd } // namespace clang diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 73357d8db..19ebd3027 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -505,14 +505,15 @@ struct CodeCompletionBuilder { }; // Determine the symbol ID for a Sema code completion result, if possible. -llvm::Optional getSymbolID(const CodeCompletionResult &R) { +llvm::Optional getSymbolID(const CodeCompletionResult &R, + const SourceManager &SM) { switch (R.Kind) { case CodeCompletionResult::RK_Declaration: case CodeCompletionResult::RK_Pattern: { return clang::clangd::getSymbolID(R.Declaration); } case CodeCompletionResult::RK_Macro: - // FIXME: Macros do have USRs, but the CCR doesn't contain enough info. + return clang::clangd::getSymbolID(*R.Macro, R.MacroDefInfo, SM); case CodeCompletionResult::RK_Keyword: return None; } @@ -1435,7 +1436,8 @@ class CodeCompleteFlow { llvm::DenseSet UsedIndexResults; auto CorrespondingIndexResult = [&](const CodeCompletionResult &SemaResult) -> const Symbol * { - if (auto SymID = getSymbolID(SemaResult)) { + if (auto SymID = + getSymbolID(SemaResult, Recorder->CCSema->getSourceManager())) { auto I = IndexResults.find(*SymID); if (I != IndexResults.end()) { UsedIndexResults.insert(&*I); diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp index 7a83243f8..ee24cf7d5 100644 --- a/clangd/index/SymbolCollector.cpp +++ b/clangd/index/SymbolCollector.cpp @@ -385,18 +385,16 @@ bool SymbolCollector::handleMacroOccurence(const IdentifierInfo *Name, Roles & static_cast(index::SymbolRole::Definition))) return true; - llvm::SmallString<128> USR; - if (index::generateUSRForMacro(Name->getName(), MI->getDefinitionLoc(), SM, - USR)) + auto ID = getSymbolID(*Name, MI, SM); + if (!ID) return true; - SymbolID ID(USR); // Only collect one instance in case there are multiple. - if (Symbols.find(ID) != nullptr) + if (Symbols.find(*ID) != nullptr) return true; Symbol S; - S.ID = std::move(ID); + S.ID = std::move(*ID); S.Name = Name->getName(); S.IsIndexedForCodeCompletion = true; S.SymInfo = index::getSymbolInfoForMacro(*MI); @@ -445,11 +443,9 @@ void SymbolCollector::finish() { if (Opts.CollectMacro) { assert(PP); for (const IdentifierInfo *II : ReferencedMacros) { - llvm::SmallString<128> USR; if (const auto *MI = PP->getMacroDefinition(II).getMacroInfo()) - if (!index::generateUSRForMacro(II->getName(), MI->getDefinitionLoc(), - PP->getSourceManager(), USR)) - IncRef(SymbolID(USR)); + if (auto ID = getSymbolID(*II, MI, PP->getSourceManager())) + IncRef(*ID); } } diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 3a6fe9ba3..d2067219c 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1882,6 +1882,17 @@ TEST(CompletionTest, NoInsertIncludeIfOnePresent) { AllOf(Named("Func"), HasInclude("\"foo.h\""), Not(InsertInclude())))); } +TEST(CompletionTest, MergeMacrosFromIndexAndSema) { + Symbol Sym; + Sym.Name = "Clangd_Macro_Test"; + Sym.ID = SymbolID("c:foo.cpp@8@macro@Clangd_Macro_Test"); + Sym.SymInfo.Kind = index::SymbolKind::Macro; + Sym.IsIndexedForCodeCompletion = true; + EXPECT_THAT(completions("#define Clangd_Macro_Test\nClangd_Macro_T^", {Sym}) + .Completions, + UnorderedElementsAre(Named("Clangd_Macro_Test"))); +} + } // namespace } // namespace clangd } // namespace clang From 68a11ec086ac28d0d60e67fa2e24919bcf0676cb Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Thu, 6 Sep 2018 11:04:56 +0000 Subject: [PATCH 146/686] [clangd] Fix data race in async fuzzyFind tests. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341538 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/CodeCompleteTests.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index d2067219c..315050f79 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -966,6 +966,7 @@ class IndexRequestCollector : public SymbolIndex { bool fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref Callback) const override { + std::lock_guard Lock(Mut); Requests.push_back(Req); return true; } @@ -981,12 +982,15 @@ class IndexRequestCollector : public SymbolIndex { size_t estimateMemoryUsage() const override { return 0; } const std::vector consumeRequests() const { + std::lock_guard Lock(Mut); auto Reqs = std::move(Requests); Requests = {}; return Reqs; } private: + // We need a mutex to handle async fuzzy find requests. + mutable std::mutex Mut; mutable std::vector Requests; }; From 8ee3adc81282d75b7ef7168e85e077e51e9ffb7c Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 6 Sep 2018 12:54:43 +0000 Subject: [PATCH 147/686] [clangd] Implement proximity path boosting for Dex This patch introduces `PathURI` Search Token kind and utilizes it to uprank symbols which are defined in files with small distance to the directory where the fuzzy find request is coming from (e.g. files user is editing). Reviewed By: ioeric Reviewers: ioeric, sammccall Differential Revision: https://reviews.llvm.org/D51481 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341542 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/FileDistance.h | 5 + clangd/URI.cpp | 20 ++++ clangd/URI.h | 5 + clangd/index/SymbolYAML.cpp | 3 +- clangd/index/SymbolYAML.h | 1 + clangd/index/dex/DexIndex.cpp | 154 ++++++++++++++++++++++++----- clangd/index/dex/DexIndex.h | 40 +++++--- clangd/index/dex/Token.h | 15 ++- clangd/tool/ClangdMain.cpp | 4 +- unittests/clangd/DexIndexTests.cpp | 96 +++++++++++++++--- 10 files changed, 283 insertions(+), 60 deletions(-) diff --git a/clangd/FileDistance.h b/clangd/FileDistance.h index 631fddf5e..f85e8ed76 100644 --- a/clangd/FileDistance.h +++ b/clangd/FileDistance.h @@ -37,6 +37,9 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FILEDISTANCE_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FILEDISTANCE_H + #include "URI.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" @@ -107,3 +110,5 @@ class URIDistance { } // namespace clangd } // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_FILEDISTANCE_H diff --git a/clangd/URI.cpp b/clangd/URI.cpp index a72264089..d5fd481dd 100644 --- a/clangd/URI.cpp +++ b/clangd/URI.cpp @@ -181,6 +181,26 @@ llvm::Expected URI::create(llvm::StringRef AbsolutePath, return S->get()->uriFromAbsolutePath(AbsolutePath); } +llvm::Expected URI::create(llvm::StringRef AbsolutePath, + const std::vector &Schemes) { + if (!llvm::sys::path::is_absolute(AbsolutePath)) + return make_string_error("Not a valid absolute path: " + AbsolutePath); + for (const auto &Scheme : Schemes) { + auto URI = URI::create(AbsolutePath, Scheme); + // For some paths, conversion to different URI schemes is impossible. These + // should be just skipped. + if (!URI) { + // Ignore the error. + llvm::consumeError(URI.takeError()); + continue; + } + return URI; + } + return make_string_error( + "Couldn't convert " + AbsolutePath + + " to any given scheme: " + llvm::join(Schemes, ", ")); +} + URI URI::createFile(llvm::StringRef AbsolutePath) { auto U = create(AbsolutePath, "file"); if (!U) diff --git a/clangd/URI.h b/clangd/URI.h index 112264f71..aa9dd2427 100644 --- a/clangd/URI.h +++ b/clangd/URI.h @@ -45,6 +45,11 @@ class URI { static llvm::Expected create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme); + // Similar to above except this uses the first scheme in \p Schemes that + // works. + static llvm::Expected create(llvm::StringRef AbsolutePath, + const std::vector &Schemes); + /// This creates a file:// URI for \p AbsolutePath. The path must be absolute. static URI createFile(llvm::StringRef AbsolutePath); diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/SymbolYAML.cpp index 78e13aefb..292ea5a7d 100644 --- a/clangd/index/SymbolYAML.cpp +++ b/clangd/index/SymbolYAML.cpp @@ -185,6 +185,7 @@ std::string SymbolToYAML(Symbol Sym) { } std::unique_ptr loadIndex(llvm::StringRef SymbolFilename, + llvm::ArrayRef URISchemes, bool UseDex) { trace::Span OverallTracer("LoadIndex"); auto Buffer = llvm::MemoryBuffer::getFile(SymbolFilename); @@ -209,7 +210,7 @@ std::unique_ptr loadIndex(llvm::StringRef SymbolFilename, if (!Slab) return nullptr; trace::Span Tracer("BuildIndex"); - return UseDex ? dex::DexIndex::build(std::move(*Slab)) + return UseDex ? dex::DexIndex::build(std::move(*Slab), URISchemes) : MemIndex::build(std::move(*Slab), RefSlab()); } diff --git a/clangd/index/SymbolYAML.h b/clangd/index/SymbolYAML.h index 3f75492f2..7da16f128 100644 --- a/clangd/index/SymbolYAML.h +++ b/clangd/index/SymbolYAML.h @@ -45,6 +45,7 @@ void SymbolsToYAML(const SymbolSlab &Symbols, llvm::raw_ostream &OS); // The size of global symbols should be relatively small, so that all symbols // can be managed in memory. std::unique_ptr loadIndex(llvm::StringRef SymbolFilename, + llvm::ArrayRef URISchemes, bool UseDex = true); } // namespace clangd diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/DexIndex.cpp index ed1f60b9b..f1acf546b 100644 --- a/clangd/index/dex/DexIndex.cpp +++ b/clangd/index/dex/DexIndex.cpp @@ -8,8 +8,11 @@ //===----------------------------------------------------------------------===// #include "DexIndex.h" +#include "../../FileDistance.h" #include "../../FuzzyMatch.h" #include "../../Logger.h" +#include "../../Quality.h" +#include "llvm/ADT/StringSet.h" #include #include @@ -26,28 +29,87 @@ namespace { // Returns the tokens which are given symbols's characteristics. For example, // trigrams and scopes. // FIXME(kbobyrev): Support more token types: -// * Path proximity // * Types +// * Namespace proximity std::vector generateSearchTokens(const Symbol &Sym) { std::vector Result = generateIdentifierTrigrams(Sym.Name); Result.emplace_back(Token::Kind::Scope, Sym.Scope); + // Skip token generation for symbols with unknown declaration location. + if (!Sym.CanonicalDeclaration.FileURI.empty()) + for (const auto &ProximityURI : + generateProximityURIs(Sym.CanonicalDeclaration.FileURI)) + Result.emplace_back(Token::Kind::ProximityURI, ProximityURI); return Result; } +// Constructs BOOST iterators for Path Proximities. +std::vector> createFileProximityIterators( + llvm::ArrayRef ProximityPaths, + llvm::ArrayRef URISchemes, + const llvm::DenseMap &InvertedIndex) { + std::vector> BoostingIterators; + // Deduplicate parent URIs extracted from the ProximityPaths. + llvm::StringSet<> ParentURIs; + llvm::StringMap Sources; + for (const auto &Path : ProximityPaths) { + Sources[Path] = SourceParams(); + auto PathURI = URI::create(Path, URISchemes); + if (!PathURI) { + elog("Given ProximityPath {0} is can not be converted to any known URI " + "scheme. fuzzyFind request will ignore it.", + Path); + llvm::consumeError(PathURI.takeError()); + } + const auto PathProximityURIs = generateProximityURIs(PathURI->toString()); + for (const auto &ProximityURI : PathProximityURIs) + ParentURIs.insert(ProximityURI); + } + // Use SymbolRelevanceSignals for symbol relevance evaluation: use defaults + // for all parameters except for Proximity Path distance signal. + SymbolRelevanceSignals PathProximitySignals; + // DistanceCalculator will find the shortest distance from ProximityPaths to + // any URI extracted from the ProximityPaths. + URIDistance DistanceCalculator(Sources); + PathProximitySignals.FileProximityMatch = &DistanceCalculator; + // Try to build BOOST iterator for each Proximity Path provided by + // ProximityPaths. Boosting factor should depend on the distance to the + // Proximity Path: the closer processed path is, the higher boosting factor. + for (const auto &ParentURI : ParentURIs.keys()) { + const auto It = + InvertedIndex.find(Token(Token::Kind::ProximityURI, ParentURI)); + if (It != InvertedIndex.end()) { + // FIXME(kbobyrev): Append LIMIT on top of every BOOST iterator. + PathProximitySignals.SymbolURI = ParentURI; + BoostingIterators.push_back( + createBoost(create(It->second), PathProximitySignals.evaluate())); + } + } + return BoostingIterators; +} + } // namespace void DexIndex::buildIndex() { - for (const Symbol *Sym : Symbols) { + std::vector> ScoredSymbols(Symbols.size()); + + for (size_t I = 0; I < Symbols.size(); ++I) { + const Symbol *Sym = Symbols[I]; LookupTable[Sym->ID] = Sym; - SymbolQuality[Sym] = quality(*Sym); + ScoredSymbols[I] = {quality(*Sym), Sym}; } // Symbols are sorted by symbol qualities so that items in the posting lists // are stored in the descending order of symbol quality. - std::sort(begin(Symbols), end(Symbols), - [&](const Symbol *LHS, const Symbol *RHS) { - return SymbolQuality[LHS] > SymbolQuality[RHS]; - }); + std::sort(begin(ScoredSymbols), end(ScoredSymbols), + std::greater>()); + + // SymbolQuality was empty up until now. + SymbolQuality.resize(Symbols.size()); + // Populate internal storage using Symbol + Score pairs. + for (size_t I = 0; I < ScoredSymbols.size(); ++I) { + SymbolQuality[I] = ScoredSymbols[I].first; + Symbols[I] = ScoredSymbols[I].second; + } // Populate TempInvertedIndex with posting lists for index symbols. for (DocID SymbolRank = 0; SymbolRank < Symbols.size(); ++SymbolRank) { @@ -96,6 +158,17 @@ bool DexIndex::fuzzyFind( if (!ScopeIterators.empty()) TopLevelChildren.push_back(createOr(move(ScopeIterators))); + // Add proximity paths boosting. + auto BoostingIterators = createFileProximityIterators( + Req.ProximityPaths, URISchemes, InvertedIndex); + // Boosting iterators do not actually filter symbols. In order to preserve + // the validity of resulting query, TRUE iterator should be added along + // BOOSTs. + if (!BoostingIterators.empty()) { + BoostingIterators.push_back(createTrue(Symbols.size())); + TopLevelChildren.push_back(createOr(move(BoostingIterators))); + } + // Use TRUE iterator if both trigrams and scopes from the query are not // present in the symbol index. auto QueryIterator = TopLevelChildren.empty() @@ -106,32 +179,36 @@ bool DexIndex::fuzzyFind( // FIXME(kbobyrev): Pre-scoring retrieval threshold should be adjusted as // using 100x of the requested number might not be good in practice, e.g. // when the requested number of items is small. - const unsigned ItemsToRetrieve = 100 * Req.MaxCandidateCount; + const size_t ItemsToRetrieve = 100 * Req.MaxCandidateCount; auto Root = createLimit(move(QueryIterator), ItemsToRetrieve); - // FIXME(kbobyrev): Add boosting to the query and utilize retrieved - // boosting scores. - std::vector> SymbolDocIDs = consume(*Root); - - // Retrieve top Req.MaxCandidateCount items. - std::priority_queue> Top; - for (const auto &P : SymbolDocIDs) { - const DocID SymbolDocID = P.first; + + using IDAndScore = std::pair; + std::vector IDAndScores = consume(*Root); + + auto Compare = [](const IDAndScore &LHS, const IDAndScore &RHS) { + return LHS.second > RHS.second; + }; + TopN Top(Req.MaxCandidateCount, Compare); + for (const auto &IDAndScore : IDAndScores) { + const DocID SymbolDocID = IDAndScore.first; const auto *Sym = Symbols[SymbolDocID]; const llvm::Optional Score = Filter.match(Sym->Name); if (!Score) continue; - // Multiply score by a negative factor so that Top stores items with the - // highest actual score. - Top.emplace(-(*Score) * SymbolQuality.find(Sym)->second, Sym); - if (Top.size() > Req.MaxCandidateCount) { + // Combine Fuzzy Matching score, precomputed symbol quality and boosting + // score for a cumulative final symbol score. + const float FinalScore = + (*Score) * SymbolQuality[SymbolDocID] * IDAndScore.second; + // If Top.push(...) returns true, it means that it had to pop an item. In + // this case, it is possible to retrieve more symbols. + if (Top.push({SymbolDocID, FinalScore})) More = true; - Top.pop(); - } } - // Apply callback to the top Req.MaxCandidateCount items. - for (; !Top.empty(); Top.pop()) - Callback(*Top.top().second); + // Apply callback to the top Req.MaxCandidateCount items in the descending + // order of cumulative score. + for (const auto &Item : std::move(Top).items()) + Callback(*Symbols[Item.first]); return More; } @@ -161,6 +238,33 @@ size_t DexIndex::estimateMemoryUsage() const { return Bytes; } +std::vector generateProximityURIs(llvm::StringRef URIPath) { + std::vector Result; + auto ParsedURI = URI::parse(URIPath); + assert(ParsedURI && + "Non-empty argument of generateProximityURIs() should be a valid " + "URI."); + StringRef Body = ParsedURI->body(); + // FIXME(kbobyrev): Currently, this is a heuristic which defines the maximum + // size of resulting vector. Some projects might want to have higher limit if + // the file hierarchy is deeper. For the generic case, it would be useful to + // calculate Limit in the index build stage by calculating the maximum depth + // of the project source tree at runtime. + size_t Limit = 5; + // Insert original URI before the loop: this would save a redundant iteration + // with a URI parse. + Result.emplace_back(ParsedURI->toString()); + while (!Body.empty() && --Limit > 0) { + // FIXME(kbobyrev): Parsing and encoding path to URIs is not necessary and + // could be optimized. + Body = llvm::sys::path::parent_path(Body, llvm::sys::path::Style::posix); + URI TokenURI(ParsedURI->scheme(), ParsedURI->authority(), Body); + if (!Body.empty()) + Result.emplace_back(TokenURI.toString()); + } + return Result; +} + } // namespace dex } // namespace clangd } // namespace clang diff --git a/clangd/index/dex/DexIndex.h b/clangd/index/dex/DexIndex.h index 3244e62ec..78d9d4a0a 100644 --- a/clangd/index/dex/DexIndex.h +++ b/clangd/index/dex/DexIndex.h @@ -39,22 +39,26 @@ namespace dex { class DexIndex : public SymbolIndex { public: // All symbols must outlive this index. - template DexIndex(Range &&Symbols) { + template + DexIndex(Range &&Symbols, llvm::ArrayRef URISchemes) + : URISchemes(URISchemes) { for (auto &&Sym : Symbols) this->Symbols.push_back(&Sym); buildIndex(); } // Symbols are owned by BackingData, Index takes ownership. template - DexIndex(Range &&Symbols, Payload &&BackingData) - : DexIndex(std::forward(Symbols)) { + DexIndex(Range &&Symbols, Payload &&BackingData, + llvm::ArrayRef URISchemes) + : DexIndex(std::forward(Symbols), URISchemes) { KeepAlive = std::shared_ptr( std::make_shared(std::move(BackingData)), nullptr); } /// Builds an index from a slab. The index takes ownership of the slab. - static std::unique_ptr build(SymbolSlab Slab) { - return llvm::make_unique(Slab, std::move(Slab)); + static std::unique_ptr + build(SymbolSlab Slab, llvm::ArrayRef URISchemes) { + return llvm::make_unique(Slab, std::move(Slab), URISchemes); } bool @@ -72,21 +76,31 @@ class DexIndex : public SymbolIndex { private: void buildIndex(); + /// Stores symbols sorted in the descending order of symbol quality.. std::vector Symbols; + /// SymbolQuality[I] is the quality of Symbols[I]. + std::vector SymbolQuality; llvm::DenseMap LookupTable; - llvm::DenseMap SymbolQuality; - // Inverted index is a mapping from the search token to the posting list, - // which contains all items which can be characterized by such search token. - // For example, if the search token is scope "std::", the corresponding - // posting list would contain all indices of symbols defined in namespace std. - // Inverted index is used to retrieve posting lists which are processed during - // the fuzzyFind process. + /// Inverted index is a mapping from the search token to the posting list, + /// which contains all items which can be characterized by such search token. + /// For example, if the search token is scope "std::", the corresponding + /// posting list would contain all indices of symbols defined in namespace + /// std. Inverted index is used to retrieve posting lists which are processed + /// during the fuzzyFind process. llvm::DenseMap InvertedIndex; std::shared_ptr KeepAlive; // poor man's move-only std::any + + const std::vector URISchemes; }; +/// Returns Search Token for a number of parent directories of given Path. +/// Should be used within the index build process. +/// +/// This function is exposed for testing only. +std::vector generateProximityURIs(llvm::StringRef URIPath); + } // namespace dex } // namespace clangd } // namespace clang -#endif +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEXINDEX_H diff --git a/clangd/index/dex/Token.h b/clangd/index/dex/Token.h index af12f670d..f8b7447dc 100644 --- a/clangd/index/dex/Token.h +++ b/clangd/index/dex/Token.h @@ -41,6 +41,10 @@ struct Token { /// Kind specifies Token type which defines semantics for the internal /// representation. Each Kind has different representation stored in Data /// field. + // FIXME(kbobyrev): Storing Data hash would be more efficient than storing raw + // strings. For example, PathURI store URIs of each directory and its parents, + // which induces a lot of overhead because these paths tend to be long and + // each parent directory is a prefix. enum class Kind { /// Represents trigram used for fuzzy search of unqualified symbol names. /// @@ -48,15 +52,20 @@ struct Token { Trigram, /// Scope primitives, e.g. "symbol belongs to namespace foo::bar". /// - /// Data stroes full scope name , e.g. "foo::bar::baz::" or "" (for global + /// Data stroes full scope name, e.g. "foo::bar::baz::" or "" (for global /// scope). Scope, + /// Path Proximity URI to symbol declaration. + /// + /// Data stores path URI of symbol declaration file or its parent. + /// + /// Example: "file:///path/to/clang-tools-extra/clangd/index/SymbolIndex.h" + /// and some amount of its parents. + ProximityURI, /// Internal Token type for invalid/special tokens, e.g. empty tokens for /// llvm::DenseMap. Sentinel, /// FIXME(kbobyrev): Add other Token Kinds - /// * Path with full or relative path to the directory in which symbol is - /// defined /// * Type with qualified type name or its USR }; diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 39dd0833f..0155f1703 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -275,8 +275,8 @@ int main(int argc, char *argv[]) { // Load the index asynchronously. Meanwhile SwapIndex returns no results. SwapIndex *Placeholder; StaticIdx.reset(Placeholder = new SwapIndex(llvm::make_unique())); - runAsync([Placeholder] { - if (auto Idx = loadIndex(YamlSymbolFile)) + runAsync([Placeholder, &Opts] { + if (auto Idx = loadIndex(YamlSymbolFile, Opts.URISchemes, UseDex)) Placeholder->reset(std::move(Idx)); }); } diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexIndexTests.cpp index da09596ab..fe7f10137 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexIndexTests.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +#include "FuzzyMatch.h" +#include "TestFS.h" #include "TestIndex.h" #include "index/Index.h" #include "index/Merge.h" @@ -30,6 +32,12 @@ namespace clangd { namespace dex { namespace { +std::vector URISchemes = {"unittest"}; + +//===----------------------------------------------------------------------===// +// Query iterator tests. +//===----------------------------------------------------------------------===// + std::vector consumeIDs(Iterator &It) { auto IDAndScore = consume(It); std::vector IDs(IDAndScore.size()); @@ -325,15 +333,24 @@ TEST(DexIndexIterators, Boost) { EXPECT_THAT(ElementBoost, 3); } +//===----------------------------------------------------------------------===// +// Search token tests. +//===----------------------------------------------------------------------===// + testing::Matcher> -trigramsAre(std::initializer_list Trigrams) { +tokensAre(std::initializer_list Strings, Token::Kind Kind) { std::vector Tokens; - for (const auto &Symbols : Trigrams) { - Tokens.push_back(Token(Token::Kind::Trigram, Symbols)); + for (const auto &TokenData : Strings) { + Tokens.push_back(Token(Kind, TokenData)); } return testing::UnorderedElementsAreArray(Tokens); } +testing::Matcher> +trigramsAre(std::initializer_list Trigrams) { + return tokensAre(Trigrams, Token::Kind::Trigram); +} + TEST(DexIndexTrigrams, IdentifierTrigrams) { EXPECT_THAT(generateIdentifierTrigrams("X86"), trigramsAre({"x86", "x$$", "x8$"})); @@ -408,8 +425,25 @@ TEST(DexIndexTrigrams, QueryTrigrams) { "hij", "ijk", "jkl", "klm"})); } +TEST(DexSearchTokens, SymbolPath) { + EXPECT_THAT(generateProximityURIs( + "unittest:///clang-tools-extra/clangd/index/Token.h"), + ElementsAre("unittest:///clang-tools-extra/clangd/index/Token.h", + "unittest:///clang-tools-extra/clangd/index", + "unittest:///clang-tools-extra/clangd", + "unittest:///clang-tools-extra", "unittest:///")); + + EXPECT_THAT(generateProximityURIs("unittest:///a/b/c.h"), + ElementsAre("unittest:///a/b/c.h", "unittest:///a/b", + "unittest:///a", "unittest:///")); +} + +//===----------------------------------------------------------------------===// +// Index tests. +//===----------------------------------------------------------------------===// + TEST(DexIndex, Lookup) { - auto I = DexIndex::build(generateSymbols({"ns::abc", "ns::xyz"})); + auto I = DexIndex::build(generateSymbols({"ns::abc", "ns::xyz"}), URISchemes); EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::abc", "ns::xyz")); @@ -421,7 +455,8 @@ TEST(DexIndex, Lookup) { TEST(DexIndex, FuzzyFind) { auto Index = DexIndex::build( generateSymbols({"ns::ABC", "ns::BCD", "::ABC", "ns::nested::ABC", - "other::ABC", "other::A"})); + "other::ABC", "other::A"}), + URISchemes); FuzzyFindRequest Req; Req.Query = "ABC"; Req.Scopes = {"ns::"}; @@ -443,7 +478,8 @@ TEST(DexIndex, FuzzyFind) { TEST(DexIndexTest, FuzzyMatchQ) { auto I = DexIndex::build( - generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"})); + generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}), + URISchemes); FuzzyFindRequest Req; Req.Query = "lol"; Req.MaxCandidateCount = 2; @@ -461,12 +497,12 @@ TEST(DexIndexTest, DexIndexDeduplicate) { symbol("2") /* duplicate */}; FuzzyFindRequest Req; Req.Query = "2"; - DexIndex I(Symbols); + DexIndex I(Symbols, URISchemes); EXPECT_THAT(match(I, Req), ElementsAre("2", "2")); } TEST(DexIndexTest, DexIndexLimitedNumMatches) { - auto I = DexIndex::build(generateNumSymbols(0, 100)); + auto I = DexIndex::build(generateNumSymbols(0, 100), URISchemes); FuzzyFindRequest Req; Req.Query = "5"; Req.MaxCandidateCount = 3; @@ -478,7 +514,8 @@ TEST(DexIndexTest, DexIndexLimitedNumMatches) { TEST(DexIndexTest, FuzzyMatch) { auto I = DexIndex::build( - generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"})); + generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}), + URISchemes); FuzzyFindRequest Req; Req.Query = "lol"; Req.MaxCandidateCount = 2; @@ -487,14 +524,16 @@ TEST(DexIndexTest, FuzzyMatch) { } TEST(DexIndexTest, MatchQualifiedNamesWithoutSpecificScope) { - auto I = DexIndex::build(generateSymbols({"a::y1", "b::y2", "y3"})); + auto I = + DexIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3")); } TEST(DexIndexTest, MatchQualifiedNamesWithGlobalScope) { - auto I = DexIndex::build(generateSymbols({"a::y1", "b::y2", "y3"})); + auto I = + DexIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {""}; @@ -503,7 +542,7 @@ TEST(DexIndexTest, MatchQualifiedNamesWithGlobalScope) { TEST(DexIndexTest, MatchQualifiedNamesWithOneScope) { auto I = DexIndex::build( - generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"})); + generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; @@ -512,7 +551,7 @@ TEST(DexIndexTest, MatchQualifiedNamesWithOneScope) { TEST(DexIndexTest, MatchQualifiedNamesWithMultipleScopes) { auto I = DexIndex::build( - generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"})); + generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::", "b::"}; @@ -520,7 +559,7 @@ TEST(DexIndexTest, MatchQualifiedNamesWithMultipleScopes) { } TEST(DexIndexTest, NoMatchNestedScopes) { - auto I = DexIndex::build(generateSymbols({"a::y1", "a::b::y2"})); + auto I = DexIndex::build(generateSymbols({"a::y1", "a::b::y2"}), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; @@ -528,7 +567,7 @@ TEST(DexIndexTest, NoMatchNestedScopes) { } TEST(DexIndexTest, IgnoreCases) { - auto I = DexIndex::build(generateSymbols({"ns::ABC", "ns::abc"})); + auto I = DexIndex::build(generateSymbols({"ns::ABC", "ns::abc"}), URISchemes); FuzzyFindRequest Req; Req.Query = "AB"; Req.Scopes = {"ns::"}; @@ -536,7 +575,7 @@ TEST(DexIndexTest, IgnoreCases) { } TEST(DexIndexTest, Lookup) { - auto I = DexIndex::build(generateSymbols({"ns::abc", "ns::xyz"})); + auto I = DexIndex::build(generateSymbols({"ns::abc", "ns::xyz"}), URISchemes); EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::abc", "ns::xyz")); @@ -545,6 +584,31 @@ TEST(DexIndexTest, Lookup) { EXPECT_THAT(lookup(*I, SymbolID("ns::nonono")), UnorderedElementsAre()); } +TEST(DexIndexTest, ProximityPathsBoosting) { + auto RootSymbol = symbol("root::abc"); + RootSymbol.CanonicalDeclaration.FileURI = "unittest:///file.h"; + auto CloseSymbol = symbol("close::abc"); + CloseSymbol.CanonicalDeclaration.FileURI = "unittest:///a/b/c/d/e/f/file.h"; + + std::vector Symbols{CloseSymbol, RootSymbol}; + DexIndex I(Symbols, URISchemes); + + FuzzyFindRequest Req; + Req.Query = "abc"; + // The best candidate can change depending on the proximity paths. + Req.MaxCandidateCount = 1; + + // FuzzyFind request comes from the file which is far from the root: expect + // CloseSymbol to come out. + Req.ProximityPaths = {testPath("a/b/c/d/e/f/file.h")}; + EXPECT_THAT(match(I, Req), ElementsAre("close::abc")); + + // FuzzyFind request comes from the file which is close to the root: expect + // RootSymbol to come out. + Req.ProximityPaths = {testPath("file.h")}; + EXPECT_THAT(match(I, Req), ElementsAre("root::abc")); +} + } // namespace } // namespace dex } // namespace clangd From 2310232cf661fa5f5052b78a966360486c06f19a Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 6 Sep 2018 13:06:04 +0000 Subject: [PATCH 148/686] [clangd] NFC: mark single-parameter constructors explicit Code health: prevent implicit conversions to user-defined types. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D51690 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341543 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index 9e4606a44..3204980b3 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -23,7 +23,7 @@ namespace { /// tree) and is simply a wrapper around PostingList::const_iterator. class DocumentIterator : public Iterator { public: - DocumentIterator(PostingListRef Documents) + explicit DocumentIterator(PostingListRef Documents) : Documents(Documents), Index(std::begin(Documents)) {} bool reachedEnd() const override { return Index == std::end(Documents); } @@ -85,7 +85,7 @@ class DocumentIterator : public Iterator { /// iterator restores the invariant: all children must point to the same item. class AndIterator : public Iterator { public: - AndIterator(std::vector> AllChildren) + explicit AndIterator(std::vector> AllChildren) : Children(std::move(AllChildren)) { assert(!Children.empty() && "AND iterator should have at least one child."); // Establish invariants. @@ -193,7 +193,7 @@ class AndIterator : public Iterator { /// soon as all of its children are exhausted. class OrIterator : public Iterator { public: - OrIterator(std::vector> AllChildren) + explicit OrIterator(std::vector> AllChildren) : Children(std::move(AllChildren)) { assert(Children.size() > 0 && "OR iterator must have at least one child."); } @@ -279,7 +279,7 @@ class OrIterator : public Iterator { /// in O(1). class TrueIterator : public Iterator { public: - TrueIterator(DocID Size) : Size(Size) {} + explicit TrueIterator(DocID Size) : Size(Size) {} bool reachedEnd() const override { return Index >= Size; } From 8c10841e96458a2769021c4ebb2cfc8e4ab2ca95 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 6 Sep 2018 13:15:03 +0000 Subject: [PATCH 149/686] [clangd] NFC: Use TopN instead of std::priority_queue Quality.cpp defines a structure for convenient storage of Top N items, it should be used instead of the `std::priority_queue` with slightly obscure semantics. This patch does not affect functionality. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D51676 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341544 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/MemIndex.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index 2a7cd601b..97ceaf7f1 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -10,7 +10,7 @@ #include "MemIndex.h" #include "../FuzzyMatch.h" #include "../Logger.h" -#include +#include "../Quality.h" namespace clang { namespace clangd { @@ -26,7 +26,7 @@ bool MemIndex::fuzzyFind( assert(!StringRef(Req.Query).contains("::") && "There must be no :: in query."); - std::priority_queue> Top; + TopN> Top(Req.MaxCandidateCount); FuzzyMatcher Filter(Req.Query); bool More = false; for (const auto Pair : Index) { @@ -38,16 +38,12 @@ bool MemIndex::fuzzyFind( if (Req.RestrictForCodeCompletion && !Sym->IsIndexedForCodeCompletion) continue; - if (auto Score = Filter.match(Sym->Name)) { - Top.emplace(-*Score * quality(*Sym), Sym); - if (Top.size() > Req.MaxCandidateCount) { - More = true; - Top.pop(); - } - } + if (auto Score = Filter.match(Sym->Name)) + if (Top.push({*Score * quality(*Sym), Sym})) + More = true; // An element with smallest score was discarded. } - for (; !Top.empty(); Top.pop()) - Callback(*Top.top().second); + for (const auto &Item : std::move(Top).items()) + Callback(*Item.second); return More; } From 4ac7fda537fc7c4ae060d3feb864c042e30b812b Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 6 Sep 2018 15:10:10 +0000 Subject: [PATCH 150/686] [clangd] Fix Dex initialization This patch sets URI schemes of Dex to SymbolCollector's default schemes in case callers tried to pass empty list of schemes. This was the case for initialization in Clangd main and was a reason of incorrect behavior. Also, it fixes a bug with missed `continue;` after spotting invalid URI scheme conversion. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341552 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/DexIndex.cpp | 1 + clangd/index/dex/DexIndex.h | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/DexIndex.cpp index f1acf546b..a2da841ed 100644 --- a/clangd/index/dex/DexIndex.cpp +++ b/clangd/index/dex/DexIndex.cpp @@ -59,6 +59,7 @@ std::vector> createFileProximityIterators( "scheme. fuzzyFind request will ignore it.", Path); llvm::consumeError(PathURI.takeError()); + continue; } const auto PathProximityURIs = generateProximityURIs(PathURI->toString()); for (const auto &ProximityURI : PathProximityURIs) diff --git a/clangd/index/dex/DexIndex.h b/clangd/index/dex/DexIndex.h index 78d9d4a0a..f3c3a048b 100644 --- a/clangd/index/dex/DexIndex.h +++ b/clangd/index/dex/DexIndex.h @@ -22,6 +22,7 @@ #include "../Index.h" #include "../MemIndex.h" +#include "../SymbolCollector.h" #include "Iterator.h" #include "Token.h" #include "Trigram.h" @@ -40,8 +41,14 @@ class DexIndex : public SymbolIndex { public: // All symbols must outlive this index. template - DexIndex(Range &&Symbols, llvm::ArrayRef URISchemes) - : URISchemes(URISchemes) { + DexIndex(Range &&Symbols, llvm::ArrayRef Schemes) + : URISchemes(Schemes) { + // If Schemes don't contain any items, fall back to SymbolCollector's + // default URI schemes. + if (URISchemes.empty()) { + SymbolCollector::Options Opts; + URISchemes = Opts.URISchemes; + } for (auto &&Sym : Symbols) this->Symbols.push_back(&Sym); buildIndex(); @@ -90,7 +97,7 @@ class DexIndex : public SymbolIndex { llvm::DenseMap InvertedIndex; std::shared_ptr KeepAlive; // poor man's move-only std::any - const std::vector URISchemes; + std::vector URISchemes; }; /// Returns Search Token for a number of parent directories of given Path. From 1d52c0348cd65087938ef214af55d9d4a1e5f6a0 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Thu, 6 Sep 2018 18:52:26 +0000 Subject: [PATCH 151/686] [clangd] Add "Deprecated" field to Symbol and CodeCompletion. Summary: Also set "deprecated" field in LSP CompletionItem. Reviewers: sammccall, kadircet Reviewed By: sammccall Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D51724 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341576 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 5 ++++ clangd/CodeComplete.h | 3 +++ clangd/Protocol.cpp | 2 ++ clangd/Protocol.h | 3 +++ clangd/Quality.cpp | 5 ++-- clangd/index/Index.cpp | 11 +++++++++ clangd/index/Index.h | 23 +++++++++++++++---- clangd/index/MemIndex.cpp | 3 ++- clangd/index/Merge.cpp | 1 + clangd/index/Serialization.cpp | 7 +++--- clangd/index/SymbolCollector.cpp | 7 ++++-- clangd/index/SymbolYAML.cpp | 19 +++++++++++++-- unittests/clangd/CodeCompleteTests.cpp | 28 +++++++++++++++++++---- unittests/clangd/QualityTests.cpp | 2 +- unittests/clangd/SerializationTests.cpp | 10 ++++---- unittests/clangd/SymbolCollectorTests.cpp | 15 +++++++++++- 16 files changed, 119 insertions(+), 25 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 19ebd3027..354a2770e 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -353,6 +353,8 @@ struct CodeCompletionBuilder { return std::tie(X.range.start.line, X.range.start.character) < std::tie(Y.range.start.line, Y.range.start.character); }); + Completion.Deprecated |= + (C.SemaResult->Availability == CXAvailability_Deprecated); } if (C.IndexResult) { Completion.Origin |= C.IndexResult->Origin; @@ -362,6 +364,7 @@ struct CodeCompletionBuilder { Completion.Kind = toCompletionItemKind(C.IndexResult->SymInfo.Kind); if (Completion.Name.empty()) Completion.Name = C.IndexResult->Name; + Completion.Deprecated |= (C.IndexResult->Flags & Symbol::Deprecated); } // Turn absolute path into a literal string that can be #included. @@ -1625,6 +1628,7 @@ CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const { LSP.kind = Kind; LSP.detail = BundleSize > 1 ? llvm::formatv("[{0} overloads]", BundleSize) : ReturnType; + LSP.deprecated = Deprecated; if (InsertInclude) LSP.detail += "\n" + InsertInclude->Header; LSP.documentation = Documentation; @@ -1656,6 +1660,7 @@ CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const { : InsertTextFormat::PlainText; if (InsertInclude && InsertInclude->Insertion) LSP.additionalTextEdits.push_back(*InsertInclude->Insertion); + return LSP; } diff --git a/clangd/CodeComplete.h b/clangd/CodeComplete.h index 3197e172b..6467ad354 100644 --- a/clangd/CodeComplete.h +++ b/clangd/CodeComplete.h @@ -177,6 +177,9 @@ struct CodeCompletion { }; Scores Score; + /// Indicates if this item is deprecated. + bool Deprecated = false; + // Serialize this to an LSP completion item. This is a lossy operation. CompletionItem render(const CodeCompleteOptions &) const; }; diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp index 42093797b..9b232af17 100644 --- a/clangd/Protocol.cpp +++ b/clangd/Protocol.cpp @@ -517,6 +517,8 @@ json::Value toJSON(const CompletionItem &CI) { Result["textEdit"] = *CI.textEdit; if (!CI.additionalTextEdits.empty()) Result["additionalTextEdits"] = json::Array(CI.additionalTextEdits); + if (CI.deprecated) + Result["deprecated"] = CI.deprecated; return std::move(Result); } diff --git a/clangd/Protocol.h b/clangd/Protocol.h index d97713bea..9eec9c0f5 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -768,6 +768,9 @@ struct CompletionItem { /// themselves. std::vector additionalTextEdits; + /// Indicates if this item is deprecated. + bool deprecated = false; + // TODO(krasimir): The following optional fields defined by the language // server protocol are unsupported: // diff --git a/clangd/Quality.cpp b/clangd/Quality.cpp index 7e0e8372b..5060a14d2 100644 --- a/clangd/Quality.cpp +++ b/clangd/Quality.cpp @@ -167,9 +167,7 @@ static bool isInstanceMember(const index::SymbolInfo &D) { } void SymbolQualitySignals::merge(const CodeCompletionResult &SemaCCResult) { - if (SemaCCResult.Availability == CXAvailability_Deprecated) - Deprecated = true; - + Deprecated |= (SemaCCResult.Availability == CXAvailability_Deprecated); Category = categorize(SemaCCResult); if (SemaCCResult.Declaration) { @@ -180,6 +178,7 @@ void SymbolQualitySignals::merge(const CodeCompletionResult &SemaCCResult) { } void SymbolQualitySignals::merge(const Symbol &IndexResult) { + Deprecated |= (IndexResult.Flags & Symbol::Deprecated); References = std::max(IndexResult.References, References); Category = categorize(IndexResult.SymInfo); ReservedName = ReservedName || isReserved(IndexResult.Name); diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index 48a7c688d..a01c49f65 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -55,6 +55,17 @@ raw_ostream &operator<<(raw_ostream &OS, SymbolOrigin O) { return OS; } +raw_ostream &operator<<(raw_ostream &OS, Symbol::SymbolFlag F) { + if (F == Symbol::None) + return OS << "None"; + std::string s; + if (F & Symbol::Deprecated) + s += "deprecated|"; + if (F & Symbol::IndexedForCodeCompletion) + s += "completion|"; + return OS << StringRef(s).rtrim('|'); +} + raw_ostream &operator<<(raw_ostream &OS, const Symbol &S) { return OS << S.Scope << S.Name; } diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 249237052..12727efaa 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -201,9 +201,6 @@ struct Symbol { // The number of translation units that reference this symbol from their main // file. This number is only meaningful if aggregated in an index. unsigned References = 0; - /// Whether or not this symbol is meant to be used for the code completion. - /// See also isIndexedForCodeCompletion(). - bool IsIndexedForCodeCompletion = false; /// Where this symbol came from. Usually an index provides a constant value. SymbolOrigin Origin = SymbolOrigin::Unknown; /// A brief description of the symbol that can be appended in the completion @@ -244,9 +241,27 @@ struct Symbol { /// any definition. llvm::SmallVector IncludeHeaders; - // FIXME: add extra fields for index scoring signals. + enum SymbolFlag : uint8_t { + None = 0, + /// Whether or not this symbol is meant to be used for the code completion. + /// See also isIndexedForCodeCompletion(). + IndexedForCodeCompletion = 1 << 0, + /// Indicates if the symbol is deprecated. + Deprecated = 1 << 1, + }; + + SymbolFlag Flags = SymbolFlag::None; + /// FIXME: also add deprecation message and fixit? }; +inline Symbol::SymbolFlag operator|(Symbol::SymbolFlag A, Symbol::SymbolFlag B) { + return static_cast(static_cast(A) | + static_cast(B)); +} +inline Symbol::SymbolFlag &operator|=(Symbol::SymbolFlag &A, Symbol::SymbolFlag B) { + return A = A | B; +} llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Symbol &S); +raw_ostream &operator<<(raw_ostream &, Symbol::SymbolFlag); // Invokes Callback with each StringRef& contained in the Symbol. // Useful for deduplicating backing strings. diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index 97ceaf7f1..9ef6f089f 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -35,7 +35,8 @@ bool MemIndex::fuzzyFind( // Exact match against all possible scopes. if (!Req.Scopes.empty() && !llvm::is_contained(Req.Scopes, Sym->Scope)) continue; - if (Req.RestrictForCodeCompletion && !Sym->IsIndexedForCodeCompletion) + if (Req.RestrictForCodeCompletion && + !(Sym->Flags & Symbol::IndexedForCodeCompletion)) continue; if (auto Score = Filter.match(Sym->Name)) diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index 5f79cffa5..48087fde1 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -150,6 +150,7 @@ Symbol mergeSymbol(const Symbol &L, const Symbol &R) { } S.Origin |= O.Origin | SymbolOrigin::Merge; + S.Flags |= O.Flags; return S; } diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index 2bc8e7be6..bf4a131cc 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "Serialization.h" #include "../RIFF.h" +#include "Index.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" @@ -200,7 +201,7 @@ void writeSymbol(const Symbol &Sym, const StringTableOut &Strings, } } writeVar(Sym.References, OS); - OS.write(Sym.IsIndexedForCodeCompletion); + OS.write(static_cast(Sym.Flags)); OS.write(static_cast(Sym.Origin)); writeVar(Strings.index(Sym.Signature), OS); writeVar(Strings.index(Sym.CompletionSnippetSuffix), OS); @@ -274,7 +275,7 @@ Expected readSymbol(StringRef &Data, const StringTableIn &Strings) { } } Sym.References = consumeVar(Data); - Sym.IsIndexedForCodeCompletion = consume8(Data); + Sym.Flags = static_cast(consume8(Data)); Sym.Origin = static_cast(consume8(Data)); READ_STRING(Sym.Signature); READ_STRING(Sym.CompletionSnippetSuffix); @@ -305,7 +306,7 @@ Expected readSymbol(StringRef &Data, const StringTableIn &Strings) { // The current versioning scheme is simple - non-current versions are rejected. // If you make a breaking change, bump this version number to invalidate stored // data. Later we may want to support some backward compatibility. -constexpr static uint32_t Version = 2; +constexpr static uint32_t Version = 3; Expected readIndexFile(StringRef Data) { auto RIFF = riff::readFile(Data); diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp index ee24cf7d5..f63046331 100644 --- a/clangd/index/SymbolCollector.cpp +++ b/clangd/index/SymbolCollector.cpp @@ -396,7 +396,7 @@ bool SymbolCollector::handleMacroOccurence(const IdentifierInfo *Name, Symbol S; S.ID = std::move(*ID); S.Name = Name->getName(); - S.IsIndexedForCodeCompletion = true; + S.Flags |= Symbol::IndexedForCodeCompletion; S.SymInfo = index::getSymbolInfoForMacro(*MI); std::string FileURI; if (auto DeclLoc = getTokenLocation(MI->getDefinitionLoc(), SM, Opts, @@ -491,7 +491,8 @@ const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, // FIXME: this returns foo:bar: for objective-C methods, we prefer only foo: // for consistency with CodeCompletionString and a clean name/signature split. - S.IsIndexedForCodeCompletion = isIndexedForCodeCompletion(ND, Ctx); + if (isIndexedForCodeCompletion(ND, Ctx)) + S.Flags |= Symbol::IndexedForCodeCompletion; S.SymInfo = index::getSymbolInfo(&ND); std::string FileURI; if (auto DeclLoc = getTokenLocation(findNameLoc(&ND), SM, Opts, @@ -531,6 +532,8 @@ const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, S.IncludeHeaders.emplace_back(Include, 1); S.Origin = Opts.Origin; + if (ND.getAvailability() == AR_Deprecated) + S.Flags |= Symbol::Deprecated; Symbols.insert(S); return Symbols.find(S.ID); } diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/SymbolYAML.cpp index 292ea5a7d..c1bdb363d 100644 --- a/clangd/index/SymbolYAML.cpp +++ b/clangd/index/SymbolYAML.cpp @@ -17,6 +17,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" +#include LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(clang::clangd::Symbol) LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Symbol::IncludeHeaderWithReferences) @@ -48,6 +49,19 @@ struct NormalizedSymbolID { std::string HexString; }; +struct NormalizedSymbolFlag { + NormalizedSymbolFlag(IO &) {} + NormalizedSymbolFlag(IO &, Symbol::SymbolFlag F) { + Flag = static_cast(F); + } + + Symbol::SymbolFlag denormalize(IO &) { + return static_cast(Flag); + } + + uint8_t Flag = 0; +}; + template <> struct MappingTraits { static void mapping(IO &IO, SymbolLocation::Position &Value) { IO.mapRequired("Line", Value.Line); @@ -83,6 +97,8 @@ struct MappingTraits { template <> struct MappingTraits { static void mapping(IO &IO, Symbol &Sym) { MappingNormalization NSymbolID(IO, Sym.ID); + MappingNormalization NSymbolFlag( + IO, Sym.Flags); IO.mapRequired("ID", NSymbolID->HexString); IO.mapRequired("Name", Sym.Name); IO.mapRequired("Scope", Sym.Scope); @@ -91,8 +107,7 @@ template <> struct MappingTraits { SymbolLocation()); IO.mapOptional("Definition", Sym.Definition, SymbolLocation()); IO.mapOptional("References", Sym.References, 0u); - IO.mapOptional("IsIndexedForCodeCompletion", Sym.IsIndexedForCodeCompletion, - false); + IO.mapOptional("Flags", NSymbolFlag->Flag); IO.mapOptional("Signature", Sym.Signature); IO.mapOptional("CompletionSnippetSuffix", Sym.CompletionSnippetSuffix); IO.mapOptional("Documentation", Sym.Documentation); diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 315050f79..7e7fde7a9 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -77,6 +77,7 @@ Matcher &> Has(std::string Name, return Contains(AllOf(Named(std::move(Name)), Kind(K))); } MATCHER(IsDocumented, "") { return !arg.Documentation.empty(); } +MATCHER(Deprecated, "") { return arg.Deprecated; } std::unique_ptr memIndex(std::vector Symbols) { SymbolSlab::Builder Slab; @@ -161,7 +162,7 @@ Symbol sym(StringRef QName, index::SymbolKind Kind, StringRef USRFormat) { USR += Regex("^.*$").sub(USRFormat, Sym.Name); // e.g. func -> @F@func# Sym.ID = SymbolID(USR); Sym.SymInfo.Kind = Kind; - Sym.IsIndexedForCodeCompletion = true; + Sym.Flags |= Symbol::IndexedForCodeCompletion; Sym.Origin = SymbolOrigin::Static; return Sym; } @@ -720,9 +721,11 @@ TEST(CompletionTest, Documentation) { TEST(CompletionTest, GlobalCompletionFiltering) { Symbol Class = cls("XYZ"); - Class.IsIndexedForCodeCompletion = false; + Class.Flags = static_cast( + Class.Flags & ~(Symbol::IndexedForCodeCompletion)); Symbol Func = func("XYZ::foooo"); - Func.IsIndexedForCodeCompletion = false; + Func.Flags = static_cast( + Func.Flags & ~(Symbol::IndexedForCodeCompletion)); auto Results = completions(R"(// void f() { XYZ::foooo^ @@ -1374,6 +1377,7 @@ TEST(CompletionTest, Render) { EXPECT_EQ(R.documentation, "This is x()."); EXPECT_THAT(R.additionalTextEdits, IsEmpty()); EXPECT_EQ(R.sortText, sortText(1.0, "x")); + EXPECT_FALSE(R.deprecated); Opts.EnableSnippets = true; R = C.render(Opts); @@ -1392,6 +1396,10 @@ TEST(CompletionTest, Render) { C.BundleSize = 2; R = C.render(Opts); EXPECT_EQ(R.detail, "[2 overloads]\n\"foo.h\""); + + C.Deprecated = true; + R = C.render(Opts); + EXPECT_TRUE(R.deprecated); } TEST(CompletionTest, IgnoreRecoveryResults) { @@ -1891,12 +1899,24 @@ TEST(CompletionTest, MergeMacrosFromIndexAndSema) { Sym.Name = "Clangd_Macro_Test"; Sym.ID = SymbolID("c:foo.cpp@8@macro@Clangd_Macro_Test"); Sym.SymInfo.Kind = index::SymbolKind::Macro; - Sym.IsIndexedForCodeCompletion = true; + Sym.Flags |= Symbol::IndexedForCodeCompletion; EXPECT_THAT(completions("#define Clangd_Macro_Test\nClangd_Macro_T^", {Sym}) .Completions, UnorderedElementsAre(Named("Clangd_Macro_Test"))); } +TEST(CompletionTest, DeprecatedResults) { + std::string Body = R"cpp( + void TestClangd(); + void TestClangc() __attribute__((deprecated("", ""))); + )cpp"; + + EXPECT_THAT( + completions(Body + "int main() { TestClang^ }").Completions, + UnorderedElementsAre(AllOf(Named("TestClangd"), Not(Deprecated())), + AllOf(Named("TestClangc"), Deprecated()))); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/unittests/clangd/QualityTests.cpp b/unittests/clangd/QualityTests.cpp index 1dcb39117..ca13d4b79 100644 --- a/unittests/clangd/QualityTests.cpp +++ b/unittests/clangd/QualityTests.cpp @@ -59,7 +59,7 @@ TEST(QualityTests, SymbolQualitySignalExtraction) { F.References = 24; // TestTU doesn't count references, so fake it. Quality = {}; Quality.merge(F); - EXPECT_FALSE(Quality.Deprecated); // FIXME: Include deprecated bit in index. + EXPECT_TRUE(Quality.Deprecated); EXPECT_FALSE(Quality.ReservedName); EXPECT_EQ(Quality.References, 24u); EXPECT_EQ(Quality.Category, SymbolQualitySignals::Function); diff --git a/unittests/clangd/SerializationTests.cpp b/unittests/clangd/SerializationTests.cpp index cc430963c..4a50addec 100644 --- a/unittests/clangd/SerializationTests.cpp +++ b/unittests/clangd/SerializationTests.cpp @@ -35,7 +35,7 @@ Scope: 'clang::' End: Line: 1 Column: 1 -IsIndexedForCodeCompletion: true +Flags: 1 Documentation: 'Foo doc' ReturnType: 'int' IncludeHeaders: @@ -62,7 +62,7 @@ Scope: 'clang::' End: Line: 1 Column: 1 -IsIndexedForCodeCompletion: false +Flags: 2 Signature: '-sig' CompletionSnippetSuffix: '-snippet' ... @@ -82,7 +82,8 @@ TEST(SerializationTest, YAMLConversions) { EXPECT_EQ(Sym1.Documentation, "Foo doc"); EXPECT_EQ(Sym1.ReturnType, "int"); EXPECT_EQ(Sym1.CanonicalDeclaration.FileURI, "file:///path/foo.h"); - EXPECT_TRUE(Sym1.IsIndexedForCodeCompletion); + EXPECT_TRUE(Sym1.Flags & Symbol::IndexedForCodeCompletion); + EXPECT_FALSE(Sym1.Flags & Symbol::Deprecated); EXPECT_THAT(Sym1.IncludeHeaders, UnorderedElementsAre(IncludeHeaderWithRef("include1", 7u), IncludeHeaderWithRef("include2", 3u))); @@ -94,7 +95,8 @@ TEST(SerializationTest, YAMLConversions) { EXPECT_EQ(Sym2.Signature, "-sig"); EXPECT_EQ(Sym2.ReturnType, ""); EXPECT_EQ(Sym2.CanonicalDeclaration.FileURI, "file:///path/bar.h"); - EXPECT_FALSE(Sym2.IsIndexedForCodeCompletion); + EXPECT_FALSE(Sym2.Flags & Symbol::IndexedForCodeCompletion); + EXPECT_TRUE(Sym2.Flags & Symbol::Deprecated); std::string ConcatenatedYAML; { diff --git a/unittests/clangd/SymbolCollectorTests.cpp b/unittests/clangd/SymbolCollectorTests.cpp index 6688daee1..4a9eed77c 100644 --- a/unittests/clangd/SymbolCollectorTests.cpp +++ b/unittests/clangd/SymbolCollectorTests.cpp @@ -80,8 +80,10 @@ MATCHER_P(DefRange, Pos, "") { } MATCHER_P(RefCount, R, "") { return int(arg.References) == R; } MATCHER_P(ForCodeCompletion, IsIndexedForCodeCompletion, "") { - return arg.IsIndexedForCodeCompletion == IsIndexedForCodeCompletion; + return static_cast(arg.Flags & Symbol::IndexedForCodeCompletion) == + IsIndexedForCodeCompletion; } +MATCHER(Deprecated, "") { return arg.Flags & Symbol::Deprecated; } MATCHER(RefRange, "") { const Ref &Pos = testing::get<0>(arg); const Range &Range = testing::get<1>(arg); @@ -1014,6 +1016,17 @@ TEST_F(SymbolCollectorTest, CollectMacros) { DeclRange(Header.range("used"))))); } +TEST_F(SymbolCollectorTest, DeprecatedSymbols) { + const std::string Header = R"( + void TestClangc() __attribute__((deprecated("", ""))); + void TestClangd(); + )"; + runSymbolCollector(Header, /**/ ""); + EXPECT_THAT(Symbols, UnorderedElementsAre( + AllOf(QName("TestClangc"), Deprecated()), + AllOf(QName("TestClangd"), Not(Deprecated())))); +} + } // namespace } // namespace clangd } // namespace clang From fd2d6d99444e790680fbfec823f16e7a45a6d1b3 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Thu, 6 Sep 2018 20:16:34 +0000 Subject: [PATCH 152/686] Fix reported range of partial token replacement Summary: Fixes bug: 38678 Reviewers: klimek, rsmith Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D51192 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341583 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/ClangTidy.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clang-tidy/ClangTidy.cpp b/clang-tidy/ClangTidy.cpp index f497fd749..d9eb1a511 100644 --- a/clang-tidy/ClangTidy.cpp +++ b/clang-tidy/ClangTidy.cpp @@ -122,10 +122,6 @@ class ErrorReporter { << Message.Message << Name; for (const auto &FileAndReplacements : Error.Fix) { for (const auto &Repl : FileAndReplacements.second) { - // Retrieve the source range for applicable fixes. Macro definitions - // on the command line have locations in a virtual buffer and don't - // have valid file paths and are therefore not applicable. - SourceRange Range; SourceLocation FixLoc; ++TotalFixes; bool CanBeApplied = false; @@ -166,7 +162,11 @@ class ErrorReporter { FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset()); SourceLocation FixEndLoc = FixLoc.getLocWithOffset(Repl.getLength()); - Range = SourceRange(FixLoc, FixEndLoc); + // Retrieve the source range for applicable fixes. Macro definitions + // on the command line have locations in a virtual buffer and don't + // have valid file paths and are therefore not applicable. + CharSourceRange Range = + CharSourceRange::getCharRange(SourceRange(FixLoc, FixEndLoc)); Diag << FixItHint::CreateReplacement(Range, Repl.getReplacementText()); } From 66df463ea51a720fcd902e2531dbc32b1af155fc Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Fri, 7 Sep 2018 09:18:58 +0000 Subject: [PATCH 153/686] [clangd] NFC: Document URIDistance `URIDistance` constructor should mention that `Sources` must contain *absolute paths*, not URIs. This is not very clear when looking at the interface, especially given that `distance(...)` accepts `URI`, not an absolute path which can give the wrong impression. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D51691 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341639 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/FileDistance.h | 1 + 1 file changed, 1 insertion(+) diff --git a/clangd/FileDistance.h b/clangd/FileDistance.h index f85e8ed76..f30f1aae8 100644 --- a/clangd/FileDistance.h +++ b/clangd/FileDistance.h @@ -89,6 +89,7 @@ class FileDistance { // comparison on the bodies. class URIDistance { public: + // \p Sources must contain absolute paths, not URIs. URIDistance(llvm::StringMap Sources, const FileDistanceOptions &Opts = {}) : Sources(Sources), Opts(Opts) {} From 407578ab21333f0e7bcf525089dd48c9902d5df7 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Fri, 7 Sep 2018 09:25:23 +0000 Subject: [PATCH 154/686] [clang-tidy] Abseil: Allow macros inside of absl to use internal absl things git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341643 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/AbseilMatcher.h | 2 +- test/clang-tidy/Inputs/absl/strings/internal-file.h | 2 ++ test/clang-tidy/abseil-no-internal-dependencies.cpp | 8 ++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/clang-tidy/abseil/AbseilMatcher.h b/clang-tidy/abseil/AbseilMatcher.h index 5759d4707..0426790a2 100644 --- a/clang-tidy/abseil/AbseilMatcher.h +++ b/clang-tidy/abseil/AbseilMatcher.h @@ -33,7 +33,7 @@ AST_POLYMORPHIC_MATCHER( isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc)) { auto &SourceManager = Finder->getASTContext().getSourceManager(); - SourceLocation Loc = Node.getBeginLoc(); + SourceLocation Loc = SourceManager.getSpellingLoc(Node.getBeginLoc()); if (Loc.isInvalid()) return false; const FileEntry *FileEntry = diff --git a/test/clang-tidy/Inputs/absl/strings/internal-file.h b/test/clang-tidy/Inputs/absl/strings/internal-file.h index 6014278e2..ac52109c8 100644 --- a/test/clang-tidy/Inputs/absl/strings/internal-file.h +++ b/test/clang-tidy/Inputs/absl/strings/internal-file.h @@ -31,3 +31,5 @@ class FriendUsageInternal { namespace absl { void OpeningNamespaceInternally() { strings_internal::InternalFunction(); } } // namespace absl + +#define USE_INTERNAL(x) absl::strings_internal::Internal##x() diff --git a/test/clang-tidy/abseil-no-internal-dependencies.cpp b/test/clang-tidy/abseil-no-internal-dependencies.cpp index bd56cb04d..d8aea7ee3 100644 --- a/test/clang-tidy/abseil-no-internal-dependencies.cpp +++ b/test/clang-tidy/abseil-no-internal-dependencies.cpp @@ -37,3 +37,11 @@ namespace absl { SomeContainer b; std::string Str = absl::StringsFunction("a"); } // namespace absl + +#define USE_EXTERNAL(x) absl::strings_internal::Internal##x() + +void MacroUse() { + USE_INTERNAL(Function); // no-warning + USE_EXTERNAL(Function); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not reference any 'internal' namespaces; those implementation details are reserved to Abseil +} From d2bb2e63d750ccf3bb36eb437e8d4f0df952ef67 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Fri, 7 Sep 2018 09:40:36 +0000 Subject: [PATCH 155/686] [clangd] Canonicalize include paths in clangd. Get rid of "../" and "../../". git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341645 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/CanonicalIncludes.cpp | 2 +- clangd/index/FileIndex.cpp | 2 +- clangd/index/FileIndex.h | 2 +- clangd/index/MemIndex.cpp | 6 +++--- clangd/index/Merge.cpp | 2 +- clangd/index/Serialization.cpp | 2 +- clangd/index/SymbolCollector.cpp | 12 ++++++------ clangd/index/SymbolYAML.cpp | 2 +- clangd/index/dex/DexIndex.cpp | 8 ++++---- clangd/index/dex/DexIndex.h | 6 +++--- clangd/index/dex/Token.h | 2 +- clangd/index/dex/Trigram.cpp | 2 +- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/clangd/index/CanonicalIncludes.cpp b/clangd/index/CanonicalIncludes.cpp index 4172b94f8..4bb7fa6c9 100644 --- a/clangd/index/CanonicalIncludes.cpp +++ b/clangd/index/CanonicalIncludes.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// #include "CanonicalIncludes.h" -#include "../Headers.h" +#include "Headers.h" #include "clang/Driver/Types.h" #include "llvm/Support/Path.h" #include diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 1b430e66a..0c5fe4bbd 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// #include "FileIndex.h" -#include "../Logger.h" +#include "Logger.h" #include "SymbolCollector.h" #include "clang/Index/IndexingAction.h" #include "clang/Lex/Preprocessor.h" diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index 9c616231e..3b0b6fa8d 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -16,7 +16,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_FILEINDEX_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_FILEINDEX_H -#include "../ClangdUnit.h" +#include "ClangdUnit.h" #include "Index.h" #include "MemIndex.h" #include "clang/Lex/Preprocessor.h" diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index 9ef6f089f..2ef0eaa42 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -8,9 +8,9 @@ //===-------------------------------------------------------------------===// #include "MemIndex.h" -#include "../FuzzyMatch.h" -#include "../Logger.h" -#include "../Quality.h" +#include "FuzzyMatch.h" +#include "Logger.h" +#include "Quality.h" namespace clang { namespace clangd { diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index 48087fde1..89323c14b 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// #include "Merge.h" -#include "../Logger.h" +#include "Logger.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/raw_ostream.h" diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index bf4a131cc..e1bf3226a 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// #include "Serialization.h" -#include "../RIFF.h" #include "Index.h" +#include "RIFF.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp index f63046331..2bf16f59c 100644 --- a/clangd/index/SymbolCollector.cpp +++ b/clangd/index/SymbolCollector.cpp @@ -8,13 +8,13 @@ //===----------------------------------------------------------------------===// #include "SymbolCollector.h" -#include "../AST.h" -#include "../CodeComplete.h" -#include "../CodeCompletionStrings.h" -#include "../Logger.h" -#include "../SourceCode.h" -#include "../URI.h" +#include "AST.h" #include "CanonicalIncludes.h" +#include "CodeComplete.h" +#include "CodeCompletionStrings.h" +#include "Logger.h" +#include "SourceCode.h" +#include "URI.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/ASTMatchers/ASTMatchFinder.h" diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/SymbolYAML.cpp index c1bdb363d..756d208e1 100644 --- a/clangd/index/SymbolYAML.cpp +++ b/clangd/index/SymbolYAML.cpp @@ -8,9 +8,9 @@ //===----------------------------------------------------------------------===// #include "SymbolYAML.h" -#include "../Trace.h" #include "Index.h" #include "Serialization.h" +#include "Trace.h" #include "dex/DexIndex.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/DexIndex.cpp index a2da841ed..b8282e7d2 100644 --- a/clangd/index/dex/DexIndex.cpp +++ b/clangd/index/dex/DexIndex.cpp @@ -8,10 +8,10 @@ //===----------------------------------------------------------------------===// #include "DexIndex.h" -#include "../../FileDistance.h" -#include "../../FuzzyMatch.h" -#include "../../Logger.h" -#include "../../Quality.h" +#include "FileDistance.h" +#include "FuzzyMatch.h" +#include "Logger.h" +#include "Quality.h" #include "llvm/ADT/StringSet.h" #include #include diff --git a/clangd/index/dex/DexIndex.h b/clangd/index/dex/DexIndex.h index f3c3a048b..d4f385769 100644 --- a/clangd/index/dex/DexIndex.h +++ b/clangd/index/dex/DexIndex.h @@ -20,12 +20,12 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEXINDEX_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEXINDEX_H -#include "../Index.h" -#include "../MemIndex.h" -#include "../SymbolCollector.h" #include "Iterator.h" #include "Token.h" #include "Trigram.h" +#include "index/Index.h" +#include "index/MemIndex.h" +#include "index/SymbolCollector.h" namespace clang { namespace clangd { diff --git a/clangd/index/dex/Token.h b/clangd/index/dex/Token.h index f8b7447dc..8cafcccdd 100644 --- a/clangd/index/dex/Token.h +++ b/clangd/index/dex/Token.h @@ -22,7 +22,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_DEX_TOKEN_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_DEX_TOKEN_H -#include "../Index.h" +#include "index/Index.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/raw_ostream.h" #include diff --git a/clangd/index/dex/Trigram.cpp b/clangd/index/dex/Trigram.cpp index 91044fefb..a646a38de 100644 --- a/clangd/index/dex/Trigram.cpp +++ b/clangd/index/dex/Trigram.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// #include "Trigram.h" -#include "../../FuzzyMatch.h" +#include "FuzzyMatch.h" #include "Token.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" From 8332cc4caa0b6b07f7e7c3332ec31c4a12014163 Mon Sep 17 00:00:00 2001 From: Ben Hamilton Date: Fri, 7 Sep 2018 22:02:38 +0000 Subject: [PATCH 156/686] [clang-tidy/ObjC] Update list of acronyms in PropertyDeclarationCheck Summary: This adds a few common acronyms we found were missing from PropertyDeclarationCheck. Reviewers: Wizard, hokein Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D51819 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341720 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/objc/PropertyDeclarationCheck.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clang-tidy/objc/PropertyDeclarationCheck.cpp b/clang-tidy/objc/PropertyDeclarationCheck.cpp index d7715fa2c..14e3ce1b1 100644 --- a/clang-tidy/objc/PropertyDeclarationCheck.cpp +++ b/clang-tidy/objc/PropertyDeclarationCheck.cpp @@ -42,12 +42,15 @@ constexpr llvm::StringLiteral DefaultSpecialAcronyms[] = { "[2-9]G", "ACL", "API", + "APN", + "APNS", "AR", "ARGB", "ASCII", "AV", "BGRA", "CA", + "CDN", "CF", "CG", "CI", @@ -71,14 +74,17 @@ constexpr llvm::StringLiteral DefaultSpecialAcronyms[] = { "ID", "JPG", "JS", + "JSON", "LAN", "LZW", + "LTR", "MAC", "MD", "MDNS", "MIDI", "NS", "OS", + "P2P", "PDF", "PIN", "PNG", @@ -102,12 +108,14 @@ constexpr llvm::StringLiteral DefaultSpecialAcronyms[] = { "SSO", "TCP", "TIFF", + "TOS", "TTS", "UI", "URI", "URL", "UUID", "VC", + "VO", "VOIP", "VPN", "VR", From 8765cc21f2052ba871f00fc322f5bc7f360f2de9 Mon Sep 17 00:00:00 2001 From: Ben Hamilton Date: Fri, 7 Sep 2018 22:03:48 +0000 Subject: [PATCH 157/686] [clang-tidy/ObjC] Update list of acronyms in PropertyDeclarationCheck Summary: This adds a few common acronyms we found were missing from PropertyDeclarationCheck. Reviewers: Wizard, hokein Reviewed By: hokein Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D51819 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341721 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/clang-tidy/checks/objc-property-declaration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/clang-tidy/checks/objc-property-declaration.rst b/docs/clang-tidy/checks/objc-property-declaration.rst index f851056b4..b372ccd1d 100644 --- a/docs/clang-tidy/checks/objc-property-declaration.rst +++ b/docs/clang-tidy/checks/objc-property-declaration.rst @@ -63,7 +63,7 @@ Options If set to ``1``, the value in ``Acronyms`` is appended to the default list of acronyms: - ``ACL;API;ARGB;ASCII;BGRA;CMYK;DNS;FPS;FTP;GIF;GPS;HD;HDR;HTML;HTTP;HTTPS;HUD;ID;JPG;JS;LAN;LZW;MDNS;MIDI;OS;PDF;PIN;PNG;POI;PSTN;PTR;QA;QOS;RGB;RGBA;RGBX;ROM;RPC;RTF;RTL;SDK;SSO;TCP;TIFF;TTS;UI;URI;URL;VC;VOIP;VPN;VR;WAN;XML``. + ``[2-9]G;ACL;API;APN;APNS;AR;ARGB;ASCII;AV;BGRA;CA;CDN;CF;CG;CI;CRC;CV;CMYK;DNS;FPS;FTP;GIF;GL;GPS;GUID;HD;HDR;HMAC;HTML;HTTP;HTTPS;HUD;ID;JPG;JS;JSON;LAN;LZW;LTR;MAC;MD;MDNS;MIDI;NS;OS;P2P;PDF;PIN;PNG;POI;PSTN;PTR;QA;QOS;RGB;RGBA;RGBX;RIPEMD;ROM;RPC;RTF;RTL;SC;SDK;SHA;SQL;SSO;TCP;TIFF;TOS;TTS;UI;URI;URL;UUID;VC;VO;VOIP;VPN;VR;W;WAN;X;XML;Y;Z``. If set to ``0``, the value in ``Acronyms`` replaces the default list of acronyms. From c8e115a6ad7271fe6d846754ca6fab0a735d4a7f Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 10 Sep 2018 07:57:28 +0000 Subject: [PATCH 158/686] [clangd] Make advanceTo() faster on Posting Lists If the current element is already beyond advanceTo()'s DocID, just return instead of doing binary search. This simple optimization saves up to 6-7% performance, Reviewed By: ilya-biryukov Differential Revision: https://reviews.llvm.org/D51802 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341781 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index 3204980b3..5cbd0f490 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -38,7 +38,10 @@ class DocumentIterator : public Iterator { /// or higher than the given one. void advanceTo(DocID ID) override { assert(!reachedEnd() && "DOCUMENT iterator can't advance() at the end."); - Index = std::lower_bound(Index, std::end(Documents), ID); + // If current ID is beyond requested one, iterator is already in the right + // state. + if (peek() < ID) + Index = std::lower_bound(Index, std::end(Documents), ID); } DocID peek() const override { From f5a3de086b257dacab7b5c0e94a928466e6c3802 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 10 Sep 2018 08:23:53 +0000 Subject: [PATCH 159/686] [clangd] NFC: Rename DexIndex to Dex Also, cleanup some redundant includes. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D51774 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341784 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 2 +- clangd/index/SymbolYAML.cpp | 4 +- clangd/index/dex/{DexIndex.cpp => Dex.cpp} | 23 +++-- clangd/index/dex/{DexIndex.h => Dex.h} | 20 ++-- clangd/tool/ClangdMain.cpp | 1 - unittests/clangd/CMakeLists.txt | 2 +- .../{DexIndexTests.cpp => DexTests.cpp} | 94 +++++++++---------- unittests/clangd/TestIndex.h | 4 - 8 files changed, 72 insertions(+), 78 deletions(-) rename clangd/index/dex/{DexIndex.cpp => Dex.cpp} (94%) rename clangd/index/dex/{DexIndex.h => Dex.h} (86%) rename unittests/clangd/{DexIndexTests.cpp => DexTests.cpp} (89%) diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index df859b45b..eb6ed7223 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -46,7 +46,7 @@ add_clang_library(clangDaemon index/SymbolCollector.cpp index/SymbolYAML.cpp - index/dex/DexIndex.cpp + index/dex/Dex.cpp index/dex/Iterator.cpp index/dex/Trigram.cpp diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/SymbolYAML.cpp index 756d208e1..01c7987ac 100644 --- a/clangd/index/SymbolYAML.cpp +++ b/clangd/index/SymbolYAML.cpp @@ -11,7 +11,7 @@ #include "Index.h" #include "Serialization.h" #include "Trace.h" -#include "dex/DexIndex.h" +#include "dex/Dex.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Errc.h" @@ -225,7 +225,7 @@ std::unique_ptr loadIndex(llvm::StringRef SymbolFilename, if (!Slab) return nullptr; trace::Span Tracer("BuildIndex"); - return UseDex ? dex::DexIndex::build(std::move(*Slab), URISchemes) + return UseDex ? dex::Dex::build(std::move(*Slab), URISchemes) : MemIndex::build(std::move(*Slab), RefSlab()); } diff --git a/clangd/index/dex/DexIndex.cpp b/clangd/index/dex/Dex.cpp similarity index 94% rename from clangd/index/dex/DexIndex.cpp rename to clangd/index/dex/Dex.cpp index b8282e7d2..cc8aa0aee 100644 --- a/clangd/index/dex/DexIndex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -1,4 +1,4 @@ -//===--- DexIndex.cpp - Dex Symbol Index Implementation ---------*- C++ -*-===// +//===--- Dex.cpp - Dex Symbol Index Implementation --------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "DexIndex.h" +#include "Dex.h" #include "FileDistance.h" #include "FuzzyMatch.h" #include "Logger.h" @@ -90,7 +90,7 @@ std::vector> createFileProximityIterators( } // namespace -void DexIndex::buildIndex() { +void Dex::buildIndex() { std::vector> ScoredSymbols(Symbols.size()); for (size_t I = 0; I < Symbols.size(); ++I) { @@ -119,16 +119,15 @@ void DexIndex::buildIndex() { InvertedIndex[Token].push_back(SymbolRank); } - vlog("Built DexIndex with estimated memory usage {0} bytes.", + vlog("Built Dex with estimated memory usage {0} bytes.", estimateMemoryUsage()); } /// Constructs iterators over tokens extracted from the query and exhausts it /// while applying Callback to each symbol in the order of decreasing quality /// of the matched symbols. -bool DexIndex::fuzzyFind( - const FuzzyFindRequest &Req, - llvm::function_ref Callback) const { +bool Dex::fuzzyFind(const FuzzyFindRequest &Req, + llvm::function_ref Callback) const { assert(!StringRef(Req.Query).contains("::") && "There must be no :: in query."); FuzzyMatcher Filter(Req.Query); @@ -213,8 +212,8 @@ bool DexIndex::fuzzyFind( return More; } -void DexIndex::lookup(const LookupRequest &Req, - llvm::function_ref Callback) const { +void Dex::lookup(const LookupRequest &Req, + llvm::function_ref Callback) const { for (const auto &ID : Req.IDs) { auto I = LookupTable.find(ID); if (I != LookupTable.end()) @@ -222,12 +221,12 @@ void DexIndex::lookup(const LookupRequest &Req, } } -void DexIndex::refs(const RefsRequest &Req, - llvm::function_ref Callback) const { +void Dex::refs(const RefsRequest &Req, + llvm::function_ref Callback) const { log("refs is not implemented."); } -size_t DexIndex::estimateMemoryUsage() const { +size_t Dex::estimateMemoryUsage() const { size_t Bytes = LookupTable.size() * sizeof(std::pair); Bytes += SymbolQuality.size() * sizeof(std::pair); diff --git a/clangd/index/dex/DexIndex.h b/clangd/index/dex/Dex.h similarity index 86% rename from clangd/index/dex/DexIndex.h rename to clangd/index/dex/Dex.h index d4f385769..fab2eabfb 100644 --- a/clangd/index/dex/DexIndex.h +++ b/clangd/index/dex/Dex.h @@ -1,4 +1,4 @@ -//===--- DexIndex.h - Dex Symbol Index Implementation -----------*- C++ -*-===// +//===--- Dex.h - Dex Symbol Index Implementation ----------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -17,8 +17,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEXINDEX_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEXINDEX_H +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEX_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEX_H #include "Iterator.h" #include "Token.h" @@ -37,11 +37,11 @@ namespace dex { // changed frequently, it's safe to assume that it has to be built only once // (when the clangd process starts). Therefore, it can be easier to store built // index on disk and then load it if available. -class DexIndex : public SymbolIndex { +class Dex : public SymbolIndex { public: // All symbols must outlive this index. template - DexIndex(Range &&Symbols, llvm::ArrayRef Schemes) + Dex(Range &&Symbols, llvm::ArrayRef Schemes) : URISchemes(Schemes) { // If Schemes don't contain any items, fall back to SymbolCollector's // default URI schemes. @@ -55,9 +55,9 @@ class DexIndex : public SymbolIndex { } // Symbols are owned by BackingData, Index takes ownership. template - DexIndex(Range &&Symbols, Payload &&BackingData, - llvm::ArrayRef URISchemes) - : DexIndex(std::forward(Symbols), URISchemes) { + Dex(Range &&Symbols, Payload &&BackingData, + llvm::ArrayRef URISchemes) + : Dex(std::forward(Symbols), URISchemes) { KeepAlive = std::shared_ptr( std::make_shared(std::move(BackingData)), nullptr); } @@ -65,7 +65,7 @@ class DexIndex : public SymbolIndex { /// Builds an index from a slab. The index takes ownership of the slab. static std::unique_ptr build(SymbolSlab Slab, llvm::ArrayRef URISchemes) { - return llvm::make_unique(Slab, std::move(Slab), URISchemes); + return llvm::make_unique(Slab, std::move(Slab), URISchemes); } bool @@ -110,4 +110,4 @@ std::vector generateProximityURIs(llvm::StringRef URIPath); } // namespace clangd } // namespace clang -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEXINDEX_H +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEX_H diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 0155f1703..d5c5d18f1 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -13,7 +13,6 @@ #include "RIFF.h" #include "Trace.h" #include "index/SymbolYAML.h" -#include "index/dex/DexIndex.h" #include "clang/Basic/Version.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" diff --git a/unittests/clangd/CMakeLists.txt b/unittests/clangd/CMakeLists.txt index dee2e4e22..a870c2990 100644 --- a/unittests/clangd/CMakeLists.txt +++ b/unittests/clangd/CMakeLists.txt @@ -16,7 +16,7 @@ add_extra_unittest(ClangdTests CodeCompleteTests.cpp CodeCompletionStringsTests.cpp ContextTests.cpp - DexIndexTests.cpp + DexTests.cpp DraftStoreTests.cpp FileDistanceTests.cpp FileIndexTests.cpp diff --git a/unittests/clangd/DexIndexTests.cpp b/unittests/clangd/DexTests.cpp similarity index 89% rename from unittests/clangd/DexIndexTests.cpp rename to unittests/clangd/DexTests.cpp index fe7f10137..67d837003 100644 --- a/unittests/clangd/DexIndexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -1,4 +1,4 @@ -//===-- DexIndexTests.cpp ----------------------------*- C++ -*-----------===// +//===-- DexTests.cpp ---------------------------------*- C++ -*-----------===// // // The LLVM Compiler Infrastructure // @@ -12,7 +12,7 @@ #include "TestIndex.h" #include "index/Index.h" #include "index/Merge.h" -#include "index/dex/DexIndex.h" +#include "index/dex/Dex.h" #include "index/dex/Iterator.h" #include "index/dex/Token.h" #include "index/dex/Trigram.h" @@ -46,7 +46,7 @@ std::vector consumeIDs(Iterator &It) { return IDs; } -TEST(DexIndexIterators, DocumentIterator) { +TEST(DexIterators, DocumentIterator) { const PostingList L = {4, 7, 8, 20, 42, 100}; auto DocIterator = create(L); @@ -69,7 +69,7 @@ TEST(DexIndexIterators, DocumentIterator) { EXPECT_TRUE(DocIterator->reachedEnd()); } -TEST(DexIndexIterators, AndWithEmpty) { +TEST(DexIterators, AndWithEmpty) { const PostingList L0; const PostingList L1 = {0, 5, 7, 10, 42, 320, 9000}; @@ -82,7 +82,7 @@ TEST(DexIndexIterators, AndWithEmpty) { EXPECT_THAT(consumeIDs(*AndWithEmpty), ElementsAre()); } -TEST(DexIndexIterators, AndTwoLists) { +TEST(DexIterators, AndTwoLists) { const PostingList L0 = {0, 5, 7, 10, 42, 320, 9000}; const PostingList L1 = {0, 4, 7, 10, 30, 60, 320, 9000}; @@ -106,7 +106,7 @@ TEST(DexIndexIterators, AndTwoLists) { And->advanceTo(9001); } -TEST(DexIndexIterators, AndThreeLists) { +TEST(DexIterators, AndThreeLists) { const PostingList L0 = {0, 5, 7, 10, 42, 320, 9000}; const PostingList L1 = {0, 4, 7, 10, 30, 60, 320, 9000}; const PostingList L2 = {1, 4, 7, 11, 30, 60, 320, 9000}; @@ -120,7 +120,7 @@ TEST(DexIndexIterators, AndThreeLists) { EXPECT_TRUE(And->reachedEnd()); } -TEST(DexIndexIterators, OrWithEmpty) { +TEST(DexIterators, OrWithEmpty) { const PostingList L0; const PostingList L1 = {0, 5, 7, 10, 42, 320, 9000}; @@ -134,7 +134,7 @@ TEST(DexIndexIterators, OrWithEmpty) { ElementsAre(0U, 5U, 7U, 10U, 42U, 320U, 9000U)); } -TEST(DexIndexIterators, OrTwoLists) { +TEST(DexIterators, OrTwoLists) { const PostingList L0 = {0, 5, 7, 10, 42, 320, 9000}; const PostingList L1 = {0, 4, 7, 10, 30, 60, 320, 9000}; @@ -167,7 +167,7 @@ TEST(DexIndexIterators, OrTwoLists) { ElementsAre(0U, 4U, 5U, 7U, 10U, 30U, 42U, 60U, 320U, 9000U)); } -TEST(DexIndexIterators, OrThreeLists) { +TEST(DexIterators, OrThreeLists) { const PostingList L0 = {0, 5, 7, 10, 42, 320, 9000}; const PostingList L1 = {0, 4, 7, 10, 30, 60, 320, 9000}; const PostingList L2 = {1, 4, 7, 11, 30, 60, 320, 9000}; @@ -197,7 +197,7 @@ TEST(DexIndexIterators, OrThreeLists) { // etc iterators) appear. However, it is not exhaustive and it would be // beneficial to implement automatic generation (e.g. fuzzing) of query trees // for more comprehensive testing. -TEST(DexIndexIterators, QueryTree) { +TEST(DexIterators, QueryTree) { // // +-----------------+ // |And Iterator:1, 5| @@ -254,7 +254,7 @@ TEST(DexIndexIterators, QueryTree) { EXPECT_TRUE(Root->reachedEnd()); } -TEST(DexIndexIterators, StringRepresentation) { +TEST(DexIterators, StringRepresentation) { const PostingList L0 = {4, 7, 8, 20, 42, 100}; const PostingList L1 = {1, 3, 5, 8, 9}; const PostingList L2 = {1, 5, 7, 9}; @@ -272,7 +272,7 @@ TEST(DexIndexIterators, StringRepresentation) { "END] [{1}, 3, 5, 8, 9, END]))"); } -TEST(DexIndexIterators, Limit) { +TEST(DexIterators, Limit) { const PostingList L0 = {3, 6, 7, 20, 42, 100}; const PostingList L1 = {1, 3, 5, 6, 7, 30, 100}; const PostingList L2 = {0, 3, 5, 7, 8, 100}; @@ -292,7 +292,7 @@ TEST(DexIndexIterators, Limit) { EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(3, 7)); } -TEST(DexIndexIterators, True) { +TEST(DexIterators, True) { auto TrueIterator = createTrue(0U); EXPECT_TRUE(TrueIterator->reachedEnd()); EXPECT_THAT(consumeIDs(*TrueIterator), ElementsAre()); @@ -305,7 +305,7 @@ TEST(DexIndexIterators, True) { EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(1, 2, 5)); } -TEST(DexIndexIterators, Boost) { +TEST(DexIterators, Boost) { auto BoostIterator = createBoost(createTrue(5U), 42U); EXPECT_FALSE(BoostIterator->reachedEnd()); auto ElementBoost = BoostIterator->consume(); @@ -351,7 +351,7 @@ trigramsAre(std::initializer_list Trigrams) { return tokensAre(Trigrams, Token::Kind::Trigram); } -TEST(DexIndexTrigrams, IdentifierTrigrams) { +TEST(DexTrigrams, IdentifierTrigrams) { EXPECT_THAT(generateIdentifierTrigrams("X86"), trigramsAre({"x86", "x$$", "x8$"})); @@ -392,7 +392,7 @@ TEST(DexIndexTrigrams, IdentifierTrigrams) { "hkl", "ijk", "ikl", "jkl", "klm", "ab$", "ad$"})); } -TEST(DexIndexTrigrams, QueryTrigrams) { +TEST(DexTrigrams, QueryTrigrams) { EXPECT_THAT(generateQueryTrigrams("c"), trigramsAre({"c$$"})); EXPECT_THAT(generateQueryTrigrams("cl"), trigramsAre({"cl$"})); EXPECT_THAT(generateQueryTrigrams("cla"), trigramsAre({"cla"})); @@ -442,8 +442,8 @@ TEST(DexSearchTokens, SymbolPath) { // Index tests. //===----------------------------------------------------------------------===// -TEST(DexIndex, Lookup) { - auto I = DexIndex::build(generateSymbols({"ns::abc", "ns::xyz"}), URISchemes); +TEST(Dex, Lookup) { + auto I = Dex::build(generateSymbols({"ns::abc", "ns::xyz"}), URISchemes); EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::abc", "ns::xyz")); @@ -452,8 +452,8 @@ TEST(DexIndex, Lookup) { EXPECT_THAT(lookup(*I, SymbolID("ns::nonono")), UnorderedElementsAre()); } -TEST(DexIndex, FuzzyFind) { - auto Index = DexIndex::build( +TEST(Dex, FuzzyFind) { + auto Index = Dex::build( generateSymbols({"ns::ABC", "ns::BCD", "::ABC", "ns::nested::ABC", "other::ABC", "other::A"}), URISchemes); @@ -476,8 +476,8 @@ TEST(DexIndex, FuzzyFind) { "other::A")); } -TEST(DexIndexTest, FuzzyMatchQ) { - auto I = DexIndex::build( +TEST(DexTest, FuzzyMatchQ) { + auto I = Dex::build( generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}), URISchemes); FuzzyFindRequest Req; @@ -487,22 +487,22 @@ TEST(DexIndexTest, FuzzyMatchQ) { UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); } -// FIXME(kbobyrev): This test is different for DexIndex and MemIndex: while -// MemIndex manages response deduplication, DexIndex simply returns all matched +// FIXME(kbobyrev): This test is different for Dex and MemIndex: while +// MemIndex manages response deduplication, Dex simply returns all matched // symbols which means there might be equivalent symbols in the response. -// Before drop-in replacement of MemIndex with DexIndex happens, FileIndex +// Before drop-in replacement of MemIndex with Dex happens, FileIndex // should handle deduplication instead. -TEST(DexIndexTest, DexIndexDeduplicate) { +TEST(DexTest, DexDeduplicate) { std::vector Symbols = {symbol("1"), symbol("2"), symbol("3"), symbol("2") /* duplicate */}; FuzzyFindRequest Req; Req.Query = "2"; - DexIndex I(Symbols, URISchemes); + Dex I(Symbols, URISchemes); EXPECT_THAT(match(I, Req), ElementsAre("2", "2")); } -TEST(DexIndexTest, DexIndexLimitedNumMatches) { - auto I = DexIndex::build(generateNumSymbols(0, 100), URISchemes); +TEST(DexTest, DexLimitedNumMatches) { + auto I = Dex::build(generateNumSymbols(0, 100), URISchemes); FuzzyFindRequest Req; Req.Query = "5"; Req.MaxCandidateCount = 3; @@ -512,8 +512,8 @@ TEST(DexIndexTest, DexIndexLimitedNumMatches) { EXPECT_TRUE(Incomplete); } -TEST(DexIndexTest, FuzzyMatch) { - auto I = DexIndex::build( +TEST(DexTest, FuzzyMatch) { + auto I = Dex::build( generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}), URISchemes); FuzzyFindRequest Req; @@ -523,25 +523,25 @@ TEST(DexIndexTest, FuzzyMatch) { UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); } -TEST(DexIndexTest, MatchQualifiedNamesWithoutSpecificScope) { +TEST(DexTest, MatchQualifiedNamesWithoutSpecificScope) { auto I = - DexIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), URISchemes); + Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3")); } -TEST(DexIndexTest, MatchQualifiedNamesWithGlobalScope) { +TEST(DexTest, MatchQualifiedNamesWithGlobalScope) { auto I = - DexIndex::build(generateSymbols({"a::y1", "b::y2", "y3"}), URISchemes); + Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {""}; EXPECT_THAT(match(*I, Req), UnorderedElementsAre("y3")); } -TEST(DexIndexTest, MatchQualifiedNamesWithOneScope) { - auto I = DexIndex::build( +TEST(DexTest, MatchQualifiedNamesWithOneScope) { + auto I = Dex::build( generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; @@ -549,8 +549,8 @@ TEST(DexIndexTest, MatchQualifiedNamesWithOneScope) { EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "a::y2")); } -TEST(DexIndexTest, MatchQualifiedNamesWithMultipleScopes) { - auto I = DexIndex::build( +TEST(DexTest, MatchQualifiedNamesWithMultipleScopes) { + auto I = Dex::build( generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; @@ -558,24 +558,24 @@ TEST(DexIndexTest, MatchQualifiedNamesWithMultipleScopes) { EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "a::y2", "b::y3")); } -TEST(DexIndexTest, NoMatchNestedScopes) { - auto I = DexIndex::build(generateSymbols({"a::y1", "a::b::y2"}), URISchemes); +TEST(DexTest, NoMatchNestedScopes) { + auto I = Dex::build(generateSymbols({"a::y1", "a::b::y2"}), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1")); } -TEST(DexIndexTest, IgnoreCases) { - auto I = DexIndex::build(generateSymbols({"ns::ABC", "ns::abc"}), URISchemes); +TEST(DexTest, IgnoreCases) { + auto I = Dex::build(generateSymbols({"ns::ABC", "ns::abc"}), URISchemes); FuzzyFindRequest Req; Req.Query = "AB"; Req.Scopes = {"ns::"}; EXPECT_THAT(match(*I, Req), UnorderedElementsAre("ns::ABC", "ns::abc")); } -TEST(DexIndexTest, Lookup) { - auto I = DexIndex::build(generateSymbols({"ns::abc", "ns::xyz"}), URISchemes); +TEST(DexTest, Lookup) { + auto I = Dex::build(generateSymbols({"ns::abc", "ns::xyz"}), URISchemes); EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::abc", "ns::xyz")); @@ -584,14 +584,14 @@ TEST(DexIndexTest, Lookup) { EXPECT_THAT(lookup(*I, SymbolID("ns::nonono")), UnorderedElementsAre()); } -TEST(DexIndexTest, ProximityPathsBoosting) { +TEST(DexTest, ProximityPathsBoosting) { auto RootSymbol = symbol("root::abc"); RootSymbol.CanonicalDeclaration.FileURI = "unittest:///file.h"; auto CloseSymbol = symbol("close::abc"); CloseSymbol.CanonicalDeclaration.FileURI = "unittest:///a/b/c/d/e/f/file.h"; std::vector Symbols{CloseSymbol, RootSymbol}; - DexIndex I(Symbols, URISchemes); + Dex I(Symbols, URISchemes); FuzzyFindRequest Req; Req.Query = "abc"; diff --git a/unittests/clangd/TestIndex.h b/unittests/clangd/TestIndex.h index 3f7d1e295..01dcc086b 100644 --- a/unittests/clangd/TestIndex.h +++ b/unittests/clangd/TestIndex.h @@ -12,10 +12,6 @@ #include "index/Index.h" #include "index/Merge.h" -#include "index/dex/DexIndex.h" -#include "index/dex/Iterator.h" -#include "index/dex/Token.h" -#include "index/dex/Trigram.h" namespace clang { namespace clangd { From d643747e92fd64ccffba02e690ffef093da43407 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Mon, 10 Sep 2018 08:52:04 +0000 Subject: [PATCH 160/686] ReleaseNotes: update links to use https git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341787 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/ReleaseNotes.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index c660bc762..b18ee76ea 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -6,13 +6,13 @@ Extra Clang Tools 8.0.0 (In-Progress) Release Notes :local: :depth: 3 -Written by the `LLVM Team `_ +Written by the `LLVM Team `_ .. warning:: These are in-progress notes for the upcoming Extra Clang Tools 8 release. Release notes for previous releases can be found on - `the Download Page `_. + `the Download Page `_. Introduction ============ @@ -21,16 +21,16 @@ This document contains the release notes for the Extra Clang Tools, part of the Clang release 8.0.0. Here we describe the status of the Extra Clang Tools in some detail, including major improvements from the previous release and new feature work. All LLVM releases may be downloaded from the `LLVM releases web -site `_. +site `_. For more information about Clang or LLVM, including information about -the latest release, please see the `Clang Web Site `_ or -the `LLVM Web Site `_. +the latest release, please see the `Clang Web Site `_ or +the `LLVM Web Site `_. Note that if you are reading this file from a Subversion checkout or the main Clang web page, this document applies to the *next* release, not the current one. To see the release notes for a specific release, please -see the `releases page `_. +see the `releases page `_. What's New in Extra Clang Tools 8.0.0? ====================================== From fc5164a15bf68b3d24e133c2fbdeffc2477b5223 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Mon, 10 Sep 2018 10:00:47 +0000 Subject: [PATCH 161/686] [clangd] Fix async index loading (from r341376). Summary: This wasn't actually async (due to std::future destructor blocking). If it were, we would have clean shutdown issues if main returned and destroyed Placeholder before the thread is done with it. We could attempt to avoid any blocking by using shared_ptr or weak_ptr tricks so the thread can detect Placeholder's destruction, but there are other potential issues (e.g. loadIndex does tracing, and we'll destroy the tracer...) Instead, once LSPServer::run returns, we wait for the index to finish loading before exiting. Performance is not critical in this situation. Reviewers: ilya-biryukov Subscribers: ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51674 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341797 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/tool/ClangdMain.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index d5c5d18f1..c7d99d2a1 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -270,14 +270,17 @@ int main(int argc, char *argv[]) { Opts.ResourceDir = ResourceDir; Opts.BuildDynamicSymbolIndex = EnableIndex; std::unique_ptr StaticIdx; + std::future AsyncIndexLoad; // Block exit while loading the index. if (EnableIndex && !YamlSymbolFile.empty()) { // Load the index asynchronously. Meanwhile SwapIndex returns no results. SwapIndex *Placeholder; StaticIdx.reset(Placeholder = new SwapIndex(llvm::make_unique())); - runAsync([Placeholder, &Opts] { + AsyncIndexLoad = runAsync([Placeholder, &Opts] { if (auto Idx = loadIndex(YamlSymbolFile, Opts.URISchemes, UseDex)) Placeholder->reset(std::move(Idx)); }); + if (RunSynchronously) + AsyncIndexLoad.wait(); } Opts.StaticIndex = StaticIdx.get(); Opts.AsyncThreadsCount = WorkerThreadsCount; From 250f000fca02620fe20a2ca500407e9f37504952 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 10 Sep 2018 11:46:07 +0000 Subject: [PATCH 162/686] [clangd] Add symbol slab size to index memory consumption estimates Currently, `SymbolIndex::estimateMemoryUsage()` returns the "overhead" estimate, i.e. the estimate of the Index data structure excluding backing data (such as Symbol Slab and Reference Slab). This patch propagates information about paired data size where necessary. Reviewed By: ioeric, sammccall Differential Revision: https://reviews.llvm.org/D51539 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341800 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/FileIndex.cpp | 9 ++++++++- clangd/index/MemIndex.cpp | 7 +++++-- clangd/index/MemIndex.h | 6 +++++- clangd/index/dex/Dex.cpp | 14 ++++++-------- clangd/index/dex/Dex.h | 10 ++++++++-- unittests/clangd/IndexTests.cpp | 10 +++++----- 6 files changed, 37 insertions(+), 19 deletions(-) diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 0c5fe4bbd..20bfb871f 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -127,11 +127,18 @@ std::unique_ptr FileSymbols::buildMemIndex() { } } + size_t StorageSize = RefsStorage.size() * sizeof(Ref); + for (const auto &Slab : SymbolSlabs) + StorageSize += Slab->bytes(); + for (const auto &RefSlab : RefSlabs) + StorageSize += RefSlab->bytes(); + // Index must keep the slabs and contiguous ranges alive. return llvm::make_unique( llvm::make_pointee_range(AllSymbols), std::move(AllRefs), std::make_tuple(std::move(SymbolSlabs), std::move(RefSlabs), - std::move(RefsStorage))); + std::move(RefsStorage)), + StorageSize); } void FileIndex::update(PathRef Path, ASTContext *AST, diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index 2ef0eaa42..2cb20a785 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -16,8 +16,11 @@ namespace clang { namespace clangd { std::unique_ptr MemIndex::build(SymbolSlab Slab, RefSlab Refs) { + // Store Slab size before it is moved. + const auto BackingDataSize = Slab.bytes() + Refs.bytes(); auto Data = std::make_pair(std::move(Slab), std::move(Refs)); - return llvm::make_unique(Data.first, Data.second, std::move(Data)); + return llvm::make_unique(Data.first, Data.second, std::move(Data), + BackingDataSize); } bool MemIndex::fuzzyFind( @@ -70,7 +73,7 @@ void MemIndex::refs(const RefsRequest &Req, } size_t MemIndex::estimateMemoryUsage() const { - return Index.getMemorySize(); + return Index.getMemorySize() + Refs.getMemorySize() + BackingDataSize; } } // namespace clangd diff --git a/clangd/index/MemIndex.h b/clangd/index/MemIndex.h index add458962..24f2ba197 100644 --- a/clangd/index/MemIndex.h +++ b/clangd/index/MemIndex.h @@ -30,11 +30,13 @@ class MemIndex : public SymbolIndex { } // Symbols are owned by BackingData, Index takes ownership. template - MemIndex(SymbolRange &&Symbols, RefRange &&Refs, Payload &&BackingData) + MemIndex(SymbolRange &&Symbols, RefRange &&Refs, Payload &&BackingData, + size_t BackingDataSize) : MemIndex(std::forward(Symbols), std::forward(Refs)) { KeepAlive = std::shared_ptr( std::make_shared(std::move(BackingData)), nullptr); + this->BackingDataSize = BackingDataSize; } /// Builds an index from slabs. The index takes ownership of the data. @@ -58,6 +60,8 @@ class MemIndex : public SymbolIndex { // A map from symbol ID to symbol refs, support query by IDs. llvm::DenseMap> Refs; std::shared_ptr KeepAlive; // poor man's move-only std::any + // Size of memory retained by KeepAlive. + size_t BackingDataSize = 0; }; } // namespace clangd diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index cc8aa0aee..59d448cc2 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -227,15 +227,13 @@ void Dex::refs(const RefsRequest &Req, } size_t Dex::estimateMemoryUsage() const { - size_t Bytes = - LookupTable.size() * sizeof(std::pair); - Bytes += SymbolQuality.size() * sizeof(std::pair); - Bytes += InvertedIndex.size() * sizeof(Token); - - for (const auto &P : InvertedIndex) { + size_t Bytes = Symbols.size() * sizeof(const Symbol *); + Bytes += SymbolQuality.size() * sizeof(float); + Bytes += LookupTable.getMemorySize(); + Bytes += InvertedIndex.getMemorySize(); + for (const auto &P : InvertedIndex) Bytes += P.second.size() * sizeof(DocID); - } - return Bytes; + return Bytes + BackingDataSize; } std::vector generateProximityURIs(llvm::StringRef URIPath) { diff --git a/clangd/index/dex/Dex.h b/clangd/index/dex/Dex.h index fab2eabfb..4d9baa7a5 100644 --- a/clangd/index/dex/Dex.h +++ b/clangd/index/dex/Dex.h @@ -55,17 +55,21 @@ class Dex : public SymbolIndex { } // Symbols are owned by BackingData, Index takes ownership. template - Dex(Range &&Symbols, Payload &&BackingData, + Dex(Range &&Symbols, Payload &&BackingData, size_t BackingDataSize, llvm::ArrayRef URISchemes) : Dex(std::forward(Symbols), URISchemes) { KeepAlive = std::shared_ptr( std::make_shared(std::move(BackingData)), nullptr); + this->BackingDataSize = BackingDataSize; } /// Builds an index from a slab. The index takes ownership of the slab. static std::unique_ptr build(SymbolSlab Slab, llvm::ArrayRef URISchemes) { - return llvm::make_unique(Slab, std::move(Slab), URISchemes); + // Store Slab size before it is moved. + const auto BackingDataSize = Slab.bytes(); + return llvm::make_unique(Slab, std::move(Slab), BackingDataSize, + URISchemes); } bool @@ -96,6 +100,8 @@ class Dex : public SymbolIndex { /// during the fuzzyFind process. llvm::DenseMap InvertedIndex; std::shared_ptr KeepAlive; // poor man's move-only std::any + // Size of memory retained by KeepAlive. + size_t BackingDataSize = 0; std::vector URISchemes; }; diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp index 7742e5201..2eb9c23be 100644 --- a/unittests/clangd/IndexTests.cpp +++ b/unittests/clangd/IndexTests.cpp @@ -10,11 +10,11 @@ #include "Annotations.h" #include "TestIndex.h" #include "TestTU.h" -#include "gmock/gmock.h" #include "index/FileIndex.h" #include "index/Index.h" #include "index/MemIndex.h" #include "index/Merge.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" using testing::_; @@ -58,11 +58,11 @@ TEST(SwapIndexTest, OldIndexRecycled) { auto Token = std::make_shared(); std::weak_ptr WeakToken = Token; - SwapIndex S( - llvm::make_unique(SymbolSlab(), RefSlab(), std::move(Token))); - EXPECT_FALSE(WeakToken.expired()); // Current MemIndex keeps it alive. + SwapIndex S(llvm::make_unique( + SymbolSlab(), RefSlab(), std::move(Token), /*BackingDataSize=*/0)); + EXPECT_FALSE(WeakToken.expired()); // Current MemIndex keeps it alive. S.reset(llvm::make_unique()); // Now the MemIndex is destroyed. - EXPECT_TRUE(WeakToken.expired()); // So the token is too. + EXPECT_TRUE(WeakToken.expired()); // So the token is too. } TEST(MemIndexTest, MemIndexDeduplicate) { From 22f57fd308da05b405c19ea53995025725ee390a Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 10 Sep 2018 11:51:05 +0000 Subject: [PATCH 163/686] [clangd] Implement FuzzyFindRequest JSON (de)serialization JSON (de)serialization of `FuzzyFindRequest` might be useful for both D51090 and D51628. Also, this allows precise logging of the fuzzy find requests. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D51852 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341802 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 3 +-- clangd/index/Index.cpp | 27 +++++++++++++++++++++++++++ clangd/index/Index.h | 6 +++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 354a2770e..1e632dce9 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -1381,8 +1381,7 @@ class CodeCompleteFlow { Req.Scopes = QueryScopes; // FIXME: we should send multiple weighted paths here. Req.ProximityPaths.push_back(FileName); - vlog("Code complete: fuzzyFind(\"{0}\", scopes=[{1}])", Req.Query, - llvm::join(Req.Scopes.begin(), Req.Scopes.end(), ",")); + vlog("Code complete: fuzzyFind({0:2})", toJSON(Req)); if (SpecFuzzyFind) SpecFuzzyFind->NewReq = Req; diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index a01c49f65..013ba5cf9 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -175,6 +175,33 @@ std::shared_ptr SwapIndex::snapshot() const { return Index; } +bool fromJSON(const llvm::json::Value &Parameters, FuzzyFindRequest &Request) { + json::ObjectMapper O(Parameters); + llvm::Optional MaxCandidateCount; + bool OK = + O && O.map("Query", Request.Query) && O.map("Scopes", Request.Scopes) && + O.map("RestrictForCodeCompletion", Request.RestrictForCodeCompletion) && + O.map("ProximityPaths", Request.ProximityPaths) && + O.map("MaxCandidateCount", MaxCandidateCount); + if (MaxCandidateCount) + Request.MaxCandidateCount = MaxCandidateCount.getValue(); + return OK; +} + +llvm::json::Value toJSON(const FuzzyFindRequest &Request) { + auto Result = json::Object{ + {"Query", Request.Query}, + {"Scopes", json::Array{Request.Scopes}}, + {"RestrictForCodeCompletion", Request.RestrictForCodeCompletion}, + {"ProximityPaths", json::Array{Request.ProximityPaths}}, + }; + // A huge limit means no limit, leave it out. + if (Request.MaxCandidateCount <= std::numeric_limits::max()) + Result["MaxCandidateCount"] = + static_cast(Request.MaxCandidateCount); + return Result; +} + bool SwapIndex::fuzzyFind(const FuzzyFindRequest &R, llvm::function_ref CB) const { return snapshot()->fuzzyFind(R, CB); diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 12727efaa..b531d1e29 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -19,8 +19,10 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/StringSaver.h" #include +#include #include #include #include @@ -435,7 +437,7 @@ struct FuzzyFindRequest { std::vector Scopes; /// \brief The number of top candidates to return. The index may choose to /// return more than this, e.g. if it doesn't know which candidates are best. - size_t MaxCandidateCount = UINT_MAX; + size_t MaxCandidateCount = std::numeric_limits::max(); /// If set to true, only symbols for completion support will be considered. bool RestrictForCodeCompletion = false; /// Contextually relevant files (e.g. the file we're code-completing in). @@ -450,6 +452,8 @@ struct FuzzyFindRequest { } bool operator!=(const FuzzyFindRequest &Req) const { return !(*this == Req); } }; +bool fromJSON(const llvm::json::Value &Value, FuzzyFindRequest &Request); +llvm::json::Value toJSON(const FuzzyFindRequest &Request); struct LookupRequest { llvm::DenseSet IDs; From 3e809c55711467050303ede2891e87ed4da9e99e Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Mon, 10 Sep 2018 14:22:42 +0000 Subject: [PATCH 164/686] [clangd] Add unittests for D51038 Reviewers: ilya-biryukov, ioeric, hokein Reviewed By: ilya-biryukov Subscribers: MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D51039 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341830 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/CodeCompleteTests.cpp | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 7e7fde7a9..c1010038d 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1917,6 +1917,56 @@ TEST(CompletionTest, DeprecatedResults) { AllOf(Named("TestClangc"), Deprecated()))); } +TEST(SignatureHelpTest, InsideArgument) { + { + const auto Results = signatures(R"cpp( + void foo(int x); + void foo(int x, int y); + int main() { foo(1+^); } + )cpp"); + EXPECT_THAT( + Results.signatures, + ElementsAre(Sig("foo(int x) -> void", {"int x"}), + Sig("foo(int x, int y) -> void", {"int x", "int y"}))); + EXPECT_EQ(0, Results.activeParameter); + } + { + const auto Results = signatures(R"cpp( + void foo(int x); + void foo(int x, int y); + int main() { foo(1^); } + )cpp"); + EXPECT_THAT( + Results.signatures, + ElementsAre(Sig("foo(int x) -> void", {"int x"}), + Sig("foo(int x, int y) -> void", {"int x", "int y"}))); + EXPECT_EQ(0, Results.activeParameter); + } + { + const auto Results = signatures(R"cpp( + void foo(int x); + void foo(int x, int y); + int main() { foo(1^0); } + )cpp"); + EXPECT_THAT( + Results.signatures, + ElementsAre(Sig("foo(int x) -> void", {"int x"}), + Sig("foo(int x, int y) -> void", {"int x", "int y"}))); + EXPECT_EQ(0, Results.activeParameter); + } + { + const auto Results = signatures(R"cpp( + void foo(int x); + void foo(int x, int y); + int bar(int x, int y); + int main() { bar(foo(2, 3^)); } + )cpp"); + EXPECT_THAT(Results.signatures, ElementsAre(Sig("foo(int x, int y) -> void", + {"int x", "int y"}))); + EXPECT_EQ(1, Results.activeParameter); + } +} + } // namespace } // namespace clangd } // namespace clang From 91c7d8355f0e8a1f5a387f5d00f0775ead0b3fae Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 10 Sep 2018 14:31:38 +0000 Subject: [PATCH 165/686] [clangd] Unbreak buildbots after r341802 Solution: use std::move when returning result from toJSON(...). git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341832 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Index.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index 013ba5cf9..c0c821a8e 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -199,7 +199,7 @@ llvm::json::Value toJSON(const FuzzyFindRequest &Request) { if (Request.MaxCandidateCount <= std::numeric_limits::max()) Result["MaxCandidateCount"] = static_cast(Request.MaxCandidateCount); - return Result; + return std::move(Result); } bool SwapIndex::fuzzyFind(const FuzzyFindRequest &R, From d204c088c7d8f6892bb0145794dd243f8c91ea96 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Mon, 10 Sep 2018 18:05:13 +0000 Subject: [PATCH 166/686] [clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer Summary: - If a function is unresolved, assume it mutates its arguments - Follow unresolved member expressions for nested mutations Reviewers: aaron.ballman, JonasToth, george.karpenkov Subscribers: xazax.hun, a.sidorin, cfe-commits Differential Revision: https://reviews.llvm.org/D50619 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341848 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/utils/ExprMutationAnalyzer.cpp | 26 ++++- .../clang-tidy/ExprMutationAnalyzerTest.cpp | 110 +++++++++++++++++- 2 files changed, 126 insertions(+), 10 deletions(-) diff --git a/clang-tidy/utils/ExprMutationAnalyzer.cpp b/clang-tidy/utils/ExprMutationAnalyzer.cpp index 424fa8f67..f1cd1daf5 100644 --- a/clang-tidy/utils/ExprMutationAnalyzer.cpp +++ b/clang-tidy/utils/ExprMutationAnalyzer.cpp @@ -145,11 +145,16 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { hasUnaryOperand(equalsNode(Exp))); // Invoking non-const member function. + // A member function is assumed to be non-const when it is unresolved. const auto NonConstMethod = cxxMethodDecl(unless(isConst())); const auto AsNonConstThis = expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))), cxxOperatorCallExpr(callee(NonConstMethod), - hasArgument(0, equalsNode(Exp))))); + hasArgument(0, equalsNode(Exp))), + callExpr(callee(expr(anyOf( + unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))), + cxxDependentScopeMemberExpr( + hasObjectExpression(equalsNode(Exp))))))))); // Taking address of 'Exp'. // We're assuming 'Exp' is mutated as soon as its address is taken, though in @@ -165,10 +170,16 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp))); // Used as non-const-ref argument when calling a function. + // An argument is assumed to be non-const-ref when the function is unresolved. const auto NonConstRefParam = forEachArgumentWithParam( equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType()))); - const auto AsNonConstRefArg = - anyOf(callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam)); + const auto AsNonConstRefArg = anyOf( + callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam), + callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(), + cxxDependentScopeMemberExpr(), + hasType(templateTypeParmType())))), + hasAnyArgument(equalsNode(Exp))), + cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp)))); // Captured by a lambda by reference. // If we're initializing a capture with 'Exp' directly then we're initializing @@ -195,9 +206,12 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) { // Check whether any member of 'Exp' is mutated. - const auto MemberExprs = match( - findAll(memberExpr(hasObjectExpression(equalsNode(Exp))).bind("expr")), - *Stm, *Context); + const auto MemberExprs = + match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))), + cxxDependentScopeMemberExpr( + hasObjectExpression(equalsNode(Exp))))) + .bind("expr")), + *Stm, *Context); return findExprMutation(MemberExprs); } diff --git a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp index 042d4cbef..117bcfe42 100644 --- a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp +++ b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp @@ -114,6 +114,26 @@ TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) { EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); } +TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) { + auto AST = tooling::buildASTFromCode( + "struct X { template void mf(); };" + "template void f() { X x; x.mf(); }"); + auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); + + AST = + tooling::buildASTFromCode("template void f() { T x; x.mf(); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); + + AST = tooling::buildASTFromCode( + "template struct X;" + "template void f() { X x; x.mf(); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); +} + TEST(ExprMutationAnalyzerTest, ConstMemberFunc) { const auto AST = tooling::buildASTFromCode( "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }"); @@ -292,6 +312,51 @@ TEST(ExprMutationAnalyzerTest, Forward) { ElementsAre("std::forward(x)")); } +TEST(ExprMutationAnalyzerTest, CallUnresolved) { + auto AST = + tooling::buildASTFromCode("template void f() { T x; g(x); }"); + auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); + + AST = tooling::buildASTFromCode( + "template void f() { char x[N]; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); + + AST = tooling::buildASTFromCode( + "template void f(T t) { int x; g(t, x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)")); + + AST = tooling::buildASTFromCode( + "template void f(T t) { int x; t.mf(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)")); + + AST = tooling::buildASTFromCode( + "template struct S;" + "template void f() { S s; int x; s.mf(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)")); + + AST = tooling::buildASTFromCode( + "struct S { template void mf(); };" + "template void f(S s) { int x; s.mf(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)")); + + AST = tooling::buildASTFromCode("template " + "void g(F f) { int x; f(x); } "); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)")); + + AST = tooling::buildASTFromCode( + "template void f() { int x; (void)T(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)")); +} + TEST(ExprMutationAnalyzerTest, ReturnAsValue) { const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }"); const auto Results = @@ -347,6 +412,21 @@ TEST(ExprMutationAnalyzerTest, ArrayToPointerDecay) { EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); } +TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) { + const auto AST = tooling::buildASTFromCode( + "template struct S { static constexpr int v = 8; };" + "template <> struct S { static constexpr int v = 4; };" + "void g(char*);" + "template void f() { char x[S::v]; g(x); }" + "template <> void f() { char y[S::v]; g(y); }"); + const auto ResultsX = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)")); + const auto ResultsY = + match(withEnclosingCompound(declRefTo("y")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y")); +} + TEST(ExprMutationAnalyzerTest, FollowRefModified) { const auto AST = tooling::buildASTFromCode( "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; " @@ -398,21 +478,43 @@ TEST(ExprMutationAnalyzerTest, ArrayElementNotModified) { } TEST(ExprMutationAnalyzerTest, NestedMemberModified) { - const auto AST = tooling::buildASTFromCode( + auto AST = tooling::buildASTFromCode( "void f() { struct A { int vi; }; struct B { A va; }; " "struct C { B vb; }; C x; x.vb.va.vi = 10; }"); - const auto Results = + auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.vb.va.vi = 10")); + + AST = tooling::buildASTFromCode( + "template void f() { T x; x.y.z = 10; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10")); + + AST = tooling::buildASTFromCode( + "template struct S;" + "template void f() { S x; x.y.z = 10; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10")); } TEST(ExprMutationAnalyzerTest, NestedMemberNotModified) { - const auto AST = tooling::buildASTFromCode( + auto AST = tooling::buildASTFromCode( "void f() { struct A { int vi; }; struct B { A va; }; " "struct C { B vb; }; C x; x.vb.va.vi; }"); - const auto Results = + auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = + tooling::buildASTFromCode("template void f() { T x; x.y.z; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode( + "template struct S;" + "template void f() { S x; x.y.z; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); } TEST(ExprMutationAnalyzerTest, CastToValue) { From e8f226ac6aadac6d149ade141390052dcbac2701 Mon Sep 17 00:00:00 2001 From: Roman Lebedev Date: Mon, 10 Sep 2018 19:59:18 +0000 Subject: [PATCH 167/686] [clang-tidy] ExprMutationAnalyzer: construct from references. Fixes PR38888 Summary: I have hit this the rough way, while trying to use this in D51870. There is no particular point in storing the pointers, and moreover the pointers are assumed to be non-null, and that assumption is not enforced. If they are null, it won't be able to do anything good with them anyway. Initially i thought about simply adding asserts() that they are not null, but taking/storing references looks like even cleaner solution? Fixes [[ https://bugs.llvm.org/show_bug.cgi?id=38888 | PR38888 ]] Reviewers: JonasToth, shuaiwang, alexfh, george.karpenkov Reviewed By: shuaiwang Subscribers: xazax.hun, a.sidorin, Szelethus, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D51884 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341854 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/performance/ForRangeCopyCheck.cpp | 4 ++-- .../performance/UnnecessaryValueParamCheck.cpp | 4 ++-- clang-tidy/utils/ExprMutationAnalyzer.cpp | 16 ++++++++-------- clang-tidy/utils/ExprMutationAnalyzer.h | 6 +++--- .../clang-tidy/ExprMutationAnalyzerTest.cpp | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/clang-tidy/performance/ForRangeCopyCheck.cpp b/clang-tidy/performance/ForRangeCopyCheck.cpp index 867d95d8e..0a3cc4f6b 100644 --- a/clang-tidy/performance/ForRangeCopyCheck.cpp +++ b/clang-tidy/performance/ForRangeCopyCheck.cpp @@ -88,8 +88,8 @@ bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced( // Because the fix (changing to `const auto &`) will introduce an unused // compiler warning which can't be suppressed. // Since this case is very rare, it is safe to ignore it. - if (!utils::ExprMutationAnalyzer(ForRange.getBody(), &Context) - .isMutated(&LoopVar) && + if (!utils::ExprMutationAnalyzer(*ForRange.getBody(), Context) + .isMutated(&LoopVar) && !utils::decl_ref_expr::allDeclRefExprs(LoopVar, *ForRange.getBody(), Context) .empty()) { diff --git a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp index 8c9259c79..63b853dec 100644 --- a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp +++ b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp @@ -95,14 +95,14 @@ void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) { // Do not trigger on non-const value parameters when they are mutated either // within the function body or within init expression(s) when the function is // a ctor. - if (utils::ExprMutationAnalyzer(Function->getBody(), Result.Context) + if (utils::ExprMutationAnalyzer(*Function->getBody(), *Result.Context) .isMutated(Param)) return; // CXXCtorInitializer might also mutate Param but they're not part of function // body, so check them separately here. if (const auto *Ctor = dyn_cast(Function)) { for (const auto *Init : Ctor->inits()) { - if (utils::ExprMutationAnalyzer(Init->getInit(), Result.Context) + if (utils::ExprMutationAnalyzer(*Init->getInit(), *Result.Context) .isMutated(Param)) return; } diff --git a/clang-tidy/utils/ExprMutationAnalyzer.cpp b/clang-tidy/utils/ExprMutationAnalyzer.cpp index f1cd1daf5..f979e97a0 100644 --- a/clang-tidy/utils/ExprMutationAnalyzer.cpp +++ b/clang-tidy/utils/ExprMutationAnalyzer.cpp @@ -102,7 +102,7 @@ bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) { hasDescendant(equalsNode(Exp)))), cxxNoexceptExpr()))))) .bind("expr")), - *Stm, *Context)) != nullptr; + Stm, Context)) != nullptr; } const Stmt * @@ -125,7 +125,7 @@ ExprMutationAnalyzer::findDeclMutation(ArrayRef Matches) { const Stmt *ExprMutationAnalyzer::findDeclMutation(const Decl *Dec) { const auto Refs = match( - findAll(declRefExpr(to(equalsNode(Dec))).bind("expr")), *Stm, *Context); + findAll(declRefExpr(to(equalsNode(Dec))).bind("expr")), Stm, Context); for (const auto &RefNodes : Refs) { const auto *E = RefNodes.getNodeAs("expr"); if (findMutation(E)) @@ -200,7 +200,7 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { AsNonConstRefArg, AsLambdaRefCaptureInit, AsNonConstRefReturn)) .bind("stmt")), - *Stm, *Context); + Stm, Context); return selectFirst("stmt", Matches); } @@ -211,7 +211,7 @@ const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) { cxxDependentScopeMemberExpr( hasObjectExpression(equalsNode(Exp))))) .bind("expr")), - *Stm, *Context); + Stm, Context); return findExprMutation(MemberExprs); } @@ -220,7 +220,7 @@ const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) { const auto SubscriptExprs = match( findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp)))) .bind("expr")), - *Stm, *Context); + Stm, Context); return findExprMutation(SubscriptExprs); } @@ -233,7 +233,7 @@ const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) { implicitCastExpr(hasImplicitDestinationType( nonConstReferenceType())))) .bind("expr")), - *Stm, *Context); + Stm, Context); return findExprMutation(Casts); } @@ -245,7 +245,7 @@ const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) { hasLoopVariable( varDecl(hasType(nonConstReferenceType())).bind("decl")), hasRangeInit(equalsNode(Exp)))), - *Stm, *Context); + Stm, Context); return findDeclMutation(LoopVars); } @@ -265,7 +265,7 @@ const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) { unless(hasParent(declStmt(hasParent( cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt")))))))) .bind("decl"))), - *Stm, *Context); + Stm, Context); return findDeclMutation(Refs); } diff --git a/clang-tidy/utils/ExprMutationAnalyzer.h b/clang-tidy/utils/ExprMutationAnalyzer.h index 256bb7128..e295de9bb 100644 --- a/clang-tidy/utils/ExprMutationAnalyzer.h +++ b/clang-tidy/utils/ExprMutationAnalyzer.h @@ -23,7 +23,7 @@ namespace utils { /// a given statement. class ExprMutationAnalyzer { public: - ExprMutationAnalyzer(const Stmt *Stm, ASTContext *Context) + ExprMutationAnalyzer(const Stmt &Stm, ASTContext &Context) : Stm(Stm), Context(Context) {} bool isMutated(const Decl *Dec) { return findDeclMutation(Dec) != nullptr; } @@ -44,8 +44,8 @@ class ExprMutationAnalyzer { const Stmt *findRangeLoopMutation(const Expr *Exp); const Stmt *findReferenceMutation(const Expr *Exp); - const Stmt *const Stm; - ASTContext *const Context; + const Stmt &Stm; + ASTContext &Context; llvm::DenseMap Results; }; diff --git a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp index 117bcfe42..013493ef2 100644 --- a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp +++ b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp @@ -42,14 +42,14 @@ StmtMatcher withEnclosingCompound(ExprMatcher Matcher) { bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) { const auto *const S = selectFirst("stmt", Results); const auto *const E = selectFirst("expr", Results); - return utils::ExprMutationAnalyzer(S, &AST->getASTContext()).isMutated(E); + return utils::ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(E); } SmallVector mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) { const auto *const S = selectFirst("stmt", Results); SmallVector Chain; - utils::ExprMutationAnalyzer Analyzer(S, &AST->getASTContext()); + utils::ExprMutationAnalyzer Analyzer(*S, AST->getASTContext()); for (const auto *E = selectFirst("expr", Results); E != nullptr;) { const Stmt *By = Analyzer.findMutation(E); std::string buffer; From 888f00c3ecd73a8cb2b65aae798c79142283c26b Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Mon, 10 Sep 2018 23:58:04 +0000 Subject: [PATCH 168/686] Revert "[clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer" Summary: Tests somehow break on windows (and only on windows) http://lab.llvm.org:8011/builders/clang-x64-ninja-win7/builds/13003 http://lab.llvm.org:8011/builders/clang-x86-windows-msvc2015/builds/13747 I have yet figure out why so reverting to unbreak first. Reviewers: george.karpenkov Subscribers: xazax.hun, a.sidorin, Szelethus, cfe-commits Differential Revision: https://reviews.llvm.org/D51898 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341886 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/utils/ExprMutationAnalyzer.cpp | 26 +---- .../clang-tidy/ExprMutationAnalyzerTest.cpp | 110 +----------------- 2 files changed, 10 insertions(+), 126 deletions(-) diff --git a/clang-tidy/utils/ExprMutationAnalyzer.cpp b/clang-tidy/utils/ExprMutationAnalyzer.cpp index f979e97a0..adf4095d0 100644 --- a/clang-tidy/utils/ExprMutationAnalyzer.cpp +++ b/clang-tidy/utils/ExprMutationAnalyzer.cpp @@ -145,16 +145,11 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { hasUnaryOperand(equalsNode(Exp))); // Invoking non-const member function. - // A member function is assumed to be non-const when it is unresolved. const auto NonConstMethod = cxxMethodDecl(unless(isConst())); const auto AsNonConstThis = expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))), cxxOperatorCallExpr(callee(NonConstMethod), - hasArgument(0, equalsNode(Exp))), - callExpr(callee(expr(anyOf( - unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))), - cxxDependentScopeMemberExpr( - hasObjectExpression(equalsNode(Exp))))))))); + hasArgument(0, equalsNode(Exp))))); // Taking address of 'Exp'. // We're assuming 'Exp' is mutated as soon as its address is taken, though in @@ -170,16 +165,10 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp))); // Used as non-const-ref argument when calling a function. - // An argument is assumed to be non-const-ref when the function is unresolved. const auto NonConstRefParam = forEachArgumentWithParam( equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType()))); - const auto AsNonConstRefArg = anyOf( - callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam), - callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(), - cxxDependentScopeMemberExpr(), - hasType(templateTypeParmType())))), - hasAnyArgument(equalsNode(Exp))), - cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp)))); + const auto AsNonConstRefArg = + anyOf(callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam)); // Captured by a lambda by reference. // If we're initializing a capture with 'Exp' directly then we're initializing @@ -206,12 +195,9 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) { // Check whether any member of 'Exp' is mutated. - const auto MemberExprs = - match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))), - cxxDependentScopeMemberExpr( - hasObjectExpression(equalsNode(Exp))))) - .bind("expr")), - Stm, Context); + const auto MemberExprs = match( + findAll(memberExpr(hasObjectExpression(equalsNode(Exp))).bind("expr")), + Stm, Context); return findExprMutation(MemberExprs); } diff --git a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp index 013493ef2..9d0728bf9 100644 --- a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp +++ b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp @@ -114,26 +114,6 @@ TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) { EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); } -TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) { - auto AST = tooling::buildASTFromCode( - "struct X { template void mf(); };" - "template void f() { X x; x.mf(); }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); - - AST = - tooling::buildASTFromCode("template void f() { T x; x.mf(); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); - - AST = tooling::buildASTFromCode( - "template struct X;" - "template void f() { X x; x.mf(); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); -} - TEST(ExprMutationAnalyzerTest, ConstMemberFunc) { const auto AST = tooling::buildASTFromCode( "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }"); @@ -312,51 +292,6 @@ TEST(ExprMutationAnalyzerTest, Forward) { ElementsAre("std::forward(x)")); } -TEST(ExprMutationAnalyzerTest, CallUnresolved) { - auto AST = - tooling::buildASTFromCode("template void f() { T x; g(x); }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); - - AST = tooling::buildASTFromCode( - "template void f() { char x[N]; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); - - AST = tooling::buildASTFromCode( - "template void f(T t) { int x; g(t, x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)")); - - AST = tooling::buildASTFromCode( - "template void f(T t) { int x; t.mf(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)")); - - AST = tooling::buildASTFromCode( - "template struct S;" - "template void f() { S s; int x; s.mf(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)")); - - AST = tooling::buildASTFromCode( - "struct S { template void mf(); };" - "template void f(S s) { int x; s.mf(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)")); - - AST = tooling::buildASTFromCode("template " - "void g(F f) { int x; f(x); } "); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)")); - - AST = tooling::buildASTFromCode( - "template void f() { int x; (void)T(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)")); -} - TEST(ExprMutationAnalyzerTest, ReturnAsValue) { const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }"); const auto Results = @@ -412,21 +347,6 @@ TEST(ExprMutationAnalyzerTest, ArrayToPointerDecay) { EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); } -TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) { - const auto AST = tooling::buildASTFromCode( - "template struct S { static constexpr int v = 8; };" - "template <> struct S { static constexpr int v = 4; };" - "void g(char*);" - "template void f() { char x[S::v]; g(x); }" - "template <> void f() { char y[S::v]; g(y); }"); - const auto ResultsX = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)")); - const auto ResultsY = - match(withEnclosingCompound(declRefTo("y")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y")); -} - TEST(ExprMutationAnalyzerTest, FollowRefModified) { const auto AST = tooling::buildASTFromCode( "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; " @@ -478,43 +398,21 @@ TEST(ExprMutationAnalyzerTest, ArrayElementNotModified) { } TEST(ExprMutationAnalyzerTest, NestedMemberModified) { - auto AST = tooling::buildASTFromCode( + const auto AST = tooling::buildASTFromCode( "void f() { struct A { int vi; }; struct B { A va; }; " "struct C { B vb; }; C x; x.vb.va.vi = 10; }"); - auto Results = + const auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.vb.va.vi = 10")); - - AST = tooling::buildASTFromCode( - "template void f() { T x; x.y.z = 10; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10")); - - AST = tooling::buildASTFromCode( - "template struct S;" - "template void f() { S x; x.y.z = 10; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10")); } TEST(ExprMutationAnalyzerTest, NestedMemberNotModified) { - auto AST = tooling::buildASTFromCode( + const auto AST = tooling::buildASTFromCode( "void f() { struct A { int vi; }; struct B { A va; }; " "struct C { B vb; }; C x; x.vb.va.vi; }"); - auto Results = + const auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = - tooling::buildASTFromCode("template void f() { T x; x.y.z; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "template struct S;" - "template void f() { S x; x.y.z; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); } TEST(ExprMutationAnalyzerTest, CastToValue) { From 6237e33d0b0496af1097e2e8c3cfbdc3c0d2faeb Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Tue, 11 Sep 2018 02:23:35 +0000 Subject: [PATCH 169/686] Revert "Revert "[clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer"" This is the same as D50619 plus fixes for buildbot failures on windows. The test failures on windows are caused by -fdelayed-template-parsing and is fixed by forcing -fno-delayed-template-parsing on test cases that requires AST for uninstantiated templates. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341891 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/utils/ExprMutationAnalyzer.cpp | 26 +++- .../clang-tidy/ExprMutationAnalyzerTest.cpp | 126 +++++++++++++++++- 2 files changed, 142 insertions(+), 10 deletions(-) diff --git a/clang-tidy/utils/ExprMutationAnalyzer.cpp b/clang-tidy/utils/ExprMutationAnalyzer.cpp index adf4095d0..f979e97a0 100644 --- a/clang-tidy/utils/ExprMutationAnalyzer.cpp +++ b/clang-tidy/utils/ExprMutationAnalyzer.cpp @@ -145,11 +145,16 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { hasUnaryOperand(equalsNode(Exp))); // Invoking non-const member function. + // A member function is assumed to be non-const when it is unresolved. const auto NonConstMethod = cxxMethodDecl(unless(isConst())); const auto AsNonConstThis = expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))), cxxOperatorCallExpr(callee(NonConstMethod), - hasArgument(0, equalsNode(Exp))))); + hasArgument(0, equalsNode(Exp))), + callExpr(callee(expr(anyOf( + unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))), + cxxDependentScopeMemberExpr( + hasObjectExpression(equalsNode(Exp))))))))); // Taking address of 'Exp'. // We're assuming 'Exp' is mutated as soon as its address is taken, though in @@ -165,10 +170,16 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp))); // Used as non-const-ref argument when calling a function. + // An argument is assumed to be non-const-ref when the function is unresolved. const auto NonConstRefParam = forEachArgumentWithParam( equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType()))); - const auto AsNonConstRefArg = - anyOf(callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam)); + const auto AsNonConstRefArg = anyOf( + callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam), + callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(), + cxxDependentScopeMemberExpr(), + hasType(templateTypeParmType())))), + hasAnyArgument(equalsNode(Exp))), + cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp)))); // Captured by a lambda by reference. // If we're initializing a capture with 'Exp' directly then we're initializing @@ -195,9 +206,12 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) { // Check whether any member of 'Exp' is mutated. - const auto MemberExprs = match( - findAll(memberExpr(hasObjectExpression(equalsNode(Exp))).bind("expr")), - Stm, Context); + const auto MemberExprs = + match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))), + cxxDependentScopeMemberExpr( + hasObjectExpression(equalsNode(Exp))))) + .bind("expr")), + Stm, Context); return findExprMutation(MemberExprs); } diff --git a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp index 9d0728bf9..71511d635 100644 --- a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp +++ b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp @@ -114,6 +114,29 @@ TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) { EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); } +TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) { + auto AST = tooling::buildASTFromCodeWithArgs( + "struct X { template void mf(); };" + "template void f() { X x; x.mf(); }", + {"-fno-delayed-template-parsing"}); + auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); + + AST = tooling::buildASTFromCodeWithArgs( + "template void f() { T x; x.mf(); }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); + + AST = tooling::buildASTFromCodeWithArgs( + "template struct X;" + "template void f() { X x; x.mf(); }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); +} + TEST(ExprMutationAnalyzerTest, ConstMemberFunc) { const auto AST = tooling::buildASTFromCode( "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }"); @@ -292,6 +315,59 @@ TEST(ExprMutationAnalyzerTest, Forward) { ElementsAre("std::forward(x)")); } +TEST(ExprMutationAnalyzerTest, CallUnresolved) { + auto AST = tooling::buildASTFromCodeWithArgs( + "template void f() { T x; g(x); }", + {"-fno-delayed-template-parsing"}); + auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); + + AST = tooling::buildASTFromCodeWithArgs( + "template void f() { char x[N]; g(x); }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); + + AST = tooling::buildASTFromCodeWithArgs( + "template void f(T t) { int x; g(t, x); }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)")); + + AST = tooling::buildASTFromCodeWithArgs( + "template void f(T t) { int x; t.mf(x); }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)")); + + AST = tooling::buildASTFromCodeWithArgs( + "template struct S;" + "template void f() { S s; int x; s.mf(x); }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)")); + + AST = tooling::buildASTFromCodeWithArgs( + "struct S { template void mf(); };" + "template void f(S s) { int x; s.mf(x); }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)")); + + AST = tooling::buildASTFromCodeWithArgs("template " + "void g(F f) { int x; f(x); } ", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)")); + + AST = tooling::buildASTFromCodeWithArgs( + "template void f() { int x; (void)T(x); }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)")); +} + TEST(ExprMutationAnalyzerTest, ReturnAsValue) { const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }"); const auto Results = @@ -347,6 +423,22 @@ TEST(ExprMutationAnalyzerTest, ArrayToPointerDecay) { EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); } +TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) { + const auto AST = tooling::buildASTFromCodeWithArgs( + "template struct S { static constexpr int v = 8; };" + "template <> struct S { static constexpr int v = 4; };" + "void g(char*);" + "template void f() { char x[S::v]; g(x); }" + "template <> void f() { char y[S::v]; g(y); }", + {"-fno-delayed-template-parsing"}); + const auto ResultsX = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)")); + const auto ResultsY = + match(withEnclosingCompound(declRefTo("y")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y")); +} + TEST(ExprMutationAnalyzerTest, FollowRefModified) { const auto AST = tooling::buildASTFromCode( "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; " @@ -398,21 +490,47 @@ TEST(ExprMutationAnalyzerTest, ArrayElementNotModified) { } TEST(ExprMutationAnalyzerTest, NestedMemberModified) { - const auto AST = tooling::buildASTFromCode( + auto AST = tooling::buildASTFromCode( "void f() { struct A { int vi; }; struct B { A va; }; " "struct C { B vb; }; C x; x.vb.va.vi = 10; }"); - const auto Results = + auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.vb.va.vi = 10")); + + AST = tooling::buildASTFromCodeWithArgs( + "template void f() { T x; x.y.z = 10; }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10")); + + AST = tooling::buildASTFromCodeWithArgs( + "template struct S;" + "template void f() { S x; x.y.z = 10; }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10")); } TEST(ExprMutationAnalyzerTest, NestedMemberNotModified) { - const auto AST = tooling::buildASTFromCode( + auto AST = tooling::buildASTFromCode( "void f() { struct A { int vi; }; struct B { A va; }; " "struct C { B vb; }; C x; x.vb.va.vi; }"); - const auto Results = + auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCodeWithArgs( + "template void f() { T x; x.y.z; }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCodeWithArgs( + "template struct S;" + "template void f() { S x; x.y.z; }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); } TEST(ExprMutationAnalyzerTest, CastToValue) { From b8ef92e26cd77108f4b8f2bc2e40a8f98ac03ae5 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 11 Sep 2018 10:31:38 +0000 Subject: [PATCH 170/686] [clangd] NFC: Use uint32_t for FuzzyFindRequest limits Reviewed By: ioeric Differential Revision: https://reviews.llvm.org/D51860 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341921 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Index.cpp | 18 +++++++----------- clangd/index/Index.h | 4 +++- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index c0c821a8e..5a8f0d168 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -177,29 +177,25 @@ std::shared_ptr SwapIndex::snapshot() const { bool fromJSON(const llvm::json::Value &Parameters, FuzzyFindRequest &Request) { json::ObjectMapper O(Parameters); - llvm::Optional MaxCandidateCount; + int64_t MaxCandidateCount; bool OK = O && O.map("Query", Request.Query) && O.map("Scopes", Request.Scopes) && + O.map("MaxCandidateCount", MaxCandidateCount) && O.map("RestrictForCodeCompletion", Request.RestrictForCodeCompletion) && - O.map("ProximityPaths", Request.ProximityPaths) && - O.map("MaxCandidateCount", MaxCandidateCount); - if (MaxCandidateCount) - Request.MaxCandidateCount = MaxCandidateCount.getValue(); + O.map("ProximityPaths", Request.ProximityPaths); + if (OK && MaxCandidateCount <= std::numeric_limits::max()) + Request.MaxCandidateCount = MaxCandidateCount; return OK; } llvm::json::Value toJSON(const FuzzyFindRequest &Request) { - auto Result = json::Object{ + return json::Object{ {"Query", Request.Query}, {"Scopes", json::Array{Request.Scopes}}, + {"MaxCandidateCount", Request.MaxCandidateCount}, {"RestrictForCodeCompletion", Request.RestrictForCodeCompletion}, {"ProximityPaths", json::Array{Request.ProximityPaths}}, }; - // A huge limit means no limit, leave it out. - if (Request.MaxCandidateCount <= std::numeric_limits::max()) - Result["MaxCandidateCount"] = - static_cast(Request.MaxCandidateCount); - return std::move(Result); } bool SwapIndex::fuzzyFind(const FuzzyFindRequest &R, diff --git a/clangd/index/Index.h b/clangd/index/Index.h index b531d1e29..c14ed9a3a 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -437,7 +437,9 @@ struct FuzzyFindRequest { std::vector Scopes; /// \brief The number of top candidates to return. The index may choose to /// return more than this, e.g. if it doesn't know which candidates are best. - size_t MaxCandidateCount = std::numeric_limits::max(); + // FIXME: Use llvm::Optional; semantically, the absence of MaxCandidateCount + // is equivalent to setting this field to default value as below. + uint32_t MaxCandidateCount = std::numeric_limits::max(); /// If set to true, only symbols for completion support will be considered. bool RestrictForCodeCompletion = false; /// Contextually relevant files (e.g. the file we're code-completing in). From ed530d41979f479931f40826deb982a19440a441 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Tue, 11 Sep 2018 10:37:08 +0000 Subject: [PATCH 171/686] [clang-tidy] Add a missing comma after "flags" git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341925 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/AbseilMatcher.h | 19 +++++-------------- .../Inputs/absl/flags/internal-file.h | 1 + .../Inputs/absl/strings/internal-file.h | 2 -- .../abseil-no-internal-dependencies.cpp | 1 + 4 files changed, 7 insertions(+), 16 deletions(-) create mode 100644 test/clang-tidy/Inputs/absl/flags/internal-file.h diff --git a/clang-tidy/abseil/AbseilMatcher.h b/clang-tidy/abseil/AbseilMatcher.h index 0426790a2..e56720499 100644 --- a/clang-tidy/abseil/AbseilMatcher.h +++ b/clang-tidy/abseil/AbseilMatcher.h @@ -43,24 +43,15 @@ AST_POLYMORPHIC_MATCHER( // Determine whether filepath contains "absl/[absl-library]" substring, where // [absl-library] is AbseilLibraries list entry. StringRef Path = FileEntry->getName(); - const static llvm::SmallString<5> AbslPrefix("absl/"); + static constexpr llvm::StringLiteral AbslPrefix("absl/"); size_t PrefixPosition = Path.find(AbslPrefix); if (PrefixPosition == StringRef::npos) return false; Path = Path.drop_front(PrefixPosition + AbslPrefix.size()); - static const char *AbseilLibraries[] = {"algorithm", - "base", - "container", - "debugging", - "flags" - "memory", - "meta", - "numeric", - "strings", - "synchronization", - "time", - "types", - "utility"}; + static const char *AbseilLibraries[] = { + "algorithm", "base", "container", "debugging", "flags", + "memory", "meta", "numeric", "strings", "synchronization", + "time", "types", "utility"}; return std::any_of( std::begin(AbseilLibraries), std::end(AbseilLibraries), [&](const char *Library) { return Path.startswith(Library); }); diff --git a/test/clang-tidy/Inputs/absl/flags/internal-file.h b/test/clang-tidy/Inputs/absl/flags/internal-file.h new file mode 100644 index 000000000..c81cf9eee --- /dev/null +++ b/test/clang-tidy/Inputs/absl/flags/internal-file.h @@ -0,0 +1 @@ +#define USE_INTERNAL(x) absl::strings_internal::Internal##x() diff --git a/test/clang-tidy/Inputs/absl/strings/internal-file.h b/test/clang-tidy/Inputs/absl/strings/internal-file.h index ac52109c8..6014278e2 100644 --- a/test/clang-tidy/Inputs/absl/strings/internal-file.h +++ b/test/clang-tidy/Inputs/absl/strings/internal-file.h @@ -31,5 +31,3 @@ class FriendUsageInternal { namespace absl { void OpeningNamespaceInternally() { strings_internal::InternalFunction(); } } // namespace absl - -#define USE_INTERNAL(x) absl::strings_internal::Internal##x() diff --git a/test/clang-tidy/abseil-no-internal-dependencies.cpp b/test/clang-tidy/abseil-no-internal-dependencies.cpp index d8aea7ee3..272d0060b 100644 --- a/test/clang-tidy/abseil-no-internal-dependencies.cpp +++ b/test/clang-tidy/abseil-no-internal-dependencies.cpp @@ -2,6 +2,7 @@ // RUN: clang-tidy -checks='-*, abseil-no-internal-dependencies' -header-filter='.*' %s -- -I %S/Inputs 2>&1 | FileCheck %s #include "absl/strings/internal-file.h" +#include "absl/flags/internal-file.h" // CHECK-NOT: warning: #include "absl/external-file.h" From cb074083de05002c2c2664099fd24b1dd25d315d Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Tue, 11 Sep 2018 12:19:45 +0000 Subject: [PATCH 172/686] [clang-tidy] Insert absl::StrAppend when replacing StrCat. There might be no using decl for StrAppend around, inserting the qualified name is less likely to break things. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341929 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/StrCatAppendCheck.cpp | 2 +- test/clang-tidy/abseil-str-cat-append.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang-tidy/abseil/StrCatAppendCheck.cpp b/clang-tidy/abseil/StrCatAppendCheck.cpp index 25b9d17e8..490723715 100644 --- a/clang-tidy/abseil/StrCatAppendCheck.cpp +++ b/clang-tidy/abseil/StrCatAppendCheck.cpp @@ -93,7 +93,7 @@ void StrCatAppendCheck::check(const MatchFinder::MatchResult &Result) { << FixItHint::CreateReplacement( CharSourceRange::getTokenRange(Op->getBeginLoc(), Call->getCallee()->getEndLoc()), - "StrAppend") + "absl::StrAppend") << FixItHint::CreateInsertion(Call->getArg(0)->getBeginLoc(), "&"); } diff --git a/test/clang-tidy/abseil-str-cat-append.cpp b/test/clang-tidy/abseil-str-cat-append.cpp index 9a1273388..5ecb284df 100644 --- a/test/clang-tidy/abseil-str-cat-append.cpp +++ b/test/clang-tidy/abseil-str-cat-append.cpp @@ -97,7 +97,7 @@ void Bar() { // CHECK-MESSAGES: [[@LINE-1]]:3: warning: call to 'absl::StrCat' has no effect A = StrCat(A, B); // CHECK-MESSAGES: [[@LINE-1]]:3: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty -// CHECK-FIXES: {{^}} StrAppend(&A, B); +// CHECK-FIXES: {{^}} absl::StrAppend(&A, B); B = StrCat(A, B); #define M(X) X = StrCat(X, A) @@ -117,13 +117,13 @@ void OutsideAbsl() { std::string A, B; A = absl::StrCat(A, B); // CHECK-MESSAGES: [[@LINE-1]]:3: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty -// CHECK-FIXES: {{^}} StrAppend(&A, B); +// CHECK-FIXES: {{^}} absl::StrAppend(&A, B); } -void OutisdeUsingAbsl() { +void OutsideUsingAbsl() { std::string A, B; using absl::StrCat; A = StrCat(A, B); // CHECK-MESSAGES: [[@LINE-1]]:3: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty -// CHECK-FIXES: {{^}} StrAppend(&A, B); +// CHECK-FIXES: {{^}} absl::StrAppend(&A, B); } From 930e8b580da9524b1605e430c3787e18cda58fab Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Tue, 11 Sep 2018 13:01:49 +0000 Subject: [PATCH 173/686] [NFC][clangd] fix warning for extra semicolon git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341933 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/XRefsTests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittests/clangd/XRefsTests.cpp b/unittests/clangd/XRefsTests.cpp index b24c1fec3..be4acf097 100644 --- a/unittests/clangd/XRefsTests.cpp +++ b/unittests/clangd/XRefsTests.cpp @@ -1210,7 +1210,7 @@ TEST(FindReferences, NeedsIndex) { TU.Code = ("\n\n" + Main.code()).str(); EXPECT_THAT(findReferences(AST, Main.point(), TU.index().get()), ElementsAre(RangeIs(Main.range()))); -}; +} TEST(FindReferences, NoQueryForLocalSymbols) { struct RecordingIndex : public MemIndex { @@ -1244,7 +1244,7 @@ TEST(FindReferences, NoQueryForLocalSymbols) { else EXPECT_EQ(Rec.RefIDs, llvm::None) << T.AnnotatedCode; } -}; +} } // namespace } // namespace clangd From fb402ea5e632ea6c48e145cce89537c64420442e Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Tue, 11 Sep 2018 13:42:15 +0000 Subject: [PATCH 174/686] Remove unnecessary semicolon to silence -Wpedantic warning. NFCI. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341938 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/RIFF.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/RIFF.cpp b/clangd/RIFF.cpp index 28c5ae6b9..7ab365a82 100644 --- a/clangd/RIFF.cpp +++ b/clangd/RIFF.cpp @@ -37,7 +37,7 @@ Expected readChunk(StringRef &Stream) { Stream = Stream.drop_front(); } return std::move(C); -}; +} raw_ostream &operator<<(raw_ostream &OS, const Chunk &C) { OS.write(C.ID.data(), C.ID.size()); From 8429065ea7d5ced535f237b51da5ff5977d0aa5b Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Tue, 11 Sep 2018 15:12:10 +0000 Subject: [PATCH 175/686] [clangd] Add unittests for D51917 Reviewers: ilya-biryukov Reviewed By: ilya-biryukov Subscribers: ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D51924 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341950 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/CodeCompleteTests.cpp | 39 ++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index c1010038d..248dab8ca 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1967,6 +1967,45 @@ TEST(SignatureHelpTest, InsideArgument) { } } +TEST(SignatureHelpTest, ConstructorInitializeFields) { + { + const auto Results = signatures(R"cpp( + struct A { + A(int); + }; + struct B { + B() : a_elem(^) {} + A a_elem; + }; + )cpp"); + EXPECT_THAT(Results.signatures, UnorderedElementsAre( + Sig("A(int)", {"int"}), + Sig("A(A &&)", {"A &&"}), + Sig("A(const A &)", {"const A &"}) + )); + } + { + const auto Results = signatures(R"cpp( + struct A { + A(int); + }; + struct C { + C(int); + C(A); + }; + struct B { + B() : c_elem(A(1^)) {} + C c_elem; + }; + )cpp"); + EXPECT_THAT(Results.signatures, UnorderedElementsAre( + Sig("A(int)", {"int"}), + Sig("A(A &&)", {"A &&"}), + Sig("A(const A &)", {"const A &"}) + )); + } +} + } // namespace } // namespace clangd } // namespace clang From dff75f4977889614ff85f7eb1f7d1b492f00ebd0 Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Tue, 11 Sep 2018 15:56:55 +0000 Subject: [PATCH 176/686] Reland "Implement a (simple) Markdown generator" Relanding with fixes to tests for the failing bots. Differential Revision: https://reviews.llvm.org/D43424 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341955 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/CMakeLists.txt | 1 + clang-doc/Generators.cpp | 3 + clang-doc/Generators.h | 2 +- clang-doc/MDGenerator.cpp | 312 ++++++++++++++++++++++++++++++++ clang-doc/Representation.h | 15 +- clang-doc/YAMLGenerator.cpp | 10 +- clang-doc/gen_tests.py | 33 +++- clang-doc/tool/ClangDocMain.cpp | 43 +++-- test/clang-doc/md-comment.cpp | 48 +++++ test/clang-doc/md-linkage.cpp | 134 ++++++++++++++ test/clang-doc/md-module.cpp | 24 +++ test/clang-doc/md-namespace.cpp | 46 +++++ test/clang-doc/md-record.cpp | 97 ++++++++++ 13 files changed, 730 insertions(+), 38 deletions(-) create mode 100644 clang-doc/MDGenerator.cpp create mode 100644 test/clang-doc/md-comment.cpp create mode 100644 test/clang-doc/md-linkage.cpp create mode 100644 test/clang-doc/md-module.cpp create mode 100644 test/clang-doc/md-namespace.cpp create mode 100644 test/clang-doc/md-record.cpp diff --git a/clang-doc/CMakeLists.txt b/clang-doc/CMakeLists.txt index eaebf616f..1d70bb08f 100644 --- a/clang-doc/CMakeLists.txt +++ b/clang-doc/CMakeLists.txt @@ -10,6 +10,7 @@ add_clang_library(clangDoc ClangDoc.cpp Generators.cpp Mapper.cpp + MDGenerator.cpp Representation.cpp Serialize.cpp YAMLGenerator.cpp diff --git a/clang-doc/Generators.cpp b/clang-doc/Generators.cpp index fe01d6109..5a0d0c5c1 100644 --- a/clang-doc/Generators.cpp +++ b/clang-doc/Generators.cpp @@ -29,8 +29,11 @@ findGeneratorByName(llvm::StringRef Format) { // This anchor is used to force the linker to link in the generated object file // and thus register the generators. extern volatile int YAMLGeneratorAnchorSource; +extern volatile int MDGeneratorAnchorSource; static int LLVM_ATTRIBUTE_UNUSED YAMLGeneratorAnchorDest = YAMLGeneratorAnchorSource; +static int LLVM_ATTRIBUTE_UNUSED MDGeneratorAnchorDest = + MDGeneratorAnchorSource; } // namespace doc } // namespace clang diff --git a/clang-doc/Generators.h b/clang-doc/Generators.h index 9106d2cff..90a81e82d 100644 --- a/clang-doc/Generators.h +++ b/clang-doc/Generators.h @@ -27,7 +27,7 @@ class Generator { virtual ~Generator() = default; // Write out the decl info in the specified format. - virtual bool generateDocForInfo(Info *I, llvm::raw_ostream &OS) = 0; + virtual llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) = 0; }; typedef llvm::Registry GeneratorRegistry; diff --git a/clang-doc/MDGenerator.cpp b/clang-doc/MDGenerator.cpp new file mode 100644 index 000000000..f6c173c08 --- /dev/null +++ b/clang-doc/MDGenerator.cpp @@ -0,0 +1,312 @@ +//===-- MDGenerator.cpp - Markdown Generator --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Generators.h" +#include "Representation.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include + +using namespace llvm; + +namespace clang { +namespace doc { + +// Enum conversion + +std::string getAccess(AccessSpecifier AS) { + switch (AS) { + case AccessSpecifier::AS_public: + return "public"; + case AccessSpecifier::AS_protected: + return "protected"; + case AccessSpecifier::AS_private: + return "private"; + case AccessSpecifier::AS_none: + return {}; + } +} + +std::string getTagType(TagTypeKind AS) { + switch (AS) { + case TagTypeKind::TTK_Class: + return "class"; + case TagTypeKind::TTK_Union: + return "union"; + case TagTypeKind::TTK_Interface: + return "interface"; + case TagTypeKind::TTK_Struct: + return "struct"; + case TagTypeKind::TTK_Enum: + return "enum"; + } +} + +// Markdown generation + +std::string genItalic(const Twine &Text) { return "*" + Text.str() + "*"; } + +std::string genEmphasis(const Twine &Text) { return "**" + Text.str() + "**"; } + +std::string genLink(const Twine &Text, const Twine &Link) { + return "[" + Text.str() + "](" + Link.str() + ")"; +} + +std::string genReferenceList(const llvm::SmallVectorImpl &Refs) { + std::string Buffer; + llvm::raw_string_ostream Stream(Buffer); + bool First = true; + for (const auto &R : Refs) { + if (!First) + Stream << ", "; + Stream << R.Name; + First = false; + } + return Stream.str(); +} + +void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n"; } + +void writeNewLine(raw_ostream &OS) { OS << "\n"; } + +void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) { + OS << std::string(Num, '#') + " " + Text << "\n"; +} + +void writeFileDefinition(const Location &L, raw_ostream &OS) { + OS << genItalic("Defined at line " + std::to_string(L.LineNumber) + " of " + + L.Filename) + << "\n"; +} + +void writeDescription(const CommentInfo &I, raw_ostream &OS) { + if (I.Kind == "FullComment") { + for (const auto &Child : I.Children) + writeDescription(*Child, OS); + } else if (I.Kind == "ParagraphComment") { + for (const auto &Child : I.Children) + writeDescription(*Child, OS); + writeNewLine(OS); + } else if (I.Kind == "BlockCommandComment") { + OS << genEmphasis(I.Name); + for (const auto &Child : I.Children) + writeDescription(*Child, OS); + } else if (I.Kind == "InlineCommandComment") { + OS << genEmphasis(I.Name) << " " << I.Text; + } else if (I.Kind == "ParamCommandComment") { + std::string Direction = I.Explicit ? (" " + I.Direction).str() : ""; + OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n"; + } else if (I.Kind == "TParamCommandComment") { + std::string Direction = I.Explicit ? (" " + I.Direction).str() : ""; + OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n"; + } else if (I.Kind == "VerbatimBlockComment") { + for (const auto &Child : I.Children) + writeDescription(*Child, OS); + } else if (I.Kind == "VerbatimBlockLineComment") { + OS << I.Text; + writeNewLine(OS); + } else if (I.Kind == "VerbatimLineComment") { + OS << I.Text; + writeNewLine(OS); + } else if (I.Kind == "HTMLStartTagComment") { + if (I.AttrKeys.size() != I.AttrValues.size()) + return; + std::string Buffer; + llvm::raw_string_ostream Attrs(Buffer); + for (unsigned Idx = 0; Idx < I.AttrKeys.size(); ++Idx) + Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\""; + + std::string CloseTag = I.SelfClosing ? "/>" : ">"; + writeLine("<" + I.Name + Attrs.str() + CloseTag, OS); + } else if (I.Kind == "HTMLEndTagComment") { + writeLine("", OS); + } else if (I.Kind == "TextComment") { + OS << I.Text; + } else { + OS << "Unknown comment kind: " << I.Kind << ".\n"; + } +} + +void genMarkdown(const EnumInfo &I, llvm::raw_ostream &OS) { + if (I.Scoped) + writeLine("| enum class " + I.Name + " |", OS); + else + writeLine("| enum " + I.Name + " |", OS); + writeLine("--", OS); + + std::string Buffer; + llvm::raw_string_ostream Members(Buffer); + if (!I.Members.empty()) + for (const auto &N : I.Members) + Members << "| " << N << " |\n"; + writeLine(Members.str(), OS); + if (I.DefLoc) + writeFileDefinition(I.DefLoc.getValue(), OS); + + for (const auto &C : I.Description) + writeDescription(C, OS); +} + +void genMarkdown(const FunctionInfo &I, llvm::raw_ostream &OS) { + std::string Buffer; + llvm::raw_string_ostream Stream(Buffer); + bool First = true; + for (const auto &N : I.Params) { + if (!First) + Stream << ", "; + Stream << N.Type.Name + " " + N.Name; + First = false; + } + std::string Access = getAccess(I.Access); + if (Access != "") + writeHeader(genItalic(Access) + " " + I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")", 3, OS); + else + writeHeader(I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")", 3, OS); + if (I.DefLoc) + writeFileDefinition(I.DefLoc.getValue(), OS); + + for (const auto &C : I.Description) + writeDescription(C, OS); +} + +void genMarkdown(const NamespaceInfo &I, llvm::raw_ostream &OS) { + if (I.Name == "") + writeHeader("Global Namespace", 1, OS); + else + writeHeader("namespace " + I.Name, 1, OS); + writeNewLine(OS); + + if (!I.Description.empty()) { + for (const auto &C : I.Description) + writeDescription(C, OS); + writeNewLine(OS); + } + + if (!I.ChildNamespaces.empty()) { + writeHeader("Namespaces", 2, OS); + for (const auto &R : I.ChildNamespaces) + writeLine(R.Name, OS); + writeNewLine(OS); + } + if (!I.ChildRecords.empty()) { + writeHeader("Records", 2, OS); + for (const auto &R : I.ChildRecords) + writeLine(R.Name, OS); + writeNewLine(OS); + } + if (!I.ChildFunctions.empty()) { + writeHeader("Functions", 2, OS); + for (const auto &F : I.ChildFunctions) + genMarkdown(F, OS); + writeNewLine(OS); + } + if (!I.ChildEnums.empty()) { + writeHeader("Enums", 2, OS); + for (const auto &E : I.ChildEnums) + genMarkdown(E, OS); + writeNewLine(OS); + } +} + +void genMarkdown(const RecordInfo &I, llvm::raw_ostream &OS) { + writeHeader(getTagType(I.TagType) + " " + I.Name, 1, OS); + if (I.DefLoc) + writeFileDefinition(I.DefLoc.getValue(), OS); + + if (!I.Description.empty()) { + for (const auto &C : I.Description) + writeDescription(C, OS); + writeNewLine(OS); + } + + std::string Parents = genReferenceList(I.Parents); + std::string VParents = genReferenceList(I.VirtualParents); + if (!Parents.empty() || !VParents.empty()) { + if (Parents.empty()) + writeLine("Inherits from " + VParents, OS); + else if (VParents.empty()) + writeLine("Inherits from " + Parents, OS); + else + writeLine("Inherits from " + Parents + ", " + VParents, OS); + writeNewLine(OS); + } + + if (!I.Members.empty()) { + writeHeader("Members", 2, OS); + for (const auto Member : I.Members) { + std::string Access = getAccess(Member.Access); + if (Access != "") + writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS); + else + writeLine(Member.Type.Name + " " + Member.Name, OS); + } + writeNewLine(OS); + } + + if (!I.ChildRecords.empty()) { + writeHeader("Records", 2, OS); + for (const auto &R : I.ChildRecords) + writeLine(R.Name, OS); + writeNewLine(OS); + } + if (!I.ChildFunctions.empty()) { + writeHeader("Functions", 2, OS); + for (const auto &F : I.ChildFunctions) + genMarkdown(F, OS); + writeNewLine(OS); + } + if (!I.ChildEnums.empty()) { + writeHeader("Enums", 2, OS); + for (const auto &E : I.ChildEnums) + genMarkdown(E, OS); + writeNewLine(OS); + } +} + +/// Generator for Markdown documentation. +class MDGenerator : public Generator { +public: + static const char *Format; + + llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; +}; + +const char *MDGenerator::Format = "md"; + +llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { + switch (I->IT) { + case InfoType::IT_namespace: + genMarkdown(*static_cast(I), OS); + break; + case InfoType::IT_record: + genMarkdown(*static_cast(I), OS); + break; + case InfoType::IT_enum: + genMarkdown(*static_cast(I), OS); + break; + case InfoType::IT_function: + genMarkdown(*static_cast(I), OS); + break; + case InfoType::IT_default: + return llvm::make_error("Unexpected info type.\n", + llvm::inconvertibleErrorCode()); + } + return llvm::Error::success(); +} + +static GeneratorRegistry::Add MD(MDGenerator::Format, + "Generator for MD output."); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the generator. +volatile int MDGeneratorAnchorSource = 0; + +} // namespace doc +} // namespace clang diff --git a/clang-doc/Representation.h b/clang-doc/Representation.h index 7a127c08d..48f8f3d83 100644 --- a/clang-doc/Representation.h +++ b/clang-doc/Representation.h @@ -48,13 +48,14 @@ struct CommentInfo { CommentInfo(CommentInfo &Other) = delete; CommentInfo(CommentInfo &&Other) = default; - SmallString<16> Kind; // Kind of comment (TextComment, InlineCommandComment, - // HTMLStartTagComment, HTMLEndTagComment, - // BlockCommandComment, ParamCommandComment, - // TParamCommandComment, VerbatimBlockComment, - // VerbatimBlockLineComment, VerbatimLineComment). - SmallString<64> Text; // Text of the comment. - SmallString<16> Name; // Name of the comment (for Verbatim and HTML). + SmallString<16> + Kind; // Kind of comment (FullComment, ParagraphComment, TextComment, + // InlineCommandComment, HTMLStartTagComment, HTMLEndTagComment, + // BlockCommandComment, ParamCommandComment, + // TParamCommandComment, VerbatimBlockComment, + // VerbatimBlockLineComment, VerbatimLineComment). + SmallString<64> Text; // Text of the comment. + SmallString<16> Name; // Name of the comment (for Verbatim and HTML). SmallString<8> Direction; // Parameter direction (for (T)ParamCommand). SmallString<16> ParamName; // Parameter name (for (T)ParamCommand). SmallString<16> CloseName; // Closing tag name (for VerbatimBlock). diff --git a/clang-doc/YAMLGenerator.cpp b/clang-doc/YAMLGenerator.cpp index 58c1e1f36..e09390198 100644 --- a/clang-doc/YAMLGenerator.cpp +++ b/clang-doc/YAMLGenerator.cpp @@ -242,12 +242,12 @@ class YAMLGenerator : public Generator { public: static const char *Format; - bool generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; + llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; }; const char *YAMLGenerator::Format = "yaml"; -bool YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { +llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { llvm::yaml::Output InfoYAML(OS); switch (I->IT) { case InfoType::IT_namespace: @@ -263,10 +263,10 @@ bool YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { InfoYAML << *static_cast(I); break; case InfoType::IT_default: - llvm::errs() << "Unexpected info type in index.\n"; - return true; + return llvm::make_error("Unexpected info type.\n", + llvm::inconvertibleErrorCode()); } - return false; + return llvm::Error::success(); } static GeneratorRegistry::Add YAML(YAMLGenerator::Format, diff --git a/clang-doc/gen_tests.py b/clang-doc/gen_tests.py index ccdb069c4..9259684d5 100644 --- a/clang-doc/gen_tests.py +++ b/clang-doc/gen_tests.py @@ -18,16 +18,19 @@ To generate all current tests: - Generate mapper tests: - python gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix mapper + python gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix mapper -use-check-next - Generate reducer tests: - python gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix bc + python gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix bc -use-check-next - Generate yaml tests: - python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix yaml + python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--extra-arg=-fmodules-ts' -prefix yaml -use-check-next - Generate public decl tests: - python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix public + python gen_tests.py -flag='--format=yaml' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix public -use-check-next + +- Generate Markdown tests: + python gen_tests.py -flag='--format=md' -flag='--doxygen' -flag='--public' -flag='--extra-arg=-fmodules-ts' -prefix md This script was written on/for Linux, and has not been tested on any other platform and so it may not work. @@ -95,7 +98,8 @@ def get_test_case_code(test_case_path, flags): return code -def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer): +def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer, + check_next=True): output = '' run_cmd = '' if '--dump-mapper' in flags or '--dump-intermediate' in flags: @@ -119,8 +123,14 @@ def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer): output = re.sub(YAML_USR_REGEX, YAML_USR, output) output = re.sub(BITCODE_USR_REGEX, BITCODE_USR, output) output = CHECK.format(checkname) + output.rstrip() - output = run_cmd + output.replace('\n', - '\n' + CHECK_NEXT.format(checkname)) + + if check_next: + check_comment = CHECK_NEXT.format(checkname) + else: + check_comment = CHECK.format(checkname) + + output = output.replace('\n', '\n' + check_comment) + output = run_cmd + output.replace('%s\n' % check_comment, "") return output + '\n' @@ -151,6 +161,12 @@ def main(): metavar="PATH", default='llvm-bcanalyzer', help='path to llvm-bcanalyzer binary') + parser.add_argument( + '-use-check-next', + dest='check_next', + default=False, + action='store_true', + help='Whether or not to use CHECK-NEXT in the resulting tests.') args = parser.parse_args() flags = ' '.join(args.flags) @@ -188,7 +204,8 @@ def main(): if len(usr) < 2: continue all_output += get_output(root, out_file, out_dir, args.flags, - num_outputs, args.bcanalyzer) + num_outputs, args.bcanalyzer, + args.check_next) num_outputs += 1 # Add test case code to test diff --git a/clang-doc/tool/ClangDocMain.cpp b/clang-doc/tool/ClangDocMain.cpp index 6e4a92d7b..6b50f6ca8 100644 --- a/clang-doc/tool/ClangDocMain.cpp +++ b/clang-doc/tool/ClangDocMain.cpp @@ -34,6 +34,7 @@ #include "clang/Tooling/StandaloneExecution.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/APFloat.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -69,13 +70,18 @@ static llvm::cl::opt llvm::cl::init(false), llvm::cl::cat(ClangDocCategory)); enum OutputFormatTy { + md, yaml, }; -static llvm::cl::opt FormatEnum( - "format", llvm::cl::desc("Format for outputted docs."), - llvm::cl::values(clEnumVal(yaml, "Documentation in YAML format.")), - llvm::cl::init(yaml), llvm::cl::cat(ClangDocCategory)); +static llvm::cl::opt + FormatEnum("format", llvm::cl::desc("Format for outputted docs."), + llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml", + "Documentation in YAML format."), + clEnumValN(OutputFormatTy::md, "md", + "Documentation in MD format.")), + llvm::cl::init(OutputFormatTy::yaml), + llvm::cl::cat(ClangDocCategory)); static llvm::cl::opt DoxygenOnly( "doxygen", @@ -155,10 +161,12 @@ getInfoOutputFile(StringRef Root, return Path; } -std::string getFormatString(OutputFormatTy Ty) { - switch (Ty) { - case yaml: +std::string getFormatString() { + switch (FormatEnum) { + case OutputFormatTy::yaml: return "yaml"; + case OutputFormatTy::md: + return "md"; } llvm_unreachable("Unknown OutputFormatTy"); } @@ -191,14 +199,6 @@ int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); std::error_code OK; - // Fail early if an invalid format was provided. - std::string Format = getFormatString(FormatEnum); - auto G = doc::findGeneratorByName(Format); - if (!G) { - llvm::errs() << toString(G.takeError()) << "\n"; - return 1; - } - auto Exec = clang::tooling::createExecutorFromCommandLineArgs( argc, argv, ClangDocCategory); @@ -207,6 +207,15 @@ int main(int argc, const char **argv) { return 1; } + // Fail early if an invalid format was provided. + std::string Format = getFormatString(); + llvm::outs() << "Emiting docs in " << Format << " format.\n"; + auto G = doc::findGeneratorByName(Format); + if (!G) { + llvm::errs() << toString(G.takeError()) << "\n"; + return 1; + } + ArgumentsAdjuster ArgAdjuster; if (!DoxygenOnly) ArgAdjuster = combineAdjusters( @@ -277,8 +286,8 @@ int main(int argc, const char **argv) { continue; } - if (G->get()->generateDocForInfo(I, InfoOS)) - llvm::errs() << "Unable to generate docs for info.\n"; + if (auto Err = G->get()->generateDocForInfo(I, InfoOS)) + llvm::errs() << toString(std::move(Err)) << "\n"; } return 0; diff --git a/test/clang-doc/md-comment.cpp b/test/clang-doc/md-comment.cpp new file mode 100644 index 000000000..ae14eaac2 --- /dev/null +++ b/test/clang-doc/md-comment.cpp @@ -0,0 +1,48 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +/// \brief Brief description. +/// +/// Extended description that +/// continues onto the next line. +/// +///
    +///
  • Testing. +///
+/// +/// \verbatim +/// The description continues. +/// \endverbatim +/// -- +/// \param [out] I is a parameter. +/// \param J is a parameter. +/// \return void +void F(int I, int J); + +/// Bonus comment on definition +void F(int I, int J) {} + +// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: # Global Namespace +// CHECK-0: ## Functions +// CHECK-0: ### void F(int I, int J) +// CHECK-0: *Defined at line 28 of test* +// CHECK-0: **brief** Brief description. +// CHECK-0: Extended description that continues onto the next line. +// CHECK-0:
    +// CHECK-0:
  • +// CHECK-0: Testing.
+// CHECK-0: The description continues. +// CHECK-0: -- +// CHECK-0: **I** [out] +// CHECK-0: **J** +// CHECK-0: **return** void +// CHECK-0: Bonus comment on definition diff --git a/test/clang-doc/md-linkage.cpp b/test/clang-doc/md-linkage.cpp new file mode 100644 index 000000000..942ff3533 --- /dev/null +++ b/test/clang-doc/md-linkage.cpp @@ -0,0 +1,134 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void function(int x); + +inline int inlinedFunction(int x); + +int functionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +inline int inlinedFunctionWithInnerClass(int x) { + class InnerClass { //VisibleNoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +class Class { +public: + void publicMethod(); + int publicField; + +protected: + void protectedMethod(); + int protectedField; + +private: + void privateMethod(); + int privateField; +}; + +namespace named { +class NamedClass { +public: + void namedPublicMethod(); + int namedPublicField; + +protected: + void namedProtectedMethod(); + int namedProtectedField; + +private: + void namedPrivateMethod(); + int namedPrivateField; +}; + +void namedFunction(); +static void namedStaticFunction(); +inline void namedInlineFunction(); +} // namespace named + +static void staticFunction(int x); //Internal Linkage + +static int staticFunctionWithInnerClass(int x) { + class InnerClass { //NoLinkage + public: + int innerPublicMethod() { return 2; }; + }; //end class + InnerClass temp; + return temp.innerPublicMethod(); +}; + +namespace { +class AnonClass { +public: + void anonPublicMethod(); + int anonPublicField; + +protected: + void anonProtectedMethod(); + int anonProtectedField; + +private: + void anonPrivateMethod(); + int anonPrivateField; +}; + +void anonFunction(); +static void anonStaticFunction(); +inline void anonInlineFunction(); +} // namespace + +// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./Class.md | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: # class Class +// CHECK-0: *Defined at line 32 of test* +// CHECK-0: ## Members +// CHECK-0: int publicField +// CHECK-0: protected int protectedField +// CHECK-0: ## Functions +// CHECK-0: ### void publicMethod() +// CHECK-0: ### void protectedMethod() + +// RUN: cat %t/docs/./named.md | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: # namespace named +// CHECK-1: ## Functions +// CHECK-1: ### void namedFunction() +// CHECK-1: ### void namedInlineFunction() + +// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: # Global Namespace +// CHECK-2: ## Functions +// CHECK-2: ### void function(int x) +// CHECK-2: ### int inlinedFunction(int x) +// CHECK-2: ### int functionWithInnerClass(int x) +// CHECK-2: *Defined at line 14 of test* +// CHECK-2: ### int inlinedFunctionWithInnerClass(int x) +// CHECK-2: *Defined at line 23 of test* + +// RUN: cat %t/docs/named/NamedClass.md | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: # class NamedClass +// CHECK-3: *Defined at line 47 of test* +// CHECK-3: ## Members +// CHECK-3: int namedPublicField +// CHECK-3: protected int namedProtectedField +// CHECK-3: ## Functions +// CHECK-3: ### void namedPublicMethod() +// CHECK-3: ### void namedProtectedMethod() diff --git a/test/clang-doc/md-module.cpp b/test/clang-doc/md-module.cpp new file mode 100644 index 000000000..936f621bc --- /dev/null +++ b/test/clang-doc/md-module.cpp @@ -0,0 +1,24 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +export module M; + +int moduleFunction(int x); // ModuleLinkage + +static int staticModuleFunction(int x); // ModuleInternalLinkage + +export double exportedModuleFunction(double y, int z); // ExternalLinkage + +// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: # Global Namespace +// CHECK-0: ## Functions +// CHECK-0: ### int moduleFunction(int x) +// CHECK-0: ### double exportedModuleFunction(double y, int z) diff --git a/test/clang-doc/md-namespace.cpp b/test/clang-doc/md-namespace.cpp new file mode 100644 index 000000000..6db3fffd2 --- /dev/null +++ b/test/clang-doc/md-namespace.cpp @@ -0,0 +1,46 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +namespace A { + +void f(); + +} // namespace A + +namespace A { + +void f(){}; + +namespace B { + +enum E { X }; + +E func(int i) { return X; } + +} // namespace B +} // namespace A + +// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./A.md | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: # namespace A +// CHECK-0: ## Functions +// CHECK-0: ### void f() +// CHECK-0: *Defined at line 17 of test* + +// RUN: cat %t/docs/A/B.md | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: # namespace B +// CHECK-1: ## Functions +// CHECK-1: ### enum A::B::E func(int i) +// CHECK-1: *Defined at line 23 of test* +// CHECK-1: ## Enums +// CHECK-1: | enum E | +// CHECK-1: -- +// CHECK-1: | X | +// CHECK-1: *Defined at line 21 of test* diff --git a/test/clang-doc/md-record.cpp b/test/clang-doc/md-record.cpp new file mode 100644 index 000000000..1c5a1adf3 --- /dev/null +++ b/test/clang-doc/md-record.cpp @@ -0,0 +1,97 @@ +// THIS IS A GENERATED TEST. DO NOT EDIT. +// To regenerate, see clang-doc/gen_test.py docstring. +// +// This test requires Linux due to system-dependent USR for the inner class. +// REQUIRES: system-linux +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo "" > %t/compile_flags.txt +// RUN: cp "%s" "%t/test.cpp" + +void H() { + class I {}; +} + +union A { int X; int Y; }; + +enum B { X, Y }; + +enum class Bc { A, B }; + +struct C { int i; }; + +class D {}; + +class E { +public: + E() {} + ~E() {} + +protected: + void ProtectedMethod(); +}; + +void E::ProtectedMethod() {} + +class F : virtual private D, public E {}; + +class X { + class Y {}; +}; + +// RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs + + +// RUN: cat %t/docs/./F.md | FileCheck %s --check-prefix CHECK-0 +// CHECK-0: # class F +// CHECK-0: *Defined at line 36 of test* +// CHECK-0: Inherits from E, D + +// RUN: cat %t/docs/./D.md | FileCheck %s --check-prefix CHECK-1 +// CHECK-1: # class D +// CHECK-1: *Defined at line 23 of test* + +// RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-2 +// CHECK-2: # Global Namespace +// CHECK-2: ## Functions +// CHECK-2: ### void H() +// CHECK-2: *Defined at line 11 of test* +// CHECK-2: ## Enums +// CHECK-2: | enum B | +// CHECK-2: -- +// CHECK-2: | X | +// CHECK-2: | Y | +// CHECK-2: *Defined at line 17 of test* +// CHECK-2: | enum class Bc | +// CHECK-2: -- +// CHECK-2: | A | +// CHECK-2: | B | +// CHECK-2: *Defined at line 19 of test* + +// RUN: cat %t/docs/./E.md | FileCheck %s --check-prefix CHECK-3 +// CHECK-3: # class E +// CHECK-3: *Defined at line 25 of test* +// CHECK-3: ## Functions +// CHECK-3: ### void E() +// CHECK-3: *Defined at line 27 of test* +// CHECK-3: ### void ~E() +// CHECK-3: *Defined at line 28 of test* +// CHECK-3: ### void ProtectedMethod() +// CHECK-3: *Defined at line 34 of test* + +// RUN: cat %t/docs/./C.md | FileCheck %s --check-prefix CHECK-4 +// CHECK-4: # struct C +// CHECK-4: *Defined at line 21 of test* +// CHECK-4: ## Members +// CHECK-4: int i + +// RUN: cat %t/docs/./X.md | FileCheck %s --check-prefix CHECK-5 +// CHECK-5: # class X +// CHECK-5: *Defined at line 38 of test* + +// RUN: cat %t/docs/./A.md | FileCheck %s --check-prefix CHECK-6 +// CHECK-6: # union A +// CHECK-6: *Defined at line 15 of test* +// CHECK-6: ## Members +// CHECK-6: int X +// CHECK-6: int Y From 4fac53f8cbca67a03c5079b45febe04b8e20f926 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Tue, 11 Sep 2018 17:33:12 +0000 Subject: [PATCH 177/686] [clang-tidy] Handle unique owning smart pointers in ExprMutationAnalyzer Summary: For smart pointers like std::unique_ptr which uniquely owns the underlying object, treat the mutation of the pointee as mutation of the smart pointer itself. This gives better behavior for cases like this: ``` void f(std::vector> v) { // undesirable analyze result of `v` as not mutated. for (auto& p : v) { p->mutate(); // only const member function `operator->` is invoked on `p` } } ``` Reviewers: hokein, george.karpenkov Subscribers: xazax.hun, a.sidorin, Szelethus, cfe-commits Differential Revision: https://reviews.llvm.org/D50883 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341967 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/utils/ExprMutationAnalyzer.cpp | 42 ++++++++++++- .../clang-tidy/ExprMutationAnalyzerTest.cpp | 59 +++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/clang-tidy/utils/ExprMutationAnalyzer.cpp b/clang-tidy/utils/ExprMutationAnalyzer.cpp index f979e97a0..1ec1f7270 100644 --- a/clang-tidy/utils/ExprMutationAnalyzer.cpp +++ b/clang-tidy/utils/ExprMutationAnalyzer.cpp @@ -51,6 +51,20 @@ const auto nonConstReferenceType = [] { return referenceType(pointee(unless(isConstQualified()))); }; +const auto nonConstPointerType = [] { + return pointerType(pointee(unless(isConstQualified()))); +}; + +const auto isMoveOnly = [] { + return cxxRecordDecl( + hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))), + hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))), + unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(), + unless(isDeleted()))), + hasMethod(cxxMethodDecl(isCopyAssignmentOperator(), + unless(isDeleted())))))); +}; + } // namespace const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) { @@ -168,6 +182,15 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { const auto AsPointerFromArrayDecay = castExpr(hasCastKind(CK_ArrayToPointerDecay), unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp))); + // Treat calling `operator->()` of move-only classes as taking address. + // These are typically smart pointers with unique ownership so we treat + // mutation of pointee as mutation of the smart pointer itself. + const auto AsOperatorArrowThis = cxxOperatorCallExpr( + hasOverloadedOperatorName("->"), + callee(cxxMethodDecl( + ofClass(isMoveOnly()), + returns(hasUnqualifiedDesugaredType(nonConstPointerType())))), + argumentCountIs(1), hasArgument(0, equalsNode(Exp))); // Used as non-const-ref argument when calling a function. // An argument is assumed to be non-const-ref when the function is unresolved. @@ -197,8 +220,8 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { const auto Matches = match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis, AsAmpersandOperand, AsPointerFromArrayDecay, - AsNonConstRefArg, AsLambdaRefCaptureInit, - AsNonConstRefReturn)) + AsOperatorArrowThis, AsNonConstRefArg, + AsLambdaRefCaptureInit, AsNonConstRefReturn)) .bind("stmt")), Stm, Context); return selectFirst("stmt", Matches); @@ -250,6 +273,21 @@ const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) { } const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) { + // Follow non-const reference returned by `operator*()` of move-only classes. + // These are typically smart pointers with unique ownership so we treat + // mutation of pointee as mutation of the smart pointer itself. + const auto Ref = match( + findAll(cxxOperatorCallExpr( + hasOverloadedOperatorName("*"), + callee(cxxMethodDecl(ofClass(isMoveOnly()), + returns(hasUnqualifiedDesugaredType( + nonConstReferenceType())))), + argumentCountIs(1), hasArgument(0, equalsNode(Exp))) + .bind("expr")), + Stm, Context); + if (const Stmt *S = findExprMutation(Ref)) + return S; + // If 'Exp' is bound to a non-const reference, check all declRefExpr to that. const auto Refs = match( stmt(forEachDescendant( diff --git a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp index 71511d635..14878ed11 100644 --- a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp +++ b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp @@ -724,6 +724,65 @@ TEST(ExprMutationAnalyzerTest, NotUnevaluatedExpressions) { EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.f()")); } +TEST(ExprMutationAnalyzerTest, UniquePtr) { + const std::string UniquePtrDef = + "template struct UniquePtr {" + " UniquePtr();" + " UniquePtr(const UniquePtr&) = delete;" + " UniquePtr(UniquePtr&&);" + " UniquePtr& operator=(const UniquePtr&) = delete;" + " UniquePtr& operator=(UniquePtr&&);" + " T& operator*() const;" + " T* operator->() const;" + "};"; + + auto AST = tooling::buildASTFromCode( + UniquePtrDef + "void f() { UniquePtr x; *x = 10; }"); + auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("* x = 10")); + + AST = tooling::buildASTFromCode(UniquePtrDef + + "void f() { UniquePtr x; *x; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode(UniquePtrDef + + "void f() { UniquePtr x; *x; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode(UniquePtrDef + + "struct S { int v; };" + "void f() { UniquePtr x; x->v; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); + + AST = tooling::buildASTFromCode(UniquePtrDef + + "struct S { int v; };" + "void f() { UniquePtr x; x->v; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode(UniquePtrDef + + "struct S { void mf(); };" + "void f() { UniquePtr x; x->mf(); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); + + AST = tooling::buildASTFromCode( + UniquePtrDef + "struct S { void mf() const; };" + "void f() { UniquePtr x; x->mf(); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCodeWithArgs( + UniquePtrDef + "template void f() { UniquePtr x; x->mf(); }", + {"-fno-delayed-template-parsing"}); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x->mf()")); +} + } // namespace test } // namespace tidy } // namespace clang From af5e2eb00b3fade1850328cedb82266133911951 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Tue, 11 Sep 2018 20:05:37 +0000 Subject: [PATCH 178/686] [clang-tidy] Handle sugared reference types in ExprMutationAnalyzer Summary: This handles cases like this: ``` typedef int& IntRef; void mutate(IntRef); void f() { int x; mutate(x); } ``` where the param type is a sugared type (`TypedefType`) instead of a reference type directly. Note that another category of similar but different cases are already handled properly before: ``` typedef int Int; void mutate(Int&); void f() { int x; mutate(x); } ``` Reviewers: aaron.ballman, alexfh, george.karpenkov Subscribers: xazax.hun, a.sidorin, Szelethus, cfe-commits Differential Revision: https://reviews.llvm.org/D50953 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@341986 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/utils/ExprMutationAnalyzer.cpp | 17 +- .../clang-tidy/ExprMutationAnalyzerTest.cpp | 191 ++++++++++++++++-- 2 files changed, 184 insertions(+), 24 deletions(-) diff --git a/clang-tidy/utils/ExprMutationAnalyzer.cpp b/clang-tidy/utils/ExprMutationAnalyzer.cpp index 1ec1f7270..e04ac724c 100644 --- a/clang-tidy/utils/ExprMutationAnalyzer.cpp +++ b/clang-tidy/utils/ExprMutationAnalyzer.cpp @@ -48,11 +48,13 @@ AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr, } const auto nonConstReferenceType = [] { - return referenceType(pointee(unless(isConstQualified()))); + return hasUnqualifiedDesugaredType( + referenceType(pointee(unless(isConstQualified())))); }; const auto nonConstPointerType = [] { - return pointerType(pointee(unless(isConstQualified()))); + return hasUnqualifiedDesugaredType( + pointerType(pointee(unless(isConstQualified())))); }; const auto isMoveOnly = [] { @@ -185,12 +187,11 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { // Treat calling `operator->()` of move-only classes as taking address. // These are typically smart pointers with unique ownership so we treat // mutation of pointee as mutation of the smart pointer itself. - const auto AsOperatorArrowThis = cxxOperatorCallExpr( - hasOverloadedOperatorName("->"), - callee(cxxMethodDecl( - ofClass(isMoveOnly()), - returns(hasUnqualifiedDesugaredType(nonConstPointerType())))), - argumentCountIs(1), hasArgument(0, equalsNode(Exp))); + const auto AsOperatorArrowThis = + cxxOperatorCallExpr(hasOverloadedOperatorName("->"), + callee(cxxMethodDecl(ofClass(isMoveOnly()), + returns(nonConstPointerType()))), + argumentCountIs(1), hasArgument(0, equalsNode(Exp))); // Used as non-const-ref argument when calling a function. // An argument is assumed to be non-const-ref when the function is unresolved. diff --git a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp index 14878ed11..7f4a4cc54 100644 --- a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp +++ b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp @@ -168,6 +168,15 @@ TEST(ExprMutationAnalyzerTest, ByValueArgument) { match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_FALSE(isMutated(Results, AST.get())); + AST = tooling::buildASTFromCode("void g(int*); void f() { int* x; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode("typedef int* IntPtr;" + "void g(IntPtr); void f() { int* x; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + AST = tooling::buildASTFromCode( "void f() { struct A {}; A operator+(A, int); A x; x + 1; }"); Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); @@ -184,6 +193,40 @@ TEST(ExprMutationAnalyzerTest, ByValueArgument) { EXPECT_FALSE(isMutated(Results, AST.get())); } +TEST(ExprMutationAnalyzerTest, ByConstValueArgument) { + auto AST = + tooling::buildASTFromCode("void g(const int); void f() { int x; g(x); }"); + auto Results = + match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode( + "void g(int* const); void f() { int* x; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = + tooling::buildASTFromCode("typedef int* const CIntPtr;" + "void g(CIntPtr); void f() { int* x; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode( + "void f() { struct A {}; A operator+(const A, int); A x; x + 1; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode( + "void f() { struct A { A(const int); }; int x; A y(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode( + "void f() { struct A { A(); A(const A); }; A x; A y(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); +} + TEST(ExprMutationAnalyzerTest, ByNonConstRefArgument) { auto AST = tooling::buildASTFromCode("void g(int&); void f() { int x; g(x); }"); @@ -191,6 +234,36 @@ TEST(ExprMutationAnalyzerTest, ByNonConstRefArgument) { match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); + AST = tooling::buildASTFromCode("typedef int& IntRef;" + "void g(IntRef); void f() { int x; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); + + AST = + tooling::buildASTFromCode("template using TRef = T&;" + "void g(TRef); void f() { int x; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); + + AST = tooling::buildASTFromCode( + "template struct identity { using type = T; };" + "template void g(typename identity::type);" + "void f() { int x; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); + + AST = + tooling::buildASTFromCode("typedef int* IntPtr;" + "void g(IntPtr&); void f() { int* x; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); + + AST = tooling::buildASTFromCode( + "typedef int* IntPtr; typedef IntPtr& IntPtrRef;" + "void g(IntPtrRef); void f() { int* x; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); + AST = tooling::buildASTFromCode( "void f() { struct A {}; A operator+(A&, int); A x; x + 1; }"); Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); @@ -214,6 +287,25 @@ TEST(ExprMutationAnalyzerTest, ByConstRefArgument) { match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_FALSE(isMutated(Results, AST.get())); + AST = tooling::buildASTFromCode("typedef const int& CIntRef;" + "void g(CIntRef); void f() { int x; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode( + "template using CTRef = const T&;" + "void g(CTRef); void f() { int x; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode( + "template struct identity { using type = T; };" + "template " + "void g(typename identity::type);" + "void f() { int x; g(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + AST = tooling::buildASTFromCode( "void f() { struct A {}; A operator+(const A&, int); A x; x + 1; }"); Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); @@ -369,10 +461,19 @@ TEST(ExprMutationAnalyzerTest, CallUnresolved) { } TEST(ExprMutationAnalyzerTest, ReturnAsValue) { - const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }"); - const auto Results = + auto AST = tooling::buildASTFromCode("int f() { int x; return x; }"); + auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode("int* f() { int* x; return x; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode("typedef int* IntPtr;" + "IntPtr f() { int* x; return x; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); } TEST(ExprMutationAnalyzerTest, ReturnAsNonConstRef) { @@ -440,22 +541,44 @@ TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) { } TEST(ExprMutationAnalyzerTest, FollowRefModified) { - const auto AST = tooling::buildASTFromCode( + auto AST = tooling::buildASTFromCode( "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; " "int& r3 = r2; r3 = 10; }"); - const auto Results = + auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("r0", "r1", "r2", "r3", "r3 = 10")); + + AST = tooling::buildASTFromCode( + "typedef int& IntRefX;" + "using IntRefY = int&;" + "void f() { int x; IntRefX r0 = x; IntRefY r1 = r0;" + "decltype((x)) r2 = r1; r2 = 10; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), + ElementsAre("r0", "r1", "r2", "r2 = 10")); } TEST(ExprMutationAnalyzerTest, FollowRefNotModified) { - const auto AST = tooling::buildASTFromCode( + auto AST = tooling::buildASTFromCode( "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; " "int& r3 = r2; int& r4 = r3; int& r5 = r4;}"); - const auto Results = + auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode( + "void f() { int x; int& r0 = x; const int& r1 = r0;}"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode( + "typedef const int& CIntRefX;" + "using CIntRefY = const int&;" + "void f() { int x; int& r0 = x; CIntRefX r1 = r0;" + "CIntRefY r2 = r1; decltype((r1)) r3 = r2;}"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); } TEST(ExprMutationAnalyzerTest, FollowConditionalRefModified) { @@ -542,12 +665,19 @@ TEST(ExprMutationAnalyzerTest, CastToValue) { } TEST(ExprMutationAnalyzerTest, CastToRefModified) { - const auto AST = tooling::buildASTFromCode( + auto AST = tooling::buildASTFromCode( "void f() { int x; static_cast(x) = 10; }"); - const auto Results = + auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("static_cast(x) = 10")); + + AST = tooling::buildASTFromCode( + "typedef int& IntRef;" + "void f() { int x; static_cast(x) = 10; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), + ElementsAre("static_cast(x) = 10")); } TEST(ExprMutationAnalyzerTest, CastToRefNotModified) { @@ -559,11 +689,17 @@ TEST(ExprMutationAnalyzerTest, CastToRefNotModified) { } TEST(ExprMutationAnalyzerTest, CastToConstRef) { - const auto AST = tooling::buildASTFromCode( + auto AST = tooling::buildASTFromCode( "void f() { int x; static_cast(x); }"); - const auto Results = + auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = + tooling::buildASTFromCode("typedef const int& CIntRef;" + "void f() { int x; static_cast(x); }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); } TEST(ExprMutationAnalyzerTest, LambdaDefaultCaptureByValue) { @@ -601,11 +737,17 @@ TEST(ExprMutationAnalyzerTest, LambdaExplicitCaptureByRef) { } TEST(ExprMutationAnalyzerTest, RangeForArrayByRefModified) { - const auto AST = tooling::buildASTFromCode( + auto AST = tooling::buildASTFromCode( "void f() { int x[2]; for (int& e : x) e = 10; }"); - const auto Results = + auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("e", "e = 10")); + + AST = tooling::buildASTFromCode( + "typedef int& IntRef;" + "void f() { int x[2]; for (IntRef e : x) e = 10; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("e", "e = 10")); } TEST(ExprMutationAnalyzerTest, RangeForArrayByRefNotModified) { @@ -617,19 +759,36 @@ TEST(ExprMutationAnalyzerTest, RangeForArrayByRefNotModified) { } TEST(ExprMutationAnalyzerTest, RangeForArrayByValue) { - const auto AST = tooling::buildASTFromCode( + auto AST = tooling::buildASTFromCode( "void f() { int x[2]; for (int e : x) e = 10; }"); - const auto Results = + auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode( + "void f() { int* x[2]; for (int* e : x) e = nullptr; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode( + "typedef int* IntPtr;" + "void f() { int* x[2]; for (IntPtr e : x) e = nullptr; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); } TEST(ExprMutationAnalyzerTest, RangeForArrayByConstRef) { - const auto AST = tooling::buildASTFromCode( + auto AST = tooling::buildASTFromCode( "void f() { int x[2]; for (const int& e : x) e; }"); - const auto Results = + auto Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); EXPECT_FALSE(isMutated(Results, AST.get())); + + AST = tooling::buildASTFromCode( + "typedef const int& CIntRef;" + "void f() { int x[2]; for (CIntRef e : x) e; }"); + Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); } TEST(ExprMutationAnalyzerTest, RangeForNonArrayByRefModified) { From a7e338194a11580a32506b8d596862b7b19fe4b7 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Tue, 11 Sep 2018 22:59:46 +0000 Subject: [PATCH 179/686] [clangtidy] Remove old copy of ExprMutationAnalyzer Summary: This is 2/2 of moving ExprMutationAnalyzer from clangtidy to clang/Analysis. ExprMutationAnalyzer is moved to clang/Analysis in D51948. This diff migrates existing usages within clangtidy to point to the new location and remove the old copy of ExprMutationAnalyzer. Reviewers: george.karpenkov, JonasToth Reviewed By: george.karpenkov Subscribers: mgorny, a.sidorin, Szelethus, cfe-commits Differential Revision: https://reviews.llvm.org/D51950 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342006 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/performance/ForRangeCopyCheck.cpp | 5 +- .../UnnecessaryValueParamCheck.cpp | 6 +- clang-tidy/utils/CMakeLists.txt | 1 - clang-tidy/utils/ExprMutationAnalyzer.cpp | 313 ------ clang-tidy/utils/ExprMutationAnalyzer.h | 56 -- unittests/clang-tidy/CMakeLists.txt | 1 - .../clang-tidy/ExprMutationAnalyzerTest.cpp | 947 ------------------ 7 files changed, 5 insertions(+), 1324 deletions(-) delete mode 100644 clang-tidy/utils/ExprMutationAnalyzer.cpp delete mode 100644 clang-tidy/utils/ExprMutationAnalyzer.h delete mode 100644 unittests/clang-tidy/ExprMutationAnalyzerTest.cpp diff --git a/clang-tidy/performance/ForRangeCopyCheck.cpp b/clang-tidy/performance/ForRangeCopyCheck.cpp index 0a3cc4f6b..d60a2f349 100644 --- a/clang-tidy/performance/ForRangeCopyCheck.cpp +++ b/clang-tidy/performance/ForRangeCopyCheck.cpp @@ -9,9 +9,9 @@ #include "ForRangeCopyCheck.h" #include "../utils/DeclRefExprUtils.h" -#include "../utils/ExprMutationAnalyzer.h" #include "../utils/FixItHintUtils.h" #include "../utils/TypeTraits.h" +#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" using namespace clang::ast_matchers; @@ -88,8 +88,7 @@ bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced( // Because the fix (changing to `const auto &`) will introduce an unused // compiler warning which can't be suppressed. // Since this case is very rare, it is safe to ignore it. - if (!utils::ExprMutationAnalyzer(*ForRange.getBody(), Context) - .isMutated(&LoopVar) && + if (!ExprMutationAnalyzer(*ForRange.getBody(), Context).isMutated(&LoopVar) && !utils::decl_ref_expr::allDeclRefExprs(LoopVar, *ForRange.getBody(), Context) .empty()) { diff --git a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp index 63b853dec..29a8b84cc 100644 --- a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp +++ b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp @@ -10,10 +10,10 @@ #include "UnnecessaryValueParamCheck.h" #include "../utils/DeclRefExprUtils.h" -#include "../utils/ExprMutationAnalyzer.h" #include "../utils/FixItHintUtils.h" #include "../utils/Matchers.h" #include "../utils/TypeTraits.h" +#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" @@ -95,14 +95,14 @@ void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) { // Do not trigger on non-const value parameters when they are mutated either // within the function body or within init expression(s) when the function is // a ctor. - if (utils::ExprMutationAnalyzer(*Function->getBody(), *Result.Context) + if (ExprMutationAnalyzer(*Function->getBody(), *Result.Context) .isMutated(Param)) return; // CXXCtorInitializer might also mutate Param but they're not part of function // body, so check them separately here. if (const auto *Ctor = dyn_cast(Function)) { for (const auto *Init : Ctor->inits()) { - if (utils::ExprMutationAnalyzer(*Init->getInit(), *Result.Context) + if (ExprMutationAnalyzer(*Init->getInit(), *Result.Context) .isMutated(Param)) return; } diff --git a/clang-tidy/utils/CMakeLists.txt b/clang-tidy/utils/CMakeLists.txt index 487b09ac8..9162bce1e 100644 --- a/clang-tidy/utils/CMakeLists.txt +++ b/clang-tidy/utils/CMakeLists.txt @@ -3,7 +3,6 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyUtils ASTUtils.cpp DeclRefExprUtils.cpp - ExprMutationAnalyzer.cpp ExprSequence.cpp FixItHintUtils.cpp HeaderFileExtensionsUtils.cpp diff --git a/clang-tidy/utils/ExprMutationAnalyzer.cpp b/clang-tidy/utils/ExprMutationAnalyzer.cpp deleted file mode 100644 index e04ac724c..000000000 --- a/clang-tidy/utils/ExprMutationAnalyzer.cpp +++ /dev/null @@ -1,313 +0,0 @@ -//===---------- ExprMutationAnalyzer.cpp - clang-tidy ---------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#include "ExprMutationAnalyzer.h" - -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "llvm/ADT/STLExtras.h" - -namespace clang { -namespace tidy { -namespace utils { -using namespace ast_matchers; - -namespace { - -AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) { - return llvm::is_contained(Node.capture_inits(), E); -} - -AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt, - ast_matchers::internal::Matcher, InnerMatcher) { - const DeclStmt *const Range = Node.getRangeStmt(); - return InnerMatcher.matches(*Range, Finder, Builder); -} - -const ast_matchers::internal::VariadicDynCastAllOfMatcher - cxxTypeidExpr; - -AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) { - return Node.isPotentiallyEvaluated(); -} - -const ast_matchers::internal::VariadicDynCastAllOfMatcher - cxxNoexceptExpr; - -const ast_matchers::internal::VariadicDynCastAllOfMatcher - genericSelectionExpr; - -AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr, - ast_matchers::internal::Matcher, InnerMatcher) { - return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder); -} - -const auto nonConstReferenceType = [] { - return hasUnqualifiedDesugaredType( - referenceType(pointee(unless(isConstQualified())))); -}; - -const auto nonConstPointerType = [] { - return hasUnqualifiedDesugaredType( - pointerType(pointee(unless(isConstQualified())))); -}; - -const auto isMoveOnly = [] { - return cxxRecordDecl( - hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))), - hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))), - unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(), - unless(isDeleted()))), - hasMethod(cxxMethodDecl(isCopyAssignmentOperator(), - unless(isDeleted())))))); -}; - -} // namespace - -const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) { - const auto Memoized = Results.find(Exp); - if (Memoized != Results.end()) - return Memoized->second; - - if (isUnevaluated(Exp)) - return Results[Exp] = nullptr; - - for (const auto &Finder : {&ExprMutationAnalyzer::findDirectMutation, - &ExprMutationAnalyzer::findMemberMutation, - &ExprMutationAnalyzer::findArrayElementMutation, - &ExprMutationAnalyzer::findCastMutation, - &ExprMutationAnalyzer::findRangeLoopMutation, - &ExprMutationAnalyzer::findReferenceMutation}) { - if (const Stmt *S = (this->*Finder)(Exp)) - return Results[Exp] = S; - } - - return Results[Exp] = nullptr; -} - -bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) { - return selectFirst( - "expr", - match( - findAll( - expr(equalsNode(Exp), - anyOf( - // `Exp` is part of the underlying expression of - // decltype/typeof if it has an ancestor of - // typeLoc. - hasAncestor(typeLoc(unless( - hasAncestor(unaryExprOrTypeTraitExpr())))), - hasAncestor(expr(anyOf( - // `UnaryExprOrTypeTraitExpr` is unevaluated - // unless it's sizeof on VLA. - unaryExprOrTypeTraitExpr(unless(sizeOfExpr( - hasArgumentOfType(variableArrayType())))), - // `CXXTypeidExpr` is unevaluated unless it's - // applied to an expression of glvalue of - // polymorphic class type. - cxxTypeidExpr( - unless(isPotentiallyEvaluated())), - // The controlling expression of - // `GenericSelectionExpr` is unevaluated. - genericSelectionExpr(hasControllingExpr( - hasDescendant(equalsNode(Exp)))), - cxxNoexceptExpr()))))) - .bind("expr")), - Stm, Context)) != nullptr; -} - -const Stmt * -ExprMutationAnalyzer::findExprMutation(ArrayRef Matches) { - for (const auto &Nodes : Matches) { - if (const Stmt *S = findMutation(Nodes.getNodeAs("expr"))) - return S; - } - return nullptr; -} - -const Stmt * -ExprMutationAnalyzer::findDeclMutation(ArrayRef Matches) { - for (const auto &DeclNodes : Matches) { - if (const Stmt *S = findDeclMutation(DeclNodes.getNodeAs("decl"))) - return S; - } - return nullptr; -} - -const Stmt *ExprMutationAnalyzer::findDeclMutation(const Decl *Dec) { - const auto Refs = match( - findAll(declRefExpr(to(equalsNode(Dec))).bind("expr")), Stm, Context); - for (const auto &RefNodes : Refs) { - const auto *E = RefNodes.getNodeAs("expr"); - if (findMutation(E)) - return E; - } - return nullptr; -} - -const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { - // LHS of any assignment operators. - const auto AsAssignmentLhs = - binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(Exp))); - - // Operand of increment/decrement operators. - const auto AsIncDecOperand = - unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")), - hasUnaryOperand(equalsNode(Exp))); - - // Invoking non-const member function. - // A member function is assumed to be non-const when it is unresolved. - const auto NonConstMethod = cxxMethodDecl(unless(isConst())); - const auto AsNonConstThis = - expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))), - cxxOperatorCallExpr(callee(NonConstMethod), - hasArgument(0, equalsNode(Exp))), - callExpr(callee(expr(anyOf( - unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))), - cxxDependentScopeMemberExpr( - hasObjectExpression(equalsNode(Exp))))))))); - - // Taking address of 'Exp'. - // We're assuming 'Exp' is mutated as soon as its address is taken, though in - // theory we can follow the pointer and see whether it escaped `Stm` or is - // dereferenced and then mutated. This is left for future improvements. - const auto AsAmpersandOperand = - unaryOperator(hasOperatorName("&"), - // A NoOp implicit cast is adding const. - unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))), - hasUnaryOperand(equalsNode(Exp))); - const auto AsPointerFromArrayDecay = - castExpr(hasCastKind(CK_ArrayToPointerDecay), - unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp))); - // Treat calling `operator->()` of move-only classes as taking address. - // These are typically smart pointers with unique ownership so we treat - // mutation of pointee as mutation of the smart pointer itself. - const auto AsOperatorArrowThis = - cxxOperatorCallExpr(hasOverloadedOperatorName("->"), - callee(cxxMethodDecl(ofClass(isMoveOnly()), - returns(nonConstPointerType()))), - argumentCountIs(1), hasArgument(0, equalsNode(Exp))); - - // Used as non-const-ref argument when calling a function. - // An argument is assumed to be non-const-ref when the function is unresolved. - const auto NonConstRefParam = forEachArgumentWithParam( - equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType()))); - const auto AsNonConstRefArg = anyOf( - callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam), - callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(), - cxxDependentScopeMemberExpr(), - hasType(templateTypeParmType())))), - hasAnyArgument(equalsNode(Exp))), - cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp)))); - - // Captured by a lambda by reference. - // If we're initializing a capture with 'Exp' directly then we're initializing - // a reference capture. - // For value captures there will be an ImplicitCastExpr . - const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp)); - - // Returned as non-const-ref. - // If we're returning 'Exp' directly then it's returned as non-const-ref. - // For returning by value there will be an ImplicitCastExpr . - // For returning by const-ref there will be an ImplicitCastExpr (for - // adding const.) - const auto AsNonConstRefReturn = returnStmt(hasReturnValue(equalsNode(Exp))); - - const auto Matches = - match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis, - AsAmpersandOperand, AsPointerFromArrayDecay, - AsOperatorArrowThis, AsNonConstRefArg, - AsLambdaRefCaptureInit, AsNonConstRefReturn)) - .bind("stmt")), - Stm, Context); - return selectFirst("stmt", Matches); -} - -const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) { - // Check whether any member of 'Exp' is mutated. - const auto MemberExprs = - match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))), - cxxDependentScopeMemberExpr( - hasObjectExpression(equalsNode(Exp))))) - .bind("expr")), - Stm, Context); - return findExprMutation(MemberExprs); -} - -const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) { - // Check whether any element of an array is mutated. - const auto SubscriptExprs = match( - findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp)))) - .bind("expr")), - Stm, Context); - return findExprMutation(SubscriptExprs); -} - -const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) { - // If 'Exp' is casted to any non-const reference type, check the castExpr. - const auto Casts = - match(findAll(castExpr(hasSourceExpression(equalsNode(Exp)), - anyOf(explicitCastExpr(hasDestinationType( - nonConstReferenceType())), - implicitCastExpr(hasImplicitDestinationType( - nonConstReferenceType())))) - .bind("expr")), - Stm, Context); - return findExprMutation(Casts); -} - -const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) { - // If range for looping over 'Exp' with a non-const reference loop variable, - // check all declRefExpr of the loop variable. - const auto LoopVars = - match(findAll(cxxForRangeStmt( - hasLoopVariable( - varDecl(hasType(nonConstReferenceType())).bind("decl")), - hasRangeInit(equalsNode(Exp)))), - Stm, Context); - return findDeclMutation(LoopVars); -} - -const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) { - // Follow non-const reference returned by `operator*()` of move-only classes. - // These are typically smart pointers with unique ownership so we treat - // mutation of pointee as mutation of the smart pointer itself. - const auto Ref = match( - findAll(cxxOperatorCallExpr( - hasOverloadedOperatorName("*"), - callee(cxxMethodDecl(ofClass(isMoveOnly()), - returns(hasUnqualifiedDesugaredType( - nonConstReferenceType())))), - argumentCountIs(1), hasArgument(0, equalsNode(Exp))) - .bind("expr")), - Stm, Context); - if (const Stmt *S = findExprMutation(Ref)) - return S; - - // If 'Exp' is bound to a non-const reference, check all declRefExpr to that. - const auto Refs = match( - stmt(forEachDescendant( - varDecl( - hasType(nonConstReferenceType()), - hasInitializer(anyOf(equalsNode(Exp), - conditionalOperator(anyOf( - hasTrueExpression(equalsNode(Exp)), - hasFalseExpression(equalsNode(Exp)))))), - hasParent(declStmt().bind("stmt")), - // Don't follow the reference in range statement, we've handled - // that separately. - unless(hasParent(declStmt(hasParent( - cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt")))))))) - .bind("decl"))), - Stm, Context); - return findDeclMutation(Refs); -} - -} // namespace utils -} // namespace tidy -} // namespace clang diff --git a/clang-tidy/utils/ExprMutationAnalyzer.h b/clang-tidy/utils/ExprMutationAnalyzer.h deleted file mode 100644 index e295de9bb..000000000 --- a/clang-tidy/utils/ExprMutationAnalyzer.h +++ /dev/null @@ -1,56 +0,0 @@ -//===---------- ExprMutationAnalyzer.h - clang-tidy -----------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPRMUTATIONANALYZER_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPRMUTATIONANALYZER_H - -#include - -#include "clang/AST/AST.h" -#include "clang/ASTMatchers/ASTMatchers.h" -#include "llvm/ADT/DenseMap.h" - -namespace clang { -namespace tidy { -namespace utils { - -/// Analyzes whether any mutative operations are applied to an expression within -/// a given statement. -class ExprMutationAnalyzer { -public: - ExprMutationAnalyzer(const Stmt &Stm, ASTContext &Context) - : Stm(Stm), Context(Context) {} - - bool isMutated(const Decl *Dec) { return findDeclMutation(Dec) != nullptr; } - bool isMutated(const Expr *Exp) { return findMutation(Exp) != nullptr; } - const Stmt *findMutation(const Expr *Exp); - -private: - bool isUnevaluated(const Expr *Exp); - - const Stmt *findExprMutation(ArrayRef Matches); - const Stmt *findDeclMutation(ArrayRef Matches); - const Stmt *findDeclMutation(const Decl *Dec); - - const Stmt *findDirectMutation(const Expr *Exp); - const Stmt *findMemberMutation(const Expr *Exp); - const Stmt *findArrayElementMutation(const Expr *Exp); - const Stmt *findCastMutation(const Expr *Exp); - const Stmt *findRangeLoopMutation(const Expr *Exp); - const Stmt *findReferenceMutation(const Expr *Exp); - - const Stmt &Stm; - ASTContext &Context; - llvm::DenseMap Results; -}; - -} // namespace utils -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPRMUTATIONANALYZER_H diff --git a/unittests/clang-tidy/CMakeLists.txt b/unittests/clang-tidy/CMakeLists.txt index a678e59c6..c56445371 100644 --- a/unittests/clang-tidy/CMakeLists.txt +++ b/unittests/clang-tidy/CMakeLists.txt @@ -9,7 +9,6 @@ include_directories(${CLANG_LINT_SOURCE_DIR}) add_extra_unittest(ClangTidyTests ClangTidyDiagnosticConsumerTest.cpp ClangTidyOptionsTest.cpp - ExprMutationAnalyzerTest.cpp IncludeInserterTest.cpp GoogleModuleTest.cpp LLVMModuleTest.cpp diff --git a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp b/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp deleted file mode 100644 index 7f4a4cc54..000000000 --- a/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp +++ /dev/null @@ -1,947 +0,0 @@ -//===---------- ExprMutationAnalyzerTest.cpp - clang-tidy -----------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "../clang-tidy/utils/ExprMutationAnalyzer.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/Tooling/Tooling.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include - -namespace clang { -namespace tidy { -namespace test { - -using namespace clang::ast_matchers; -using ::testing::ElementsAre; -using ::testing::IsEmpty; -using ::testing::ResultOf; -using ::testing::StartsWith; -using ::testing::Values; - -namespace { - -using ExprMatcher = internal::Matcher; -using StmtMatcher = internal::Matcher; - -ExprMatcher declRefTo(StringRef Name) { - return declRefExpr(to(namedDecl(hasName(Name)))); -} - -StmtMatcher withEnclosingCompound(ExprMatcher Matcher) { - return expr(Matcher, hasAncestor(compoundStmt().bind("stmt"))).bind("expr"); -} - -bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) { - const auto *const S = selectFirst("stmt", Results); - const auto *const E = selectFirst("expr", Results); - return utils::ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(E); -} - -SmallVector -mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) { - const auto *const S = selectFirst("stmt", Results); - SmallVector Chain; - utils::ExprMutationAnalyzer Analyzer(*S, AST->getASTContext()); - for (const auto *E = selectFirst("expr", Results); E != nullptr;) { - const Stmt *By = Analyzer.findMutation(E); - std::string buffer; - llvm::raw_string_ostream stream(buffer); - By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy()); - Chain.push_back(StringRef(stream.str()).trim().str()); - E = dyn_cast(By); - } - return Chain; -} - -std::string removeSpace(std::string s) { - s.erase(std::remove_if(s.begin(), s.end(), - [](char c) { return std::isspace(c); }), - s.end()); - return s; -} - -} // namespace - -TEST(ExprMutationAnalyzerTest, Trivial) { - const auto AST = tooling::buildASTFromCode("void f() { int x; x; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -class AssignmentTest : public ::testing::TestWithParam {}; - -TEST_P(AssignmentTest, AssignmentModifies) { - const std::string ModExpr = "x " + GetParam() + " 10"; - const auto AST = - tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); -} - -INSTANTIATE_TEST_CASE_P(AllAssignmentOperators, AssignmentTest, - Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=", - "^=", "<<=", ">>="), ); - -class IncDecTest : public ::testing::TestWithParam {}; - -TEST_P(IncDecTest, IncDecModifies) { - const std::string ModExpr = GetParam(); - const auto AST = - tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); -} - -INSTANTIATE_TEST_CASE_P(AllIncDecOperators, IncDecTest, - Values("++x", "--x", "x++", "x--"), ); - -TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) { - const auto AST = tooling::buildASTFromCode( - "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); -} - -TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) { - auto AST = tooling::buildASTFromCodeWithArgs( - "struct X { template void mf(); };" - "template void f() { X x; x.mf(); }", - {"-fno-delayed-template-parsing"}); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); - - AST = tooling::buildASTFromCodeWithArgs( - "template void f() { T x; x.mf(); }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); - - AST = tooling::buildASTFromCodeWithArgs( - "template struct X;" - "template void f() { X x; x.mf(); }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); -} - -TEST(ExprMutationAnalyzerTest, ConstMemberFunc) { - const auto AST = tooling::buildASTFromCode( - "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, NonConstOperator) { - const auto AST = tooling::buildASTFromCode( - "void f() { struct Foo { Foo& operator=(int); }; Foo x; x = 10; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x = 10")); -} - -TEST(ExprMutationAnalyzerTest, ConstOperator) { - const auto AST = tooling::buildASTFromCode( - "void f() { struct Foo { int operator()() const; }; Foo x; x(); }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, ByValueArgument) { - auto AST = - tooling::buildASTFromCode("void g(int); void f() { int x; g(x); }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode("void g(int*); void f() { int* x; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode("typedef int* IntPtr;" - "void g(IntPtr); void f() { int* x; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { struct A {}; A operator+(A, int); A x; x + 1; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { struct A { A(int); }; int x; A y(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { struct A { A(); A(A); }; A x; A y(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, ByConstValueArgument) { - auto AST = - tooling::buildASTFromCode("void g(const int); void f() { int x; g(x); }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void g(int* const); void f() { int* x; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = - tooling::buildASTFromCode("typedef int* const CIntPtr;" - "void g(CIntPtr); void f() { int* x; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { struct A {}; A operator+(const A, int); A x; x + 1; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { struct A { A(const int); }; int x; A y(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { struct A { A(); A(const A); }; A x; A y(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, ByNonConstRefArgument) { - auto AST = - tooling::buildASTFromCode("void g(int&); void f() { int x; g(x); }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); - - AST = tooling::buildASTFromCode("typedef int& IntRef;" - "void g(IntRef); void f() { int x; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); - - AST = - tooling::buildASTFromCode("template using TRef = T&;" - "void g(TRef); void f() { int x; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); - - AST = tooling::buildASTFromCode( - "template struct identity { using type = T; };" - "template void g(typename identity::type);" - "void f() { int x; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); - - AST = - tooling::buildASTFromCode("typedef int* IntPtr;" - "void g(IntPtr&); void f() { int* x; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); - - AST = tooling::buildASTFromCode( - "typedef int* IntPtr; typedef IntPtr& IntPtrRef;" - "void g(IntPtrRef); void f() { int* x; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); - - AST = tooling::buildASTFromCode( - "void f() { struct A {}; A operator+(A&, int); A x; x + 1; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x + 1")); - - AST = tooling::buildASTFromCode( - "void f() { struct A { A(int&); }; int x; A y(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); - - AST = tooling::buildASTFromCode( - "void f() { struct A { A(); A(A&); }; A x; A y(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); -} - -TEST(ExprMutationAnalyzerTest, ByConstRefArgument) { - auto AST = tooling::buildASTFromCode( - "void g(const int&); void f() { int x; g(x); }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode("typedef const int& CIntRef;" - "void g(CIntRef); void f() { int x; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "template using CTRef = const T&;" - "void g(CTRef); void f() { int x; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "template struct identity { using type = T; };" - "template " - "void g(typename identity::type);" - "void f() { int x; g(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { struct A {}; A operator+(const A&, int); A x; x + 1; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { struct A { A(const int&); }; int x; A y(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { struct A { A(); A(const A&); }; A x; A y(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, ByNonConstRRefArgument) { - auto AST = tooling::buildASTFromCode( - "void g(int&&); void f() { int x; g(static_cast(x)); }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), - ElementsAre("g(static_cast(x))")); - - AST = tooling::buildASTFromCode( - "void f() { struct A {}; A operator+(A&&, int); " - "A x; static_cast
(x) + 1; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), - ElementsAre("static_cast(x) + 1")); - - AST = tooling::buildASTFromCode("void f() { struct A { A(int&&); }; " - "int x; A y(static_cast(x)); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), - ElementsAre("static_cast(x)")); - - AST = tooling::buildASTFromCode("void f() { struct A { A(); A(A&&); }; " - "A x; A y(static_cast(x)); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), - ElementsAre("static_cast(x)")); -} - -TEST(ExprMutationAnalyzerTest, ByConstRRefArgument) { - auto AST = tooling::buildASTFromCode( - "void g(const int&&); void f() { int x; g(static_cast(x)); }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { struct A {}; A operator+(const A&&, int); " - "A x; static_cast(x) + 1; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode("void f() { struct A { A(const int&&); }; " - "int x; A y(static_cast(x)); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode("void f() { struct A { A(); A(const A&&); }; " - "A x; A y(static_cast(x)); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, Move) { - // Technically almost the same as ByNonConstRRefArgument, just double checking - const auto AST = tooling::buildASTFromCode( - "namespace std {" - "template struct remove_reference { typedef T type; };" - "template struct remove_reference { typedef T type; };" - "template struct remove_reference { typedef T type; };" - "template typename std::remove_reference::type&& " - "move(T&& t) noexcept; }" - "void f() { struct A {}; A x; std::move(x); }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x)")); -} - -TEST(ExprMutationAnalyzerTest, Forward) { - // Technically almost the same as ByNonConstRefArgument, just double checking - const auto AST = tooling::buildASTFromCode( - "namespace std {" - "template struct remove_reference { typedef T type; };" - "template struct remove_reference { typedef T type; };" - "template struct remove_reference { typedef T type; };" - "template T&& " - "forward(typename std::remove_reference::type&) noexcept;" - "template T&& " - "forward(typename std::remove_reference::type&&) noexcept;" - "void f() { struct A {}; A x; std::forward(x); }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), - ElementsAre("std::forward(x)")); -} - -TEST(ExprMutationAnalyzerTest, CallUnresolved) { - auto AST = tooling::buildASTFromCodeWithArgs( - "template void f() { T x; g(x); }", - {"-fno-delayed-template-parsing"}); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); - - AST = tooling::buildASTFromCodeWithArgs( - "template void f() { char x[N]; g(x); }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); - - AST = tooling::buildASTFromCodeWithArgs( - "template void f(T t) { int x; g(t, x); }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)")); - - AST = tooling::buildASTFromCodeWithArgs( - "template void f(T t) { int x; t.mf(x); }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)")); - - AST = tooling::buildASTFromCodeWithArgs( - "template struct S;" - "template void f() { S s; int x; s.mf(x); }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)")); - - AST = tooling::buildASTFromCodeWithArgs( - "struct S { template void mf(); };" - "template void f(S s) { int x; s.mf(x); }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)")); - - AST = tooling::buildASTFromCodeWithArgs("template " - "void g(F f) { int x; f(x); } ", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)")); - - AST = tooling::buildASTFromCodeWithArgs( - "template void f() { int x; (void)T(x); }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)")); -} - -TEST(ExprMutationAnalyzerTest, ReturnAsValue) { - auto AST = tooling::buildASTFromCode("int f() { int x; return x; }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode("int* f() { int* x; return x; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode("typedef int* IntPtr;" - "IntPtr f() { int* x; return x; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, ReturnAsNonConstRef) { - const auto AST = tooling::buildASTFromCode("int& f() { int x; return x; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("return x;")); -} - -TEST(ExprMutationAnalyzerTest, ReturnAsConstRef) { - const auto AST = - tooling::buildASTFromCode("const int& f() { int x; return x; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, ReturnAsNonConstRRef) { - const auto AST = tooling::buildASTFromCode( - "int&& f() { int x; return static_cast(x); }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), - ElementsAre("return static_cast(x);")); -} - -TEST(ExprMutationAnalyzerTest, ReturnAsConstRRef) { - const auto AST = tooling::buildASTFromCode( - "const int&& f() { int x; return static_cast(x); }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, TakeAddress) { - const auto AST = - tooling::buildASTFromCode("void g(int*); void f() { int x; g(&x); }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("&x")); -} - -TEST(ExprMutationAnalyzerTest, ArrayToPointerDecay) { - const auto AST = - tooling::buildASTFromCode("void g(int*); void f() { int x[2]; g(x); }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); -} - -TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) { - const auto AST = tooling::buildASTFromCodeWithArgs( - "template struct S { static constexpr int v = 8; };" - "template <> struct S { static constexpr int v = 4; };" - "void g(char*);" - "template void f() { char x[S::v]; g(x); }" - "template <> void f() { char y[S::v]; g(y); }", - {"-fno-delayed-template-parsing"}); - const auto ResultsX = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)")); - const auto ResultsY = - match(withEnclosingCompound(declRefTo("y")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y")); -} - -TEST(ExprMutationAnalyzerTest, FollowRefModified) { - auto AST = tooling::buildASTFromCode( - "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; " - "int& r3 = r2; r3 = 10; }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), - ElementsAre("r0", "r1", "r2", "r3", "r3 = 10")); - - AST = tooling::buildASTFromCode( - "typedef int& IntRefX;" - "using IntRefY = int&;" - "void f() { int x; IntRefX r0 = x; IntRefY r1 = r0;" - "decltype((x)) r2 = r1; r2 = 10; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), - ElementsAre("r0", "r1", "r2", "r2 = 10")); -} - -TEST(ExprMutationAnalyzerTest, FollowRefNotModified) { - auto AST = tooling::buildASTFromCode( - "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; " - "int& r3 = r2; int& r4 = r3; int& r5 = r4;}"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { int x; int& r0 = x; const int& r1 = r0;}"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "typedef const int& CIntRefX;" - "using CIntRefY = const int&;" - "void f() { int x; int& r0 = x; CIntRefX r1 = r0;" - "CIntRefY r2 = r1; decltype((r1)) r3 = r2;}"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, FollowConditionalRefModified) { - const auto AST = tooling::buildASTFromCode( - "void f() { int x, y; bool b; int &r = b ? x : y; r = 10; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("r", "r = 10")); -} - -TEST(ExprMutationAnalyzerTest, FollowConditionalRefNotModified) { - const auto AST = tooling::buildASTFromCode( - "void f() { int x, y; bool b; int& r = b ? x : y; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, ArrayElementModified) { - const auto AST = - tooling::buildASTFromCode("void f() { int x[2]; x[0] = 10; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x[0] = 10")); -} - -TEST(ExprMutationAnalyzerTest, ArrayElementNotModified) { - const auto AST = tooling::buildASTFromCode("void f() { int x[2]; x[0]; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, NestedMemberModified) { - auto AST = tooling::buildASTFromCode( - "void f() { struct A { int vi; }; struct B { A va; }; " - "struct C { B vb; }; C x; x.vb.va.vi = 10; }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.vb.va.vi = 10")); - - AST = tooling::buildASTFromCodeWithArgs( - "template void f() { T x; x.y.z = 10; }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10")); - - AST = tooling::buildASTFromCodeWithArgs( - "template struct S;" - "template void f() { S x; x.y.z = 10; }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10")); -} - -TEST(ExprMutationAnalyzerTest, NestedMemberNotModified) { - auto AST = tooling::buildASTFromCode( - "void f() { struct A { int vi; }; struct B { A va; }; " - "struct C { B vb; }; C x; x.vb.va.vi; }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCodeWithArgs( - "template void f() { T x; x.y.z; }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCodeWithArgs( - "template struct S;" - "template void f() { S x; x.y.z; }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, CastToValue) { - const auto AST = - tooling::buildASTFromCode("void f() { int x; static_cast(x); }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, CastToRefModified) { - auto AST = tooling::buildASTFromCode( - "void f() { int x; static_cast(x) = 10; }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), - ElementsAre("static_cast(x) = 10")); - - AST = tooling::buildASTFromCode( - "typedef int& IntRef;" - "void f() { int x; static_cast(x) = 10; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), - ElementsAre("static_cast(x) = 10")); -} - -TEST(ExprMutationAnalyzerTest, CastToRefNotModified) { - const auto AST = - tooling::buildASTFromCode("void f() { int x; static_cast(x); }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, CastToConstRef) { - auto AST = tooling::buildASTFromCode( - "void f() { int x; static_cast(x); }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = - tooling::buildASTFromCode("typedef const int& CIntRef;" - "void f() { int x; static_cast(x); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, LambdaDefaultCaptureByValue) { - const auto AST = - tooling::buildASTFromCode("void f() { int x; [=]() { x = 10; }; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, LambdaExplicitCaptureByValue) { - const auto AST = - tooling::buildASTFromCode("void f() { int x; [x]() { x = 10; }; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, LambdaDefaultCaptureByRef) { - const auto AST = - tooling::buildASTFromCode("void f() { int x; [&]() { x = 10; }; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), - ElementsAre(ResultOf(removeSpace, "[&](){x=10;}"))); -} - -TEST(ExprMutationAnalyzerTest, LambdaExplicitCaptureByRef) { - const auto AST = - tooling::buildASTFromCode("void f() { int x; [&x]() { x = 10; }; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), - ElementsAre(ResultOf(removeSpace, "[&x](){x=10;}"))); -} - -TEST(ExprMutationAnalyzerTest, RangeForArrayByRefModified) { - auto AST = tooling::buildASTFromCode( - "void f() { int x[2]; for (int& e : x) e = 10; }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("e", "e = 10")); - - AST = tooling::buildASTFromCode( - "typedef int& IntRef;" - "void f() { int x[2]; for (IntRef e : x) e = 10; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("e", "e = 10")); -} - -TEST(ExprMutationAnalyzerTest, RangeForArrayByRefNotModified) { - const auto AST = - tooling::buildASTFromCode("void f() { int x[2]; for (int& e : x) e; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, RangeForArrayByValue) { - auto AST = tooling::buildASTFromCode( - "void f() { int x[2]; for (int e : x) e = 10; }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { int* x[2]; for (int* e : x) e = nullptr; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "typedef int* IntPtr;" - "void f() { int* x[2]; for (IntPtr e : x) e = nullptr; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, RangeForArrayByConstRef) { - auto AST = tooling::buildASTFromCode( - "void f() { int x[2]; for (const int& e : x) e; }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "typedef const int& CIntRef;" - "void f() { int x[2]; for (CIntRef e : x) e; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, RangeForNonArrayByRefModified) { - const auto AST = - tooling::buildASTFromCode("struct V { int* begin(); int* end(); };" - "void f() { V x; for (int& e : x) e = 10; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("e", "e = 10")); -} - -TEST(ExprMutationAnalyzerTest, RangeForNonArrayByRefNotModified) { - const auto AST = - tooling::buildASTFromCode("struct V { int* begin(); int* end(); };" - "void f() { V x; for (int& e : x) e; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, RangeForNonArrayByValue) { - const auto AST = tooling::buildASTFromCode( - "struct V { const int* begin() const; const int* end() const; };" - "void f() { V x; for (int e : x) e; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, RangeForNonArrayByConstRef) { - const auto AST = tooling::buildASTFromCode( - "struct V { const int* begin() const; const int* end() const; };" - "void f() { V x; for (const int& e : x) e; }"); - const auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, UnevaluatedExpressions) { - auto AST = tooling::buildASTFromCode( - "void f() { int x, y; decltype(x = 10) z = y; }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { int x, y; __typeof(x = 10) z = y; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { int x, y; __typeof__(x = 10) z = y; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode("void f() { int x; sizeof(x = 10); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode("void f() { int x; alignof(x = 10); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode("void f() { int x; noexcept(x = 10); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCodeWithArgs("namespace std { class type_info; }" - "void f() { int x; typeid(x = 10); }", - {"-frtti"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode( - "void f() { int x; _Generic(x = 10, int: 0, default: 1); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); -} - -TEST(ExprMutationAnalyzerTest, NotUnevaluatedExpressions) { - auto AST = tooling::buildASTFromCode("void f() { int x; sizeof(int[x++]); }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x++")); - - AST = tooling::buildASTFromCodeWithArgs( - "namespace std { class type_info; }" - "struct A { virtual ~A(); }; struct B : A {};" - "struct X { A& f(); }; void f() { X x; typeid(x.f()); }", - {"-frtti"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.f()")); -} - -TEST(ExprMutationAnalyzerTest, UniquePtr) { - const std::string UniquePtrDef = - "template struct UniquePtr {" - " UniquePtr();" - " UniquePtr(const UniquePtr&) = delete;" - " UniquePtr(UniquePtr&&);" - " UniquePtr& operator=(const UniquePtr&) = delete;" - " UniquePtr& operator=(UniquePtr&&);" - " T& operator*() const;" - " T* operator->() const;" - "};"; - - auto AST = tooling::buildASTFromCode( - UniquePtrDef + "void f() { UniquePtr x; *x = 10; }"); - auto Results = - match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("* x = 10")); - - AST = tooling::buildASTFromCode(UniquePtrDef + - "void f() { UniquePtr x; *x; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode(UniquePtrDef + - "void f() { UniquePtr x; *x; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode(UniquePtrDef + - "struct S { int v; };" - "void f() { UniquePtr x; x->v; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); - - AST = tooling::buildASTFromCode(UniquePtrDef + - "struct S { int v; };" - "void f() { UniquePtr x; x->v; }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCode(UniquePtrDef + - "struct S { void mf(); };" - "void f() { UniquePtr x; x->mf(); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); - - AST = tooling::buildASTFromCode( - UniquePtrDef + "struct S { void mf() const; };" - "void f() { UniquePtr x; x->mf(); }"); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_FALSE(isMutated(Results, AST.get())); - - AST = tooling::buildASTFromCodeWithArgs( - UniquePtrDef + "template void f() { UniquePtr x; x->mf(); }", - {"-fno-delayed-template-parsing"}); - Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); - EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x->mf()")); -} - -} // namespace test -} // namespace tidy -} // namespace clang From 08df45ee0c271495e41110875589415d363065c0 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Wed, 12 Sep 2018 00:32:13 +0000 Subject: [PATCH 180/686] [NFC] Fix build breakage due to missing dep caused by D51950 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342012 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/performance/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/clang-tidy/performance/CMakeLists.txt b/clang-tidy/performance/CMakeLists.txt index ac417bcc8..b6302a5ff 100644 --- a/clang-tidy/performance/CMakeLists.txt +++ b/clang-tidy/performance/CMakeLists.txt @@ -18,6 +18,7 @@ add_clang_library(clangTidyPerformanceModule LINK_LIBS clangAST clangASTMatchers + clangAnalysis clangBasic clangLex clangTidy From 8e1fbe1740919bb2319002cb508388c1ce5900cc Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Wed, 12 Sep 2018 07:32:54 +0000 Subject: [PATCH 181/686] [clangd] Implement a Proof-of-Concept tool for symbol index exploration Reviewed By: sammccall, ilya-biryukov Differential Revision: https://reviews.llvm.org/D51628 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342025 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 1 + clangd/index/dex/dexp/CMakeLists.txt | 15 +++ clangd/index/dex/dexp/Dexp.cpp | 161 +++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 clangd/index/dex/dexp/CMakeLists.txt create mode 100644 clangd/index/dex/dexp/Dexp.cpp diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index eb6ed7223..98e2e2aef 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -74,3 +74,4 @@ if( LLVM_LIB_FUZZING_ENGINE OR LLVM_USE_SANITIZE_COVERAGE ) endif() add_subdirectory(tool) add_subdirectory(global-symbol-builder) +add_subdirectory(index/dex/dexp) diff --git a/clangd/index/dex/dexp/CMakeLists.txt b/clangd/index/dex/dexp/CMakeLists.txt new file mode 100644 index 000000000..34798f4f9 --- /dev/null +++ b/clangd/index/dex/dexp/CMakeLists.txt @@ -0,0 +1,15 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../) + +set(LLVM_LINK_COMPONENTS + LineEditor + Support + ) + +add_clang_executable(dexp + Dexp.cpp + ) + +target_link_libraries(dexp + PRIVATE + clangDaemon + ) diff --git a/clangd/index/dex/dexp/Dexp.cpp b/clangd/index/dex/dexp/Dexp.cpp new file mode 100644 index 000000000..c3c6307d3 --- /dev/null +++ b/clangd/index/dex/dexp/Dexp.cpp @@ -0,0 +1,161 @@ +//===--- Dexp.cpp - Dex EXPloration tool ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements a simple interactive tool which can be used to manually +// evaluate symbol search quality of Clangd index. +// +//===----------------------------------------------------------------------===// + +#include "../../../index/SymbolYAML.h" +#include "../Dex.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/LineEditor/LineEditor.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Signals.h" + +using clang::clangd::FuzzyFindRequest; +using clang::clangd::loadIndex; +using clang::clangd::Symbol; +using clang::clangd::SymbolIndex; +using llvm::StringRef; + +namespace { + +llvm::cl::opt + SymbolCollection("symbol-collection-file", + llvm::cl::desc("Path to the file with symbol collection"), + llvm::cl::Positional, llvm::cl::Required); + +static const std::string Overview = R"( +This is an **experimental** interactive tool to process user-provided search +queries over given symbol collection obtained via global-symbol-builder. The +tool can be used to evaluate search quality of existing index implementations +and manually construct non-trivial test cases. + +Type use "help" request to get information about the details. +)"; + +void reportTime(StringRef Name, llvm::function_ref F) { + const auto TimerStart = std::chrono::high_resolution_clock::now(); + F(); + const auto TimerStop = std::chrono::high_resolution_clock::now(); + const auto Duration = std::chrono::duration_cast( + TimerStop - TimerStart); + llvm::outs() << llvm::formatv("{0} took {1:ms+n}.\n", Name, Duration); +} + +void fuzzyFind(llvm::StringRef UnqualifiedName, const SymbolIndex &Index) { + FuzzyFindRequest Request; + Request.MaxCandidateCount = 10; + Request.Query = UnqualifiedName; + // FIXME(kbobyrev): Print symbol final scores to see the distribution. + static const auto OutputFormat = "{0,-4} | {1,-40} | {2,-25}\n"; + llvm::outs() << llvm::formatv(OutputFormat, "Rank", "Symbol ID", + "Symbol Name"); + size_t Rank = 0; + Index.fuzzyFind(Request, [&](const Symbol &Sym) { + llvm::outs() << llvm::formatv(OutputFormat, Rank++, Sym.ID.str(), Sym.Name); + }); +} + +static const std::string HelpMessage = R"(dexp commands: + +> find Name + +Constructs fuzzy find request given unqualified symbol name and returns top 10 +symbols retrieved from index. + +> lookup SymbolID + +Retrieves symbol names given USR. +)"; + +void help() { llvm::outs() << HelpMessage; } + +void lookup(StringRef USR, const SymbolIndex &Index) { + llvm::DenseSet IDs{clang::clangd::SymbolID{USR}}; + clang::clangd::LookupRequest Request{IDs}; + bool FoundSymbol = false; + Index.lookup(Request, [&](const Symbol &Sym) { + if (!FoundSymbol) + FoundSymbol = true; + llvm::outs() << SymbolToYAML(Sym); + }); + if (!FoundSymbol) + llvm::outs() << "not found\n"; +} + +// FIXME(kbobyrev): Make this an actual REPL: probably use LLVM Command Line +// library for parsing flags and arguments. +// FIXME(kbobyrev): Ideas for commands: +// * symbol lookup: print out symbol in YAML format given SymbolID +// * find symbol references: print set of reference locations +// * load/swap/reload index: this would make it possible to get rid of llvm::cl +// usages in the tool driver and actually use llvm::cl library in the REPL. +// * show posting list density histogram (our dump data somewhere so that user +// could build one) +// * show number of tokens of each kind +// * print out tokens with the most dense posting lists +// * print out tokens with least dense posting lists +void dispatch(StringRef Request, const SymbolIndex &Index) { + llvm::SmallVector Arguments; + Request.split(Arguments, ' '); + if (Arguments.empty()) { + llvm::outs() << "Request can not be empty.\n"; + help(); + return; + } + + if (Arguments.front() == "find") { + if (Arguments.size() != 2) { + llvm::outs() << "find request must specify unqualified symbol name.\n"; + return; + } + reportTime("fuzzy find request", + [&]() { fuzzyFind(Arguments.back(), Index); }); + } else if (Arguments.front() == "lookup") { + if (Arguments.size() != 2) { + llvm::outs() << "lookup request must specify symbol ID .\n"; + return; + } + reportTime("lookup request", [&]() { lookup(Arguments.back(), Index); }); + } else if (Arguments.front() == "help") { + help(); + } else { + llvm::outs() << "Unknown command. Try 'help'\n"; + } +} + +} // namespace + +int main(int argc, const char *argv[]) { + llvm::cl::ParseCommandLineOptions(argc, argv, Overview); + llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); + + std::unique_ptr Index; + reportTime("Dex build", [&]() { + Index = loadIndex(SymbolCollection, /*URISchemes=*/{}, + /*UseDex=*/true); + }); + + if (!Index) { + llvm::outs() + << "ERROR: Please provide a valid path to symbol collection file.\n"; + return -1; + } + + llvm::LineEditor LE("dexp"); + + while (llvm::Optional Request = LE.readLine()) + dispatch(Request.getValue(), *Index); + + return 0; +} From f723f89d0ef97aabf302faedf8c9f11367615346 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Wed, 12 Sep 2018 07:49:44 +0000 Subject: [PATCH 182/686] [clangd] Add index benchmarks This patch introduces index benchmarks on top of the proposed LLVM benchmark pull. Reviewed By: sammccall, lebedev.ri Differential Revision: https://reviews.llvm.org/D51090 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342026 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 4 + clangd/benchmarks/CMakeLists.txt | 9 ++ clangd/benchmarks/IndexBenchmark.cpp | 114 +++++++++++++++++++++++++ test/clangd/Inputs/BenchmarkHeader.h | 19 +++++ test/clangd/Inputs/BenchmarkSource.cpp | 1 + test/clangd/Inputs/requests.log | 5 ++ test/clangd/index-tools.test | 2 + test/lit.cfg | 6 ++ 8 files changed, 160 insertions(+) create mode 100644 clangd/benchmarks/CMakeLists.txt create mode 100644 clangd/benchmarks/IndexBenchmark.cpp create mode 100644 test/clangd/Inputs/BenchmarkHeader.h create mode 100644 test/clangd/Inputs/BenchmarkSource.cpp create mode 100644 test/clangd/Inputs/requests.log create mode 100644 test/clangd/index-tools.test diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index 98e2e2aef..3cdb1e113 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -75,3 +75,7 @@ endif() add_subdirectory(tool) add_subdirectory(global-symbol-builder) add_subdirectory(index/dex/dexp) + +if (LLVM_INCLUDE_BENCHMARKS) + add_subdirectory(benchmarks) +endif() diff --git a/clangd/benchmarks/CMakeLists.txt b/clangd/benchmarks/CMakeLists.txt new file mode 100644 index 000000000..1f3d88b42 --- /dev/null +++ b/clangd/benchmarks/CMakeLists.txt @@ -0,0 +1,9 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../) + +add_benchmark(IndexBenchmark IndexBenchmark.cpp) + +target_link_libraries(IndexBenchmark + PRIVATE + clangDaemon + LLVMSupport + ) diff --git a/clangd/benchmarks/IndexBenchmark.cpp b/clangd/benchmarks/IndexBenchmark.cpp new file mode 100644 index 000000000..44af02023 --- /dev/null +++ b/clangd/benchmarks/IndexBenchmark.cpp @@ -0,0 +1,114 @@ +//===--- IndexBenchmark.cpp - Clangd index benchmarks -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../index/SymbolYAML.h" +#include "../index/dex/Dex.h" +#include "benchmark/benchmark.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Regex.h" +#include +#include +#include + +const char *IndexFilename; +const char *LogFilename; + +namespace clang { +namespace clangd { +namespace { + +std::unique_ptr buildMem() { + return clang::clangd::loadIndex(IndexFilename, {}, false); +} + +std::unique_ptr buildDex() { + return clang::clangd::loadIndex(IndexFilename, {}, true); +} + +// This function processes user-provided Log file with fuzzy find requests in +// the following format: +// +// fuzzyFind("UnqualifiedName", scopes=["clang::", "clang::clangd::"]) +// +// It constructs vector of FuzzyFindRequests which is later used for the +// benchmarks. +std::vector extractQueriesFromLogs() { + llvm::Regex RequestMatcher("fuzzyFind\\(\"([a-zA-Z]*)\", scopes=\\[(.*)\\]"); + llvm::SmallVector Matches; + std::ifstream InputStream(LogFilename); + std::string Log((std::istreambuf_iterator(InputStream)), + std::istreambuf_iterator()); + llvm::StringRef Temporary(Log); + llvm::SmallVector Strings; + Temporary.split(Strings, '\n'); + + clang::clangd::FuzzyFindRequest R; + R.MaxCandidateCount = 100; + + llvm::SmallVector CommaSeparatedValues; + + std::vector RealRequests; + for (auto Line : Strings) { + if (RequestMatcher.match(Line, &Matches)) { + R.Query = Matches[1]; + CommaSeparatedValues.clear(); + Line.split(CommaSeparatedValues, ','); + R.Scopes.clear(); + for (auto C : CommaSeparatedValues) { + R.Scopes.push_back(C); + } + RealRequests.push_back(R); + } + } + return RealRequests; +} + +static void MemQueries(benchmark::State &State) { + const auto Mem = buildMem(); + const auto Requests = extractQueriesFromLogs(); + for (auto _ : State) + for (const auto &Request : Requests) + Mem->fuzzyFind(Request, [](const Symbol &S) {}); +} +BENCHMARK(MemQueries); + +static void DexQueries(benchmark::State &State) { + const auto Dex = buildDex(); + const auto Requests = extractQueriesFromLogs(); + for (auto _ : State) + for (const auto &Request : Requests) + Dex->fuzzyFind(Request, [](const Symbol &S) {}); +} +BENCHMARK(DexQueries); + +} // namespace +} // namespace clangd +} // namespace clang + +// FIXME(kbobyrev): Add index building time benchmarks. +// FIXME(kbobyrev): Add memory consumption "benchmarks" by manually measuring +// in-memory index size and reporting it as time. +// FIXME(kbobyrev): Create a logger wrapper to suppress debugging info printer. +int main(int argc, char *argv[]) { + if (argc < 3) { + llvm::errs() << "Usage: " << argv[0] + << " global-symbol-index.yaml fuzzy-find-requests.log " + "BENCHMARK_OPTIONS...\n"; + return -1; + } + IndexFilename = argv[1]; + LogFilename = argv[2]; + // Trim first two arguments of the benchmark invocation. + argv += 3; + argc -= 3; + ::benchmark::Initialize(&argc, argv); + ::benchmark::RunSpecifiedBenchmarks(); +} diff --git a/test/clangd/Inputs/BenchmarkHeader.h b/test/clangd/Inputs/BenchmarkHeader.h new file mode 100644 index 000000000..3b7620ada --- /dev/null +++ b/test/clangd/Inputs/BenchmarkHeader.h @@ -0,0 +1,19 @@ +namespace clang { +namespace clangd { +namespace dex { +class Dex; +} // namespace dex +} // namespace clangd +} // namespace clang + +namespace llvm { +namespace sys { + +int getHostNumPhysicalCores(); + +} // namespace sys +} // namespace llvm + +namespace { +int Variable; +} // namespace diff --git a/test/clangd/Inputs/BenchmarkSource.cpp b/test/clangd/Inputs/BenchmarkSource.cpp new file mode 100644 index 000000000..1924df9a7 --- /dev/null +++ b/test/clangd/Inputs/BenchmarkSource.cpp @@ -0,0 +1 @@ +#include "BenchmarkHeader.h" diff --git a/test/clangd/Inputs/requests.log b/test/clangd/Inputs/requests.log new file mode 100644 index 000000000..f0e669516 --- /dev/null +++ b/test/clangd/Inputs/requests.log @@ -0,0 +1,5 @@ +V[09:08:34.301] Code complete: fuzzyFind("s", scopes=[llvm::]) +V[09:08:34.368] Code complete: fuzzyFind("sy", scopes=[llvm::]) +V[09:08:34.442] Code complete: fuzzyFind("sys", scopes=[llvm::]) +V[09:09:12.075] Code complete: fuzzyFind("Dex", scopes=[clang::clangd::, clang::, clang::clangd::dex::]) +V[09:09:12.075] Code complete: fuzzyFind("Variable", scopes=[]) diff --git a/test/clangd/index-tools.test b/test/clangd/index-tools.test new file mode 100644 index 000000000..56b9c9eaf --- /dev/null +++ b/test/clangd/index-tools.test @@ -0,0 +1,2 @@ +# RUN: global-symbol-builder %p/Inputs/BenchmarkSource.cpp -- -I%p/Inputs > %t.index +# RUN: %clangd-benchmark-dir/IndexBenchmark %t.index %p/Inputs/requests.log --benchmark_min_time=0.01 diff --git a/test/lit.cfg b/test/lit.cfg index 4b4718a02..8e755c879 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -137,3 +137,9 @@ if config.clang_staticanalyzer: else: # exclude the clang-tidy test directory config.excludes.append('clang-tidy') + +clangd_benchmarks_dir = os.path.join(os.path.dirname(config.clang_tools_dir), + "tools", "clang", "tools", "extra", + "clangd", "benchmarks") +config.substitutions.append(('%clangd-benchmark-dir', + '%s' % (clangd_benchmarks_dir))) From 197c341ed1bd98b3d855f3d37e39eb2186b56396 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Wed, 12 Sep 2018 09:27:55 +0000 Subject: [PATCH 183/686] Fix buildbots after r342027 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342036 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clangd/index-tools.test | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/clangd/index-tools.test b/test/clangd/index-tools.test index 56b9c9eaf..48dbf59e0 100644 --- a/test/clangd/index-tools.test +++ b/test/clangd/index-tools.test @@ -1,2 +1,3 @@ # RUN: global-symbol-builder %p/Inputs/BenchmarkSource.cpp -- -I%p/Inputs > %t.index -# RUN: %clangd-benchmark-dir/IndexBenchmark %t.index %p/Inputs/requests.log --benchmark_min_time=0.01 +# FIXME: By default, benchmarks are excluded from the list of default targets hence not built. Find a way to depend on benchmarks to run the next command. +# RUN: if [ -f %clangd-benchmark-dir/IndexBenchmark ]; then %clangd-benchmark-dir/IndexBenchmark %t.index %p/Inputs/requests.log --benchmark_min_time=0.01 ; fi From d1b64ac186792b1c2e5eecbab37bac6e62568955 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Wed, 12 Sep 2018 09:40:13 +0000 Subject: [PATCH 184/686] [clangd] Add missing clangBasic target_link_libraries Without this, builds with `-DSHARED_LIB=ON` fail. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342037 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/dexp/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/clangd/index/dex/dexp/CMakeLists.txt b/clangd/index/dex/dexp/CMakeLists.txt index 34798f4f9..ece339d70 100644 --- a/clangd/index/dex/dexp/CMakeLists.txt +++ b/clangd/index/dex/dexp/CMakeLists.txt @@ -11,5 +11,6 @@ add_clang_executable(dexp target_link_libraries(dexp PRIVATE + clangBasic clangDaemon ) From 320c6b65c7e4f84fbea05e4884115317a8de5558 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Wed, 12 Sep 2018 10:04:16 +0000 Subject: [PATCH 185/686] [clang-tidy] Abseil: Add more directories that are slated for future absl expansion. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342041 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/AbseilMatcher.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/clang-tidy/abseil/AbseilMatcher.h b/clang-tidy/abseil/AbseilMatcher.h index e56720499..116aa953d 100644 --- a/clang-tidy/abseil/AbseilMatcher.h +++ b/clang-tidy/abseil/AbseilMatcher.h @@ -49,9 +49,10 @@ AST_POLYMORPHIC_MATCHER( return false; Path = Path.drop_front(PrefixPosition + AbslPrefix.size()); static const char *AbseilLibraries[] = { - "algorithm", "base", "container", "debugging", "flags", - "memory", "meta", "numeric", "strings", "synchronization", - "time", "types", "utility"}; + "algorithm", "base", "container", "debugging", "flags", + "hash", "iterator", "memory", "meta", "numeric", + "random", "strings", "synchronization", "time", "types", + "utility"}; return std::any_of( std::begin(AbseilLibraries), std::end(AbseilLibraries), [&](const char *Library) { return Path.startswith(Library); }); From 9588776f46d26da14c224ffd526ec04bcb3899c2 Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Wed, 12 Sep 2018 12:56:58 +0000 Subject: [PATCH 186/686] Fix MSVC "not all control paths return a value" warnings. NFCI. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342052 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/MDGenerator.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang-doc/MDGenerator.cpp b/clang-doc/MDGenerator.cpp index f6c173c08..2f523e439 100644 --- a/clang-doc/MDGenerator.cpp +++ b/clang-doc/MDGenerator.cpp @@ -32,6 +32,7 @@ std::string getAccess(AccessSpecifier AS) { case AccessSpecifier::AS_none: return {}; } + llvm_unreachable("Unknown AccessSpecifier"); } std::string getTagType(TagTypeKind AS) { @@ -47,6 +48,7 @@ std::string getTagType(TagTypeKind AS) { case TagTypeKind::TTK_Enum: return "enum"; } + llvm_unreachable("Unknown TagTypeKind"); } // Markdown generation From 27e42f2a4cc76a9299efb70b12bc12d408b48629 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Thu, 13 Sep 2018 09:44:11 +0000 Subject: [PATCH 187/686] [clangd] Rename global-symbol-builder to clangd-indexer. Summary: Given that the indexer binary is put directly into ./bin directory when built, 'clangd-' prefix seems to provide better context to the reader than 'global-'. The new name is also shorter and easier to type. Reviewers: ioeric, sammccall, kadircet Reviewed By: ioeric, sammccall Subscribers: kbobyrev, ilya-biryukov, mgorny, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D51987 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342123 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 2 +- clangd/{global-symbol-builder => indexer}/CMakeLists.txt | 6 +++--- .../GlobalSymbolBuilderMain.cpp => indexer/IndexerMain.cpp} | 4 ++-- test/CMakeLists.txt | 2 +- test/clangd/index-tools.test | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) rename clangd/{global-symbol-builder => indexer}/CMakeLists.txt (64%) rename clangd/{global-symbol-builder/GlobalSymbolBuilderMain.cpp => indexer/IndexerMain.cpp} (98%) diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index 3cdb1e113..7991aba89 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -73,7 +73,7 @@ if( LLVM_LIB_FUZZING_ENGINE OR LLVM_USE_SANITIZE_COVERAGE ) add_subdirectory(fuzzer) endif() add_subdirectory(tool) -add_subdirectory(global-symbol-builder) +add_subdirectory(indexer) add_subdirectory(index/dex/dexp) if (LLVM_INCLUDE_BENCHMARKS) diff --git a/clangd/global-symbol-builder/CMakeLists.txt b/clangd/indexer/CMakeLists.txt similarity index 64% rename from clangd/global-symbol-builder/CMakeLists.txt rename to clangd/indexer/CMakeLists.txt index c81da16ed..5ba5b6773 100644 --- a/clangd/global-symbol-builder/CMakeLists.txt +++ b/clangd/indexer/CMakeLists.txt @@ -4,11 +4,11 @@ set(LLVM_LINK_COMPONENTS Support ) -add_clang_executable(global-symbol-builder - GlobalSymbolBuilderMain.cpp +add_clang_executable(clangd-indexer + IndexerMain.cpp ) -target_link_libraries(global-symbol-builder +target_link_libraries(clangd-indexer PRIVATE clangAST clangIndex diff --git a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp b/clangd/indexer/IndexerMain.cpp similarity index 98% rename from clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp rename to clangd/indexer/IndexerMain.cpp index e0333f589..918b58789 100644 --- a/clangd/global-symbol-builder/GlobalSymbolBuilderMain.cpp +++ b/clangd/indexer/IndexerMain.cpp @@ -225,11 +225,11 @@ int main(int argc, const char **argv) { Example usage for building index for the whole project using CMake compile commands: - $ global-symbol-builder --executor=all-TUs compile_commands.json > index.yaml + $ clangd-indexer --executor=all-TUs compile_commands.json > index.yaml Example usage for file sequence index without flags: - $ global-symbol-builder File1.cpp File2.cpp ... FileN.cpp > index.yaml + $ clangd-indexer File1.cpp File2.cpp ... FileN.cpp > index.yaml Note: only symbols from header files will be collected. )"; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fafe6c0cf..e0ad2c3b6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -55,7 +55,7 @@ set(CLANG_TOOLS_TEST_DEPS # These individual tools have no tests, add them here to make them compile # together with check-clang-tools, so that we won't break them in the future. - global-symbol-builder + clangd-indexer # Unit tests ExtraToolsUnitTests diff --git a/test/clangd/index-tools.test b/test/clangd/index-tools.test index 48dbf59e0..b01d5bcd1 100644 --- a/test/clangd/index-tools.test +++ b/test/clangd/index-tools.test @@ -1,3 +1,3 @@ -# RUN: global-symbol-builder %p/Inputs/BenchmarkSource.cpp -- -I%p/Inputs > %t.index +# RUN: clangd-indexer %p/Inputs/BenchmarkSource.cpp -- -I%p/Inputs > %t.index # FIXME: By default, benchmarks are excluded from the list of default targets hence not built. Find a way to depend on benchmarks to run the next command. # RUN: if [ -f %clangd-benchmark-dir/IndexBenchmark ]; then %clangd-benchmark-dir/IndexBenchmark %t.index %p/Inputs/requests.log --benchmark_min_time=0.01 ; fi From a0217bd6e7cb6399889de5748828e1106ab3b32c Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 13 Sep 2018 10:02:48 +0000 Subject: [PATCH 188/686] [clangd] Don't create child AND and OR iterators with one posting list `AND( AND( Child ) ... )` -> `AND( Child ... )` `AND( OR( Child ) ... )` -> `AND( Child ... )` This simple optimization results in 5-6% performance improvement in the benchmark with 2000 serialized `FuzzyFindRequest`s. Reviewed By: ilya-biryukov Differential Revision: https://reviews.llvm.org/D52016 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342124 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index 5cbd0f490..8d5527205 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -198,7 +198,7 @@ class OrIterator : public Iterator { public: explicit OrIterator(std::vector> AllChildren) : Children(std::move(AllChildren)) { - assert(Children.size() > 0 && "OR iterator must have at least one child."); + assert(!Children.empty() && "OR iterator should have at least one child."); } /// Returns true if all children are exhausted. @@ -405,12 +405,16 @@ std::unique_ptr create(PostingListRef Documents) { std::unique_ptr createAnd(std::vector> Children) { - return llvm::make_unique(move(Children)); + // If there is exactly one child, pull it one level up: AND(Child) -> Child. + return Children.size() == 1 ? std::move(Children.front()) + : llvm::make_unique(move(Children)); } std::unique_ptr createOr(std::vector> Children) { - return llvm::make_unique(move(Children)); + // If there is exactly one child, pull it one level up: OR(Child) -> Child. + return Children.size() == 1 ? std::move(Children.front()) + : llvm::make_unique(move(Children)); } std::unique_ptr createTrue(DocID Size) { From 1711cfc12d1f81fca431c248198e9c9629f22a19 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 13 Sep 2018 11:40:12 +0000 Subject: [PATCH 189/686] [docs] Provide pointers to known editor plugins and extensions Many editors provide extensions and plugins with LSP Client functionality. Many of these are known to work with Clangd, this patch points users to the relevant resources for better experience. Reviewed By: ioeric, ilya-biryukov Differential Revision git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342129 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/clangd.rst | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/clangd.rst b/docs/clangd.rst index e2d18fd41..cd0599557 100644 --- a/docs/clangd.rst +++ b/docs/clangd.rst @@ -108,6 +108,41 @@ extension to the protocol. | Gen. Getters/Setters | No | No | +-------------------------------------+------------+----------+ +Editor Integration +================== + +Any full-featured Language Server Protocol Client implementation should work +with :program:`Clangd`. This `list +` contains information about +extensions and plugins that are known to work for different editors. + +Vim Integration +--------------- + +LanguageClient-neovim +~~~~~~~~~~~~~~~~~~~~~ + +One of the options of using :program:`Clangd` in :program:`vim` (or +:program:`nvim`) is to utilize `LanguageClient-neovim +`_ plugin. Please see the +`Clangd Wiki page +`_ for +instructions. + +VSCode Integration +------------------ + +:program:`VSCode` provides `vscode-clangd +` +which is published in Visual Studio Marketplace and can be installed direcetly +from :program:`VSCode`. + +Emacs Integration +----------------- + +:program:`Emacs` provides `lsp-mode ` and +`Eglot ` plugins for LSP integration. + Getting Involved ================== From f0874bf30a93e5d15ebf3e5259f90cf741d246d1 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 13 Sep 2018 11:47:48 +0000 Subject: [PATCH 190/686] [clangd] Simplify cancellation public API Summary: Task is no longer exposed: - task cancellation is hidden as a std::function - task creation returns the new context directly - checking is via free function only, with no way to avoid the context lookup The implementation is essentially the same, but a bit terser as it's hidden. isCancelled() is now safe to use outside any task (it returns false). This will leave us free to sprinkle cancellation in e.g. TUScheduler without needing elaborate test setup, and lets callers that don't cancel "just work". Updated the docs to describe the new expected use pattern. One thing I noticed: there's nothing async-specific about the cancellation. Async tasks can be cancelled from any thread (typically the one that created them), sync tasks can be cancelled from any *other* thread in the same way. So the docs now refer to "long-running" tasks instead of async ones. Updated usage in code complete, without any structural changes. I didn't update all the names of the helpers in ClangdLSPServer (these will likely be moved to JSONRPCDispatcher anyway). Reviewers: ilya-biryukov, kadircet Subscribers: ioeric, MaskRay, jkorous, arphaman, jfb, cfe-commits Differential Revision: https://reviews.llvm.org/D51996 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342130 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/Cancellation.cpp | 22 ++-- clangd/Cancellation.h | 152 +++++++++---------------- clangd/ClangdLSPServer.cpp | 9 +- clangd/ClangdLSPServer.h | 8 +- clangd/ClangdServer.cpp | 12 +- clangd/ClangdServer.h | 6 +- unittests/clangd/CancellationTests.cpp | 41 +++---- 7 files changed, 99 insertions(+), 151 deletions(-) diff --git a/clangd/Cancellation.cpp b/clangd/Cancellation.cpp index a74097583..ae2354bbe 100644 --- a/clangd/Cancellation.cpp +++ b/clangd/Cancellation.cpp @@ -13,21 +13,21 @@ namespace clang { namespace clangd { -namespace { -static Key TaskKey; -} // namespace - char CancelledError::ID = 0; +static Key>> FlagKey; -const Task &getCurrentTask() { - const auto TH = Context::current().getExisting(TaskKey); - assert(TH && "Fetched a nullptr for TaskHandle from context."); - return *TH; +std::pair cancelableTask() { + auto Flag = std::make_shared>(); + return { + Context::current().derive(FlagKey, Flag), + [Flag] { *Flag = true; }, + }; } -Context setCurrentTask(ConstTaskHandle TH) { - assert(TH && "Trying to stash a nullptr as TaskHandle into context."); - return Context::current().derive(TaskKey, std::move(TH)); +bool isCancelled() { + if (auto *Flag = Context::current().get(FlagKey)) + return **Flag; + return false; // Not in scope of a task. } } // namespace clangd diff --git a/clangd/Cancellation.h b/clangd/Cancellation.h index 58f845691..b6d60508d 100644 --- a/clangd/Cancellation.h +++ b/clangd/Cancellation.h @@ -6,124 +6,82 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// Cancellation mechanism for async tasks. Roughly all the clients of this code -// can be classified into three categories: -// 1. The code that creates and schedules async tasks, e.g. TUScheduler. -// 2. The callers of the async method that can cancel some of the running tasks, -// e.g. `ClangdLSPServer` -// 3. The code running inside the async task itself, i.e. code completion or -// find definition implementation that run clang, etc. +// Cancellation mechanism for long-running tasks. // -// For (1), the guideline is to accept a callback for the result of async -// operation and return a `TaskHandle` to allow cancelling the request. +// This manages interactions between: // -// TaskHandle someAsyncMethod(Runnable T, -// function)> Callback) { -// auto TH = Task::createHandle(); -// WithContext ContextWithCancellationToken(TH); -// auto run = [](){ -// Callback(T()); +// 1. Client code that starts some long-running work, and maybe cancels later. +// +// std::pair Task = cancelableTask(); +// { +// WithContext Cancelable(std::move(Task.first)); +// Expected +// deepThoughtAsync([](int answer){ errs() << answer; }); // } -// // Start run() in a new async thread, and make sure to propagate Context. -// return TH; -// } +// // ...some time later... +// if (User.fellAsleep()) +// Task.second(); +// +// (This example has an asynchronous computation, but synchronous examples +// work similarly - the Canceler should be invoked from another thread). +// +// 2. Library code that executes long-running work, and can exit early if the +// result is not needed. // -// The callers of async methods (2) can issue cancellations and should be -// prepared to handle `TaskCancelledError` result: +// void deepThoughtAsync(std::function Callback) { +// runAsync([Callback]{ +// int A = ponder(6); +// if (isCancelled()) +// return; +// int B = ponder(9); +// if (isCancelled()) +// return; +// Callback(A * B); +// }); +// } // -// void Caller() { -// // You should store this handle if you wanna cancel the task later on. -// TaskHandle TH = someAsyncMethod(Task, [](llvm::Expected R) { -// if(/*check for task cancellation error*/) -// // Handle the error -// // Do other things on R. -// }); -// // To cancel the task: -// sleep(5); -// TH->cancel(); -// } +// (A real example may invoke the callback with an error on cancellation, +// the CancelledError is provided for this purpose). // -// The worker code itself (3) should check for cancellations using -// `Task::isCancelled` that can be retrieved via `getCurrentTask()`. +// Cancellation has some caveats: +// - the work will only stop when/if the library code next checks for it. +// Code outside clangd such as Sema will not do this. +// - it's inherently racy: client code must be prepared to accept results +// even after requesting cancellation. +// - it's Context-based, so async work must be dispatched to threads in +// ways that preserve the context. (Like runAsync() or TUScheduler). // -// llvm::Expected AsyncTask() { -// // You can either store the read only TaskHandle by calling getCurrentTask -// // once and just use the variable everytime you want to check for -// // cancellation, or call isCancelled everytime. The former is more -// // efficient if you are going to have multiple checks. -// const auto T = getCurrentTask(); -// // DO SMTHNG... -// if(T.isCancelled()) { -// // Task has been cancelled, lets get out. -// return llvm::makeError(); -// } -// // DO SOME MORE THING... -// if(T.isCancelled()) { -// // Task has been cancelled, lets get out. -// return llvm::makeError(); -// } -// return ResultType(...); -// } -// If the operation was cancelled before task could run to completion, it should -// propagate the TaskCancelledError as a result. +// FIXME: We could add timestamps to isCancelled() and CancelledError. +// Measuring the start -> cancel -> acknowledge -> finish timeline would +// help find where libraries' cancellation should be improved. #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CANCELLATION_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CANCELLATION_H #include "Context.h" #include "llvm/Support/Error.h" -#include -#include +#include #include namespace clang { namespace clangd { -/// Enables signalling a cancellation on an async task or checking for -/// cancellation. It is thread-safe to trigger cancellation from multiple -/// threads or check for cancellation. Task object for the currently running -/// task can be obtained via clangd::getCurrentTask(). -class Task { -public: - void cancel() { CT = true; } - /// If cancellation checks are rare, one could use the isCancelled() helper in - /// the namespace to simplify the code. However, if cancellation checks are - /// frequent, the guideline is first obtain the Task object for the currently - /// running task with getCurrentTask() and do cancel checks using it to avoid - /// extra lookups in the Context. - bool isCancelled() const { return CT; } - - /// Creates a task handle that can be used by an async task to check for - /// information that can change during it's runtime, like Cancellation. - static std::shared_ptr createHandle() { - return std::shared_ptr(new Task()); - } - - Task(const Task &) = delete; - Task &operator=(const Task &) = delete; - Task(Task &&) = delete; - Task &operator=(Task &&) = delete; - -private: - Task() : CT(false) {} - std::atomic CT; -}; -using ConstTaskHandle = std::shared_ptr; -using TaskHandle = std::shared_ptr; - -/// Fetches current task information from Context. TaskHandle must have been -/// stashed into context beforehand. -const Task &getCurrentTask(); +/// A canceller requests cancellation of a task, when called. +/// Calling it again has no effect. +using Canceler = std::function; -/// Stashes current task information within the context. -LLVM_NODISCARD Context setCurrentTask(ConstTaskHandle TH); +/// Defines a new task whose cancellation may be requested. +/// The returned Context defines the scope of the task. +/// When the context is active, isCancelled() is false until the Canceler is +/// invoked, and true afterwards. +std::pair cancelableTask(); -/// Checks whether the current task has been cancelled or not. -/// Consider storing the task handler returned by getCurrentTask and then -/// calling isCancelled through it. getCurrentTask is expensive since it does a -/// lookup in the context. -inline bool isCancelled() { return getCurrentTask().isCancelled(); } +/// True if the current context is within a cancelable task which was cancelled. +/// Always false if there is no active cancelable task. +/// This isn't free (context lookup) - don't call it in a tight loop. +bool isCancelled(); +/// Conventional error when no result is returned due to cancellation. class CancelledError : public llvm::ErrorInfo { public: static char ID; diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index 53dc8aced..c01e5419c 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -348,7 +348,7 @@ void ClangdLSPServer::onCodeAction(CodeActionParams &Params) { void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) { CreateSpaceForTaskHandle(); - TaskHandle TH = Server.codeComplete( + Canceler Cancel = Server.codeComplete( Params.textDocument.uri.file(), Params.position, CCOpts, [this](llvm::Expected List) { auto _ = llvm::make_scope_exit([this]() { CleanupTaskHandle(); }); @@ -361,7 +361,7 @@ void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) { LSPList.items.push_back(R.render(CCOpts)); return reply(std::move(LSPList)); }); - StoreTaskHandle(std::move(TH)); + StoreTaskHandle(std::move(Cancel)); } void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams &Params) { @@ -635,8 +635,7 @@ void ClangdLSPServer::onCancelRequest(CancelParams &Params) { const auto &It = TaskHandles.find(Params.ID); if (It == TaskHandles.end()) return; - if (It->second) - It->second->cancel(); + It->second(); TaskHandles.erase(It); } @@ -659,7 +658,7 @@ void ClangdLSPServer::CreateSpaceForTaskHandle() { elog("Creation of space for task handle: {0} failed.", NormalizedID); } -void ClangdLSPServer::StoreTaskHandle(TaskHandle TH) { +void ClangdLSPServer::StoreTaskHandle(Canceler TH) { const json::Value *ID = getRequestId(); if (!ID) return; diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h index d717de56c..4f444eae9 100644 --- a/clangd/ClangdLSPServer.h +++ b/clangd/ClangdLSPServer.h @@ -170,9 +170,9 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { // destructed instance of ClangdLSPServer. ClangdServer Server; - // Holds task handles for running requets. Key of the map is a serialized - // request id. - llvm::StringMap TaskHandles; + // Holds cancelers for running requets. Key of the map is a serialized + // request id. FIXME: handle cancellation generically in JSONRPCDispatcher. + llvm::StringMap TaskHandles; std::mutex TaskHandlesMutex; // Following three functions are for managing TaskHandles map. They store or @@ -183,7 +183,7 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { // request. void CleanupTaskHandle(); void CreateSpaceForTaskHandle(); - void StoreTaskHandle(TaskHandle TH); + void StoreTaskHandle(Canceler TH); }; } // namespace clangd } // namespace clang diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 0a8e748b2..20af72fb4 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -201,16 +201,16 @@ void ClangdServer::removeDocument(PathRef File) { WorkScheduler.remove(File); } -TaskHandle ClangdServer::codeComplete(PathRef File, Position Pos, - const clangd::CodeCompleteOptions &Opts, - Callback CB) { +Canceler ClangdServer::codeComplete(PathRef File, Position Pos, + const clangd::CodeCompleteOptions &Opts, + Callback CB) { // Copy completion options for passing them to async task handler. auto CodeCompleteOpts = Opts; if (!CodeCompleteOpts.Index) // Respect overridden index. CodeCompleteOpts.Index = Index; - TaskHandle TH = Task::createHandle(); - WithContext ContextWithCancellation(setCurrentTask(TH)); + auto Cancelable = cancelableTask(); + WithContext ContextWithCancellation(std::move(Cancelable.first)); // Copy PCHs to avoid accessing this->PCHs concurrently std::shared_ptr PCHs = this->PCHs; auto FS = FSProvider.getFileSystem(); @@ -259,7 +259,7 @@ TaskHandle ClangdServer::codeComplete(PathRef File, Position Pos, // We use a potentially-stale preamble because latency is critical here. WorkScheduler.runWithPreamble("CodeComplete", File, TUScheduler::Stale, Bind(Task, File.str(), std::move(CB))); - return TH; + return std::move(Cancelable.second); } void ClangdServer::signatureHelp(PathRef File, Position Pos, diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h index e459fccee..41b99b533 100644 --- a/clangd/ClangdServer.h +++ b/clangd/ClangdServer.h @@ -125,9 +125,9 @@ class ClangdServer { /// while returned future is not yet ready. /// A version of `codeComplete` that runs \p Callback on the processing thread /// when codeComplete results become available. - TaskHandle codeComplete(PathRef File, Position Pos, - const clangd::CodeCompleteOptions &Opts, - Callback CB); + Canceler codeComplete(PathRef File, Position Pos, + const clangd::CodeCompleteOptions &Opts, + Callback CB); /// Provide signature help for \p File at \p Pos. This method should only be /// called for tracked files. diff --git a/unittests/clangd/CancellationTests.cpp b/unittests/clangd/CancellationTests.cpp index 7afd1086a..611ce07dd 100644 --- a/unittests/clangd/CancellationTests.cpp +++ b/unittests/clangd/CancellationTests.cpp @@ -13,57 +13,48 @@ namespace clangd { namespace { TEST(CancellationTest, CancellationTest) { - TaskHandle TH = Task::createHandle(); - WithContext ContextWithCancellation(setCurrentTask(TH)); + auto Task = cancelableTask(); + WithContext ContextWithCancellation(std::move(Task.first)); EXPECT_FALSE(isCancelled()); - TH->cancel(); + Task.second(); EXPECT_TRUE(isCancelled()); } -TEST(CancellationTest, TaskTestHandleDiesContextLives) { +TEST(CancellationTest, CancelerDiesContextLives) { llvm::Optional ContextWithCancellation; { - TaskHandle TH = Task::createHandle(); - ContextWithCancellation.emplace(setCurrentTask(TH)); + auto Task = cancelableTask(); + ContextWithCancellation.emplace(std::move(Task.first)); EXPECT_FALSE(isCancelled()); - TH->cancel(); + Task.second(); EXPECT_TRUE(isCancelled()); } EXPECT_TRUE(isCancelled()); } TEST(CancellationTest, TaskContextDiesHandleLives) { - TaskHandle TH = Task::createHandle(); + auto Task = cancelableTask(); { - WithContext ContextWithCancellation(setCurrentTask(TH)); + WithContext ContextWithCancellation(std::move(Task.first)); EXPECT_FALSE(isCancelled()); - TH->cancel(); + Task.second(); EXPECT_TRUE(isCancelled()); } // Still should be able to cancel without any problems. - TH->cancel(); -} - -TEST(CancellationTest, CancellationToken) { - TaskHandle TH = Task::createHandle(); - WithContext ContextWithCancellation(setCurrentTask(TH)); - const auto &CT = getCurrentTask(); - EXPECT_FALSE(CT.isCancelled()); - TH->cancel(); - EXPECT_TRUE(CT.isCancelled()); + Task.second(); } TEST(CancellationTest, AsynCancellationTest) { std::atomic HasCancelled(false); Notification Cancelled; - auto TaskToBeCancelled = [&](ConstTaskHandle CT) { - WithContext ContextGuard(setCurrentTask(std::move(CT))); + auto TaskToBeCancelled = [&](Context Ctx) { + WithContext ContextGuard(std::move(Ctx)); Cancelled.wait(); HasCancelled = isCancelled(); }; - TaskHandle TH = Task::createHandle(); - std::thread AsyncTask(TaskToBeCancelled, TH); - TH->cancel(); + auto Task = cancelableTask(); + std::thread AsyncTask(TaskToBeCancelled, std::move(Task.first)); + Task.second(); Cancelled.notify(); AsyncTask.join(); From 8a3c265983fc1d3533f949eab3dd553f8616cf92 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Thu, 13 Sep 2018 12:53:23 +0000 Subject: [PATCH 191/686] [clangd] Clarify and hide -index flag. Summary: The wording implies global index support, which is confusing. As most users shouldn't care about this flag, also make it hidden to avoid further confusion. Reviewers: sammccall, ilya-biryukov Subscribers: MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51977 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342134 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/tool/ClangdMain.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index c7d99d2a1..b3b34def0 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -130,10 +130,11 @@ static llvm::cl::opt InputMirrorFile( static llvm::cl::opt EnableIndex( "index", - llvm::cl::desc("Enable index-based features such as global code completion " - "and searching for symbols. " - "Clang uses an index built from symbols in opened files"), - llvm::cl::init(true)); + llvm::cl::desc( + "Enable index-based features. By default, clangd maintains an index " + "built from symbols in opened files. Global index support needs to " + "enabled separatedly."), + llvm::cl::init(true), llvm::cl::Hidden); static llvm::cl::opt ShowOrigins("debug-origin", From 50fa8ff73712d6bbac69453abc98891fc026fa8c Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 13 Sep 2018 12:58:36 +0000 Subject: [PATCH 192/686] [clangd] Allow all LSP methods to signal cancellation via $/cancelRequest Summary: The cancelable scopes are managed by JSONRPCDispatcher so that all Handlers run in cancelable contexts. (Previously ClangdServer did this, for code completion only). Cancellation request processing is therefore also in JSONRPCDispatcher. (Previously it was in ClangdLSPServer). This doesn't actually make any new commands *respect* cancellation - they'd need to check isCancelled() and bail out. But it opens the door to doing this incrementally, and putting such logic in common machinery like TUScheduler. I also rewrote the ClangdServer class/threading comments because I wanted to add to it and I got carried away. Reviewers: ilya-biryukov, kadircet Subscribers: ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D52004 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342135 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 76 +++++------------------------------- clangd/ClangdLSPServer.h | 23 +++-------- clangd/ClangdServer.cpp | 10 ++--- clangd/ClangdServer.h | 26 ++++++++---- clangd/JSONRPCDispatcher.cpp | 50 +++++++++++++++++++++++- clangd/JSONRPCDispatcher.h | 23 +++++++++-- clangd/Protocol.cpp | 33 ---------------- clangd/Protocol.h | 14 ------- clangd/ProtocolHandlers.cpp | 1 - clangd/ProtocolHandlers.h | 1 - 10 files changed, 103 insertions(+), 154 deletions(-) diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index c01e5419c..c68d422d3 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -8,7 +8,6 @@ //===----------------------------------------------------------------------===// #include "ClangdLSPServer.h" -#include "Cancellation.h" #include "Diagnostics.h" #include "JSONRPCDispatcher.h" #include "SourceCode.h" @@ -71,11 +70,6 @@ SymbolKindBitset defaultSymbolKinds() { return Defaults; } -std::string NormalizeRequestID(const json::Value &ID) { - auto NormalizedID = parseNumberOrString(&ID); - assert(NormalizedID && "Was not able to parse request id."); - return std::move(*NormalizedID); -} } // namespace void ClangdLSPServer::onInitialize(InitializeParams &Params) { @@ -347,21 +341,16 @@ void ClangdLSPServer::onCodeAction(CodeActionParams &Params) { } void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) { - CreateSpaceForTaskHandle(); - Canceler Cancel = Server.codeComplete( - Params.textDocument.uri.file(), Params.position, CCOpts, - [this](llvm::Expected List) { - auto _ = llvm::make_scope_exit([this]() { CleanupTaskHandle(); }); - - if (!List) - return replyError(List.takeError()); - CompletionList LSPList; - LSPList.isIncomplete = List->HasMore; - for (const auto &R : List->Completions) - LSPList.items.push_back(R.render(CCOpts)); - return reply(std::move(LSPList)); - }); - StoreTaskHandle(std::move(Cancel)); + Server.codeComplete(Params.textDocument.uri.file(), Params.position, CCOpts, + [this](llvm::Expected List) { + if (!List) + return replyError(List.takeError()); + CompletionList LSPList; + LSPList.isIncomplete = List->HasMore; + for (const auto &R : List->Completions) + LSPList.items.push_back(R.render(CCOpts)); + return reply(std::move(LSPList)); + }); } void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams &Params) { @@ -629,48 +618,3 @@ GlobalCompilationDatabase &ClangdLSPServer::CompilationDB::getCDB() { return *CachingCDB; return *CDB; } - -void ClangdLSPServer::onCancelRequest(CancelParams &Params) { - std::lock_guard Lock(TaskHandlesMutex); - const auto &It = TaskHandles.find(Params.ID); - if (It == TaskHandles.end()) - return; - It->second(); - TaskHandles.erase(It); -} - -void ClangdLSPServer::CleanupTaskHandle() { - const json::Value *ID = getRequestId(); - if (!ID) - return; - std::string NormalizedID = NormalizeRequestID(*ID); - std::lock_guard Lock(TaskHandlesMutex); - TaskHandles.erase(NormalizedID); -} - -void ClangdLSPServer::CreateSpaceForTaskHandle() { - const json::Value *ID = getRequestId(); - if (!ID) - return; - std::string NormalizedID = NormalizeRequestID(*ID); - std::lock_guard Lock(TaskHandlesMutex); - if (!TaskHandles.insert({NormalizedID, nullptr}).second) - elog("Creation of space for task handle: {0} failed.", NormalizedID); -} - -void ClangdLSPServer::StoreTaskHandle(Canceler TH) { - const json::Value *ID = getRequestId(); - if (!ID) - return; - std::string NormalizedID = NormalizeRequestID(*ID); - std::lock_guard Lock(TaskHandlesMutex); - auto It = TaskHandles.find(NormalizedID); - if (It == TaskHandles.end()) { - elog("CleanupTaskHandle called before store can happen for request:{0}.", - NormalizedID); - return; - } - if (It->second != nullptr) - elog("TaskHandle didn't get cleared for: {0}.", NormalizedID); - It->second = std::move(TH); -} diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h index 4f444eae9..62260e222 100644 --- a/clangd/ClangdLSPServer.h +++ b/clangd/ClangdLSPServer.h @@ -26,8 +26,11 @@ namespace clangd { class JSONOutput; class SymbolIndex; -/// This class provides implementation of an LSP server, glueing the JSON -/// dispatch and ClangdServer together. +/// This class exposes ClangdServer's capabilities via Language Server Protocol. +/// +/// JSONRPCDispatcher binds the implemented ProtocolCallbacks methods +/// (e.g. onInitialize) to corresponding JSON-RPC methods ("initialize"). +/// The server also supports $/cancelRequest (JSONRPCDispatcher provides this). class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { public: /// If \p CompileCommandsDir has a value, compile_commands.json will be @@ -76,7 +79,6 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { void onRename(RenameParams &Parames) override; void onHover(TextDocumentPositionParams &Params) override; void onChangeConfiguration(DidChangeConfigurationParams &Params) override; - void onCancelRequest(CancelParams &Params) override; std::vector getFixes(StringRef File, const clangd::Diagnostic &D); @@ -169,21 +171,6 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { // the worker thread that may otherwise run an async callback on partially // destructed instance of ClangdLSPServer. ClangdServer Server; - - // Holds cancelers for running requets. Key of the map is a serialized - // request id. FIXME: handle cancellation generically in JSONRPCDispatcher. - llvm::StringMap TaskHandles; - std::mutex TaskHandlesMutex; - - // Following three functions are for managing TaskHandles map. They store or - // remove a task handle for the request-id stored in current Context. - // FIXME(kadircet): Wrap the following three functions in a RAII object to - // make sure these do not get misused. The object might be stored in the - // Context of the thread or moved around until a reply is generated for the - // request. - void CleanupTaskHandle(); - void CreateSpaceForTaskHandle(); - void StoreTaskHandle(Canceler TH); }; } // namespace clangd } // namespace clang diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 20af72fb4..fb4f1be52 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -8,7 +8,6 @@ //===-------------------------------------------------------------------===// #include "ClangdServer.h" -#include "Cancellation.h" #include "CodeComplete.h" #include "FindSymbols.h" #include "Headers.h" @@ -201,16 +200,14 @@ void ClangdServer::removeDocument(PathRef File) { WorkScheduler.remove(File); } -Canceler ClangdServer::codeComplete(PathRef File, Position Pos, - const clangd::CodeCompleteOptions &Opts, - Callback CB) { +void ClangdServer::codeComplete(PathRef File, Position Pos, + const clangd::CodeCompleteOptions &Opts, + Callback CB) { // Copy completion options for passing them to async task handler. auto CodeCompleteOpts = Opts; if (!CodeCompleteOpts.Index) // Respect overridden index. CodeCompleteOpts.Index = Index; - auto Cancelable = cancelableTask(); - WithContext ContextWithCancellation(std::move(Cancelable.first)); // Copy PCHs to avoid accessing this->PCHs concurrently std::shared_ptr PCHs = this->PCHs; auto FS = FSProvider.getFileSystem(); @@ -259,7 +256,6 @@ Canceler ClangdServer::codeComplete(PathRef File, Position Pos, // We use a potentially-stale preamble because latency is critical here. WorkScheduler.runWithPreamble("CodeComplete", File, TUScheduler::Stale, Bind(Task, File.str(), std::move(CB))); - return std::move(Cancelable.second); } void ClangdServer::signatureHelp(PathRef File, Position Pos, diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h index 41b99b533..afbda4bd8 100644 --- a/clangd/ClangdServer.h +++ b/clangd/ClangdServer.h @@ -45,15 +45,25 @@ class DiagnosticsConsumer { std::vector Diagnostics) = 0; }; -/// Provides API to manage ASTs for a collection of C++ files and request -/// various language features. -/// Currently supports async diagnostics, code completion, formatting and goto -/// definition. +/// Manages a collection of source files and derived data (ASTs, indexes), +/// and provides language-aware features such as code completion. +/// +/// The primary client is ClangdLSPServer which exposes these features via +/// the Language Server protocol. ClangdServer may also be embedded directly, +/// though its API is not stable over time. +/// +/// ClangdServer should be used from a single thread. Many potentially-slow +/// operations have asynchronous APIs and deliver their results on another +/// thread. +/// Such operations support cancellation: if the caller sets up a cancelable +/// context, many operations will notice cancellation and fail early. +/// (ClangdLSPServer uses this to implement $/cancelRequest). class ClangdServer { public: struct Options { /// To process requests asynchronously, ClangdServer spawns worker threads. - /// If 0, all requests are processed on the calling thread. + /// If this is zero, no threads are spawned. All work is done on the calling + /// thread, and callbacks are invoked before "async" functions return. unsigned AsyncThreadsCount = getDefaultAsyncThreadsCount(); /// AST caching policy. The default is to keep up to 3 ASTs in memory. @@ -125,9 +135,9 @@ class ClangdServer { /// while returned future is not yet ready. /// A version of `codeComplete` that runs \p Callback on the processing thread /// when codeComplete results become available. - Canceler codeComplete(PathRef File, Position Pos, - const clangd::CodeCompleteOptions &Opts, - Callback CB); + void codeComplete(PathRef File, Position Pos, + const clangd::CodeCompleteOptions &Opts, + Callback CB); /// Provide signature help for \p File at \p Pos. This method should only be /// called for tracked files. diff --git a/clangd/JSONRPCDispatcher.cpp b/clangd/JSONRPCDispatcher.cpp index a3dd9ac4a..4fe4f5596 100644 --- a/clangd/JSONRPCDispatcher.cpp +++ b/clangd/JSONRPCDispatcher.cpp @@ -11,12 +11,14 @@ #include "Cancellation.h" #include "ProtocolHandlers.h" #include "Trace.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/Errno.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/JSON.h" +#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/SourceMgr.h" #include @@ -158,6 +160,16 @@ void clangd::call(StringRef Method, json::Value &&Params) { }); } +JSONRPCDispatcher::JSONRPCDispatcher(Handler UnknownHandler) + : UnknownHandler(std::move(UnknownHandler)) { + registerHandler("$/cancelRequest", [this](const json::Value &Params) { + if (auto *O = Params.getAsObject()) + if (auto *ID = O->get("id")) + return cancelRequest(*ID); + log("Bad cancellation request: {0}", Params); + }); +} + void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) { assert(!Handlers.count(Method) && "Handler already registered!"); Handlers[Method] = std::move(H); @@ -180,8 +192,7 @@ static void logIncomingMessage(const llvm::Optional &ID, } } -bool JSONRPCDispatcher::call(const json::Value &Message, - JSONOutput &Out) const { +bool JSONRPCDispatcher::call(const json::Value &Message, JSONOutput &Out) { // Message must be an object with "jsonrpc":"2.0". auto *Object = Message.getAsObject(); if (!Object || Object->getString("jsonrpc") != Optional("2.0")) @@ -214,12 +225,47 @@ bool JSONRPCDispatcher::call(const json::Value &Message, SPAN_ATTACH(Tracer, "ID", *ID); SPAN_ATTACH(Tracer, "Params", Params); + // Requests with IDs can be canceled by the client. Add cancellation context. + llvm::Optional WithCancel; + if (ID) + WithCancel.emplace(cancelableRequestContext(*ID)); + // Stash a reference to the span args, so later calls can add metadata. WithContext WithRequestSpan(RequestSpan::stash(Tracer)); Handler(std::move(Params)); return true; } +// We run cancelable requests in a context that does two things: +// - allows cancellation using RequestCancelers[ID] +// - cleans up the entry in RequestCancelers when it's no longer needed +// If a client reuses an ID, the last one wins and the first cannot be canceled. +Context JSONRPCDispatcher::cancelableRequestContext(const json::Value &ID) { + auto Task = cancelableTask(); + auto StrID = llvm::to_string(ID); // JSON-serialize ID for map key. + auto Cookie = NextRequestCookie++; // No lock, only called on main thread. + { + std::lock_guard Lock(RequestCancelersMutex); + RequestCancelers[StrID] = {std::move(Task.second), Cookie}; + } + // When the request ends, we can clean up the entry we just added. + // The cookie lets us check that it hasn't been overwritten due to ID reuse. + return Task.first.derive(make_scope_exit([this, StrID, Cookie] { + std::lock_guard Lock(RequestCancelersMutex); + auto It = RequestCancelers.find(StrID); + if (It != RequestCancelers.end() && It->second.second == Cookie) + RequestCancelers.erase(It); + })); +} + +void JSONRPCDispatcher::cancelRequest(const json::Value &ID) { + auto StrID = llvm::to_string(ID); + std::lock_guard Lock(RequestCancelersMutex); + auto It = RequestCancelers.find(StrID); + if (It != RequestCancelers.end()) + It->second.first(); // Invoke the canceler. +} + // Tries to read a line up to and including \n. // If failing, feof() or ferror() will be set. static bool readLine(std::FILE *In, std::string &Out) { diff --git a/clangd/JSONRPCDispatcher.h b/clangd/JSONRPCDispatcher.h index fda37f86c..b223ceb5f 100644 --- a/clangd/JSONRPCDispatcher.h +++ b/clangd/JSONRPCDispatcher.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H +#include "Cancellation.h" #include "Logger.h" #include "Protocol.h" #include "Trace.h" @@ -77,23 +78,37 @@ void call(llvm::StringRef Method, llvm::json::Value &&Params); /// Main JSONRPC entry point. This parses the JSONRPC "header" and calls the /// registered Handler for the method received. +/// +/// The `$/cancelRequest` notification is handled by the dispatcher itself. +/// It marks the matching request as cancelled, if it's still running. class JSONRPCDispatcher { public: - // A handler responds to requests for a particular method name. + /// A handler responds to requests for a particular method name. + /// + /// JSONRPCDispatcher will mark the handler's context as cancelled if a + /// matching cancellation request is received. Handlers are encouraged to + /// check for cancellation and fail quickly in this case. using Handler = std::function; /// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown /// method is received. - JSONRPCDispatcher(Handler UnknownHandler) - : UnknownHandler(std::move(UnknownHandler)) {} + JSONRPCDispatcher(Handler UnknownHandler); /// Registers a Handler for the specified Method. void registerHandler(StringRef Method, Handler H); /// Parses a JSONRPC message and calls the Handler for it. - bool call(const llvm::json::Value &Message, JSONOutput &Out) const; + bool call(const llvm::json::Value &Message, JSONOutput &Out); private: + // Tracking cancellations needs a mutex: handlers may finish on a different + // thread, and that's when we clean up entries in the map. + mutable std::mutex RequestCancelersMutex; + llvm::StringMap> RequestCancelers; + unsigned NextRequestCookie = 0; + Context cancelableRequestContext(const llvm::json::Value &ID); + void cancelRequest(const llvm::json::Value &ID); + llvm::StringMap Handlers; Handler UnknownHandler; }; diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp index 9b232af17..e6ffe66a1 100644 --- a/clangd/Protocol.cpp +++ b/clangd/Protocol.cpp @@ -623,38 +623,5 @@ bool fromJSON(const json::Value &Params, ReferenceParams &R) { return fromJSON(Params, Base); } -json::Value toJSON(const CancelParams &CP) { - return json::Object{{"id", CP.ID}}; -} - -llvm::raw_ostream &operator<<(llvm::raw_ostream &O, const CancelParams &CP) { - O << toJSON(CP); - return O; -} - -llvm::Optional parseNumberOrString(const json::Value *Params) { - if (!Params) - return llvm::None; - // ID is either a number or a string, check for both. - if(const auto AsString = Params->getAsString()) - return AsString->str(); - - if(const auto AsNumber = Params->getAsInteger()) - return itostr(AsNumber.getValue()); - - return llvm::None; -} - -bool fromJSON(const json::Value &Params, CancelParams &CP) { - const auto ParamsAsObject = Params.getAsObject(); - if (!ParamsAsObject) - return false; - if (auto Parsed = parseNumberOrString(ParamsAsObject->get("id"))) { - CP.ID = std::move(*Parsed); - return true; - } - return false; -} - } // namespace clangd } // namespace clang diff --git a/clangd/Protocol.h b/clangd/Protocol.h index 9eec9c0f5..f09a63c95 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -886,20 +886,6 @@ struct ReferenceParams : public TextDocumentPositionParams { }; bool fromJSON(const llvm::json::Value &, ReferenceParams &); -struct CancelParams { - /// The request id to cancel. - /// This can be either a number or string, if it is a number simply print it - /// out and always use a string. - std::string ID; -}; -llvm::json::Value toJSON(const CancelParams &); -llvm::raw_ostream &operator<<(llvm::raw_ostream &, const CancelParams &); -bool fromJSON(const llvm::json::Value &, CancelParams &); - -/// Param can be either of type string or number. Returns the result as a -/// string. -llvm::Optional parseNumberOrString(const llvm::json::Value *Param); - } // namespace clangd } // namespace clang diff --git a/clangd/ProtocolHandlers.cpp b/clangd/ProtocolHandlers.cpp index 9bb21e3e3..73da45eca 100644 --- a/clangd/ProtocolHandlers.cpp +++ b/clangd/ProtocolHandlers.cpp @@ -76,5 +76,4 @@ void clangd::registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, Register("workspace/didChangeConfiguration", &ProtocolCallbacks::onChangeConfiguration); Register("workspace/symbol", &ProtocolCallbacks::onWorkspaceSymbol); - Register("$/cancelRequest", &ProtocolCallbacks::onCancelRequest); } diff --git a/clangd/ProtocolHandlers.h b/clangd/ProtocolHandlers.h index 72ecd2b26..983b7e2f4 100644 --- a/clangd/ProtocolHandlers.h +++ b/clangd/ProtocolHandlers.h @@ -56,7 +56,6 @@ class ProtocolCallbacks { virtual void onDocumentHighlight(TextDocumentPositionParams &Params) = 0; virtual void onHover(TextDocumentPositionParams &Params) = 0; virtual void onChangeConfiguration(DidChangeConfigurationParams &Params) = 0; - virtual void onCancelRequest(CancelParams &Params) = 0; }; void registerCallbackHandlers(JSONRPCDispatcher &Dispatcher, From ae0e8f6c1a5f6c92d45a24b2266d287ed583d389 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 13 Sep 2018 14:21:50 +0000 Subject: [PATCH 193/686] [clangd] Use JSON format in benchmark requests reader After `FuzzyFindRequest` JSON (de)serialization was introduced, it should replace ad-hoc fuzzy-find request parsing implemented in the IndexBenchmark driver. Reviewed By: ilya-biryukov Differential Revision: https://reviews.llvm.org/D51971 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342137 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/benchmarks/IndexBenchmark.cpp | 67 +++++++++++++--------------- test/clangd/Inputs/requests.json | 7 +++ test/clangd/Inputs/requests.log | 5 --- test/clangd/index-tools.test | 4 +- 4 files changed, 41 insertions(+), 42 deletions(-) create mode 100644 test/clangd/Inputs/requests.json delete mode 100644 test/clangd/Inputs/requests.log diff --git a/clangd/benchmarks/IndexBenchmark.cpp b/clangd/benchmarks/IndexBenchmark.cpp index 44af02023..5714a7c62 100644 --- a/clangd/benchmarks/IndexBenchmark.cpp +++ b/clangd/benchmarks/IndexBenchmark.cpp @@ -19,56 +19,51 @@ #include const char *IndexFilename; -const char *LogFilename; +const char *RequestsFilename; namespace clang { namespace clangd { namespace { -std::unique_ptr buildMem() { - return clang::clangd::loadIndex(IndexFilename, {}, false); +std::unique_ptr buildMem() { + return loadIndex(IndexFilename, {}, false); } -std::unique_ptr buildDex() { - return clang::clangd::loadIndex(IndexFilename, {}, true); +std::unique_ptr buildDex() { + return loadIndex(IndexFilename, {}, true); } -// This function processes user-provided Log file with fuzzy find requests in -// the following format: -// -// fuzzyFind("UnqualifiedName", scopes=["clang::", "clang::clangd::"]) -// -// It constructs vector of FuzzyFindRequests which is later used for the -// benchmarks. -std::vector extractQueriesFromLogs() { - llvm::Regex RequestMatcher("fuzzyFind\\(\"([a-zA-Z]*)\", scopes=\\[(.*)\\]"); - llvm::SmallVector Matches; - std::ifstream InputStream(LogFilename); +// Reads JSON array of serialized FuzzyFindRequest's from user-provided file. +std::vector extractQueriesFromLogs() { + std::ifstream InputStream(RequestsFilename); std::string Log((std::istreambuf_iterator(InputStream)), std::istreambuf_iterator()); - llvm::StringRef Temporary(Log); - llvm::SmallVector Strings; - Temporary.split(Strings, '\n'); - clang::clangd::FuzzyFindRequest R; - R.MaxCandidateCount = 100; + std::vector Requests; + auto JSONArray = llvm::json::parse(Log); - llvm::SmallVector CommaSeparatedValues; + // Panic if the provided file couldn't be parsed. + if (!JSONArray) { + llvm::errs() << "Error when parsing JSON requests file: " + << llvm::toString(JSONArray.takeError()); + exit(1); + } + if (!JSONArray->getAsArray()) { + llvm::errs() << "Error: top-level value is not a JSON array: " << Log + << '\n'; + exit(1); + } - std::vector RealRequests; - for (auto Line : Strings) { - if (RequestMatcher.match(Line, &Matches)) { - R.Query = Matches[1]; - CommaSeparatedValues.clear(); - Line.split(CommaSeparatedValues, ','); - R.Scopes.clear(); - for (auto C : CommaSeparatedValues) { - R.Scopes.push_back(C); - } - RealRequests.push_back(R); + for (const auto &Item : *JSONArray->getAsArray()) { + FuzzyFindRequest Request; + // Panic if the provided file couldn't be parsed. + if (!fromJSON(Item, Request)) { + llvm::errs() << "Error when deserializing request: " << Item << '\n'; + exit(1); } + Requests.push_back(Request); } - return RealRequests; + return Requests; } static void MemQueries(benchmark::State &State) { @@ -100,12 +95,12 @@ BENCHMARK(DexQueries); int main(int argc, char *argv[]) { if (argc < 3) { llvm::errs() << "Usage: " << argv[0] - << " global-symbol-index.yaml fuzzy-find-requests.log " + << " global-symbol-index.yaml requests.json " "BENCHMARK_OPTIONS...\n"; return -1; } IndexFilename = argv[1]; - LogFilename = argv[2]; + RequestsFilename = argv[2]; // Trim first two arguments of the benchmark invocation. argv += 3; argc -= 3; diff --git a/test/clangd/Inputs/requests.json b/test/clangd/Inputs/requests.json new file mode 100644 index 000000000..fdcb5edd0 --- /dev/null +++ b/test/clangd/Inputs/requests.json @@ -0,0 +1,7 @@ +[{"MaxCandidateCount":100,"ProximityPaths":["/usr/home/user/clang-tools-extra/clangd/benchmarks/IndexBenchmark.cpp"],"Query":"OMP","RestrictForCodeCompletion":true,"Scopes":["clang::"]}, +{"MaxCandidateCount":100,"ProximityPaths":[],"Query":"s","RestrictForCodeCompletion":true,"Scopes":["llvm::", ""]}, +{"MaxCandidateCount":100,"ProximityPaths":[],"Query":"sy","RestrictForCodeCompletion":true,"Scopes":["llvm::", ""]}, +{"MaxCandidateCount":100,"ProximityPaths":[],"Query":"sys","RestrictForCodeCompletion":true,"Scopes":["llvm::", ""]}, +{"MaxCandidateCount":100,"ProximityPaths":[],"Query":"sys","RestrictForCodeCompletion":true,"Scopes":["llvm::", ""]}, +{"MaxCandidateCount":100,"ProximityPaths":[],"Query":"Dex","RestrictForCodeCompletion":true,"Scopes":["clang::clangd::", "clang::", "clang::clangd::dex::"]}, +{"MaxCandidateCount":100,"ProximityPaths":[],"Query":"Variable","RestrictForCodeCompletion":true,"Scopes":[""]}] diff --git a/test/clangd/Inputs/requests.log b/test/clangd/Inputs/requests.log deleted file mode 100644 index f0e669516..000000000 --- a/test/clangd/Inputs/requests.log +++ /dev/null @@ -1,5 +0,0 @@ -V[09:08:34.301] Code complete: fuzzyFind("s", scopes=[llvm::]) -V[09:08:34.368] Code complete: fuzzyFind("sy", scopes=[llvm::]) -V[09:08:34.442] Code complete: fuzzyFind("sys", scopes=[llvm::]) -V[09:09:12.075] Code complete: fuzzyFind("Dex", scopes=[clang::clangd::, clang::, clang::clangd::dex::]) -V[09:09:12.075] Code complete: fuzzyFind("Variable", scopes=[]) diff --git a/test/clangd/index-tools.test b/test/clangd/index-tools.test index b01d5bcd1..e17d2fc84 100644 --- a/test/clangd/index-tools.test +++ b/test/clangd/index-tools.test @@ -1,3 +1,5 @@ # RUN: clangd-indexer %p/Inputs/BenchmarkSource.cpp -- -I%p/Inputs > %t.index # FIXME: By default, benchmarks are excluded from the list of default targets hence not built. Find a way to depend on benchmarks to run the next command. -# RUN: if [ -f %clangd-benchmark-dir/IndexBenchmark ]; then %clangd-benchmark-dir/IndexBenchmark %t.index %p/Inputs/requests.log --benchmark_min_time=0.01 ; fi +# RUN: if [ -f %clangd-benchmark-dir/IndexBenchmark ]; then %clangd-benchmark-dir/IndexBenchmark %t.index %p/Inputs/requests.json --benchmark_min_time=0.01 ; fi +# Pass invalid JSON file and check that IndexBenchmark fails to parse it. +# RUN: if [ -f %clangd-benchmark-dir/IndexBenchmark ]; then not %clangd-benchmark-dir/IndexBenchmark %t.index %t --benchmark_min_time=0.01 ; fi From 442943b154f60fe65026bf3f1f485a4a1df83b24 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 13 Sep 2018 14:27:03 +0000 Subject: [PATCH 194/686] [clangd] Cleanup FuzzyFindRequest filtering limit semantics As discussed during D51860 review, it is better to use `llvm::Optional` here as it has clear semantics which reflect intended behavior. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D52028 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342138 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 2 +- clangd/FindSymbols.cpp | 5 +++-- clangd/index/Index.cpp | 10 +++++----- clangd/index/Index.h | 10 ++++------ clangd/index/MemIndex.cpp | 3 ++- clangd/index/dex/Dex.cpp | 9 +++++---- test/clangd/Inputs/requests.json | 14 +++++++------- unittests/clangd/DexTests.cpp | 12 +++++++----- unittests/clangd/IndexTests.cpp | 7 ++++--- 9 files changed, 38 insertions(+), 34 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 1e632dce9..64d38dd7f 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -1375,7 +1375,7 @@ class CodeCompleteFlow { // Build the query. FuzzyFindRequest Req; if (Opts.Limit) - Req.MaxCandidateCount = Opts.Limit; + Req.Limit = Opts.Limit; Req.Query = Filter->pattern(); Req.RestrictForCodeCompletion = true; Req.Scopes = QueryScopes; diff --git a/clangd/FindSymbols.cpp b/clangd/FindSymbols.cpp index b808e374e..054e63d93 100644 --- a/clangd/FindSymbols.cpp +++ b/clangd/FindSymbols.cpp @@ -117,8 +117,9 @@ getWorkspaceSymbols(StringRef Query, int Limit, const SymbolIndex *const Index, if (IsGlobalQuery || !Names.first.empty()) Req.Scopes = {Names.first}; if (Limit) - Req.MaxCandidateCount = Limit; - TopN Top(Req.MaxCandidateCount); + Req.Limit = Limit; + TopN Top( + Req.Limit ? *Req.Limit : std::numeric_limits::max()); FuzzyMatcher Filter(Req.Query); Index->fuzzyFind(Req, [HintPath, &Top, &Filter](const Symbol &Sym) { // Prefer the definition over e.g. a function declaration in a header diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index 5a8f0d168..cfc49406c 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -177,14 +177,14 @@ std::shared_ptr SwapIndex::snapshot() const { bool fromJSON(const llvm::json::Value &Parameters, FuzzyFindRequest &Request) { json::ObjectMapper O(Parameters); - int64_t MaxCandidateCount; + int64_t Limit; bool OK = O && O.map("Query", Request.Query) && O.map("Scopes", Request.Scopes) && - O.map("MaxCandidateCount", MaxCandidateCount) && + O.map("Limit", Limit) && O.map("RestrictForCodeCompletion", Request.RestrictForCodeCompletion) && O.map("ProximityPaths", Request.ProximityPaths); - if (OK && MaxCandidateCount <= std::numeric_limits::max()) - Request.MaxCandidateCount = MaxCandidateCount; + if (OK && Limit <= std::numeric_limits::max()) + Request.Limit = Limit; return OK; } @@ -192,7 +192,7 @@ llvm::json::Value toJSON(const FuzzyFindRequest &Request) { return json::Object{ {"Query", Request.Query}, {"Scopes", json::Array{Request.Scopes}}, - {"MaxCandidateCount", Request.MaxCandidateCount}, + {"Limit", Request.Limit}, {"RestrictForCodeCompletion", Request.RestrictForCodeCompletion}, {"ProximityPaths", json::Array{Request.ProximityPaths}}, }; diff --git a/clangd/index/Index.h b/clangd/index/Index.h index c14ed9a3a..8a18c6197 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -437,9 +437,7 @@ struct FuzzyFindRequest { std::vector Scopes; /// \brief The number of top candidates to return. The index may choose to /// return more than this, e.g. if it doesn't know which candidates are best. - // FIXME: Use llvm::Optional; semantically, the absence of MaxCandidateCount - // is equivalent to setting this field to default value as below. - uint32_t MaxCandidateCount = std::numeric_limits::max(); + llvm::Optional Limit; /// If set to true, only symbols for completion support will be considered. bool RestrictForCodeCompletion = false; /// Contextually relevant files (e.g. the file we're code-completing in). @@ -447,9 +445,9 @@ struct FuzzyFindRequest { std::vector ProximityPaths; bool operator==(const FuzzyFindRequest &Req) const { - return std::tie(Query, Scopes, MaxCandidateCount, RestrictForCodeCompletion, + return std::tie(Query, Scopes, Limit, RestrictForCodeCompletion, ProximityPaths) == - std::tie(Req.Query, Req.Scopes, Req.MaxCandidateCount, + std::tie(Req.Query, Req.Scopes, Req.Limit, Req.RestrictForCodeCompletion, Req.ProximityPaths); } bool operator!=(const FuzzyFindRequest &Req) const { return !(*this == Req); } @@ -476,7 +474,7 @@ class SymbolIndex { /// each matched symbol before returning. /// If returned Symbols are used outside Callback, they must be deep-copied! /// - /// Returns true if there may be more results (limited by MaxCandidateCount). + /// Returns true if there may be more results (limited by Req.Limit). virtual bool fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref Callback) const = 0; diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index 2cb20a785..546902206 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -29,7 +29,8 @@ bool MemIndex::fuzzyFind( assert(!StringRef(Req.Query).contains("::") && "There must be no :: in query."); - TopN> Top(Req.MaxCandidateCount); + TopN> Top( + Req.Limit ? *Req.Limit : std::numeric_limits::max()); FuzzyMatcher Filter(Req.Query); bool More = false; for (const auto Pair : Index) { diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 59d448cc2..0f22c7f92 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -179,8 +179,8 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, // FIXME(kbobyrev): Pre-scoring retrieval threshold should be adjusted as // using 100x of the requested number might not be good in practice, e.g. // when the requested number of items is small. - const size_t ItemsToRetrieve = 100 * Req.MaxCandidateCount; - auto Root = createLimit(move(QueryIterator), ItemsToRetrieve); + auto Root = Req.Limit ? createLimit(move(QueryIterator), *Req.Limit * 100) + : move(QueryIterator); using IDAndScore = std::pair; std::vector IDAndScores = consume(*Root); @@ -188,7 +188,8 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, auto Compare = [](const IDAndScore &LHS, const IDAndScore &RHS) { return LHS.second > RHS.second; }; - TopN Top(Req.MaxCandidateCount, Compare); + TopN Top( + Req.Limit ? *Req.Limit : std::numeric_limits::max(), Compare); for (const auto &IDAndScore : IDAndScores) { const DocID SymbolDocID = IDAndScore.first; const auto *Sym = Symbols[SymbolDocID]; @@ -205,7 +206,7 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, More = true; } - // Apply callback to the top Req.MaxCandidateCount items in the descending + // Apply callback to the top Req.Limit items in the descending // order of cumulative score. for (const auto &Item : std::move(Top).items()) Callback(*Symbols[Item.first]); diff --git a/test/clangd/Inputs/requests.json b/test/clangd/Inputs/requests.json index fdcb5edd0..3ddb9cb70 100644 --- a/test/clangd/Inputs/requests.json +++ b/test/clangd/Inputs/requests.json @@ -1,7 +1,7 @@ -[{"MaxCandidateCount":100,"ProximityPaths":["/usr/home/user/clang-tools-extra/clangd/benchmarks/IndexBenchmark.cpp"],"Query":"OMP","RestrictForCodeCompletion":true,"Scopes":["clang::"]}, -{"MaxCandidateCount":100,"ProximityPaths":[],"Query":"s","RestrictForCodeCompletion":true,"Scopes":["llvm::", ""]}, -{"MaxCandidateCount":100,"ProximityPaths":[],"Query":"sy","RestrictForCodeCompletion":true,"Scopes":["llvm::", ""]}, -{"MaxCandidateCount":100,"ProximityPaths":[],"Query":"sys","RestrictForCodeCompletion":true,"Scopes":["llvm::", ""]}, -{"MaxCandidateCount":100,"ProximityPaths":[],"Query":"sys","RestrictForCodeCompletion":true,"Scopes":["llvm::", ""]}, -{"MaxCandidateCount":100,"ProximityPaths":[],"Query":"Dex","RestrictForCodeCompletion":true,"Scopes":["clang::clangd::", "clang::", "clang::clangd::dex::"]}, -{"MaxCandidateCount":100,"ProximityPaths":[],"Query":"Variable","RestrictForCodeCompletion":true,"Scopes":[""]}] +[{"Limit":100,"ProximityPaths":["/usr/home/user/clang-tools-extra/clangd/benchmarks/IndexBenchmark.cpp"],"Query":"OMP","RestrictForCodeCompletion":true,"Scopes":["clang::"]}, +{"Limit":100,"ProximityPaths":[],"Query":"s","RestrictForCodeCompletion":true,"Scopes":["llvm::", ""]}, +{"Limit":100,"ProximityPaths":[],"Query":"sy","RestrictForCodeCompletion":true,"Scopes":["llvm::", ""]}, +{"Limit":100,"ProximityPaths":[],"Query":"sys","RestrictForCodeCompletion":true,"Scopes":["llvm::", ""]}, +{"Limit":100,"ProximityPaths":[],"Query":"sys","RestrictForCodeCompletion":true,"Scopes":["llvm::", ""]}, +{"Limit":100,"ProximityPaths":[],"Query":"Dex","RestrictForCodeCompletion":true,"Scopes":["clang::clangd::", "clang::", "clang::clangd::dex::"]}, +{"Limit":100,"ProximityPaths":[],"Query":"Variable","RestrictForCodeCompletion":true,"Scopes":[""]}] diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 67d837003..5ce4da282 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -482,7 +482,7 @@ TEST(DexTest, FuzzyMatchQ) { URISchemes); FuzzyFindRequest Req; Req.Query = "lol"; - Req.MaxCandidateCount = 2; + Req.Limit = 2; EXPECT_THAT(match(*I, Req), UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); } @@ -498,6 +498,7 @@ TEST(DexTest, DexDeduplicate) { FuzzyFindRequest Req; Req.Query = "2"; Dex I(Symbols, URISchemes); + EXPECT_FALSE(Req.Limit); EXPECT_THAT(match(I, Req), ElementsAre("2", "2")); } @@ -505,10 +506,11 @@ TEST(DexTest, DexLimitedNumMatches) { auto I = Dex::build(generateNumSymbols(0, 100), URISchemes); FuzzyFindRequest Req; Req.Query = "5"; - Req.MaxCandidateCount = 3; + Req.Limit = 3; bool Incomplete; auto Matches = match(*I, Req, &Incomplete); - EXPECT_EQ(Matches.size(), Req.MaxCandidateCount); + EXPECT_TRUE(Req.Limit); + EXPECT_EQ(Matches.size(), *Req.Limit); EXPECT_TRUE(Incomplete); } @@ -518,7 +520,7 @@ TEST(DexTest, FuzzyMatch) { URISchemes); FuzzyFindRequest Req; Req.Query = "lol"; - Req.MaxCandidateCount = 2; + Req.Limit = 2; EXPECT_THAT(match(*I, Req), UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); } @@ -596,7 +598,7 @@ TEST(DexTest, ProximityPathsBoosting) { FuzzyFindRequest Req; Req.Query = "abc"; // The best candidate can change depending on the proximity paths. - Req.MaxCandidateCount = 1; + Req.Limit = 1; // FuzzyFind request comes from the file which is far from the root: expect // CloseSymbol to come out. diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp index 2eb9c23be..187b3c556 100644 --- a/unittests/clangd/IndexTests.cpp +++ b/unittests/clangd/IndexTests.cpp @@ -78,10 +78,11 @@ TEST(MemIndexTest, MemIndexLimitedNumMatches) { auto I = MemIndex::build(generateNumSymbols(0, 100), RefSlab()); FuzzyFindRequest Req; Req.Query = "5"; - Req.MaxCandidateCount = 3; + Req.Limit = 3; bool Incomplete; auto Matches = match(*I, Req, &Incomplete); - EXPECT_EQ(Matches.size(), Req.MaxCandidateCount); + EXPECT_TRUE(Req.Limit); + EXPECT_EQ(Matches.size(), *Req.Limit); EXPECT_TRUE(Incomplete); } @@ -91,7 +92,7 @@ TEST(MemIndexTest, FuzzyMatch) { RefSlab()); FuzzyFindRequest Req; Req.Query = "lol"; - Req.MaxCandidateCount = 2; + Req.Limit = 2; EXPECT_THAT(match(*I, Req), UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); } From 7e66a67dd897693e85155c14a2fc314186d7e29f Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 13 Sep 2018 15:35:55 +0000 Subject: [PATCH 195/686] [clangd] Fix Dexp build %s/MaxCandidateCount/Limit/g after rL342138. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342143 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/dexp/Dexp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/index/dex/dexp/Dexp.cpp b/clangd/index/dex/dexp/Dexp.cpp index c3c6307d3..41f9bf00e 100644 --- a/clangd/index/dex/dexp/Dexp.cpp +++ b/clangd/index/dex/dexp/Dexp.cpp @@ -54,7 +54,7 @@ void reportTime(StringRef Name, llvm::function_ref F) { void fuzzyFind(llvm::StringRef UnqualifiedName, const SymbolIndex &Index) { FuzzyFindRequest Request; - Request.MaxCandidateCount = 10; + Request.Limit = 10; Request.Query = UnqualifiedName; // FIXME(kbobyrev): Print symbol final scores to see the distribution. static const auto OutputFormat = "{0,-4} | {1,-40} | {2,-25}\n"; From 2266973c4cf45d86fbfa2bdefcfd43d308d6a43e Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Thu, 13 Sep 2018 17:11:03 +0000 Subject: [PATCH 196/686] [clangd] Introduce PostingList interface This patch abstracts `PostingList` interface and reuses existing implementation. It will be used later to test different `PostingList` representations. No functionality change is introduced, this patch is mostly refactoring so that the following patches could focus on functionality while not being too hard to review. Reviewed By: sammccall, ioeric Differential Revision: https://reviews.llvm.org/D51982 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342155 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 1 + clangd/index/dex/Dex.cpp | 18 +++-- clangd/index/dex/Dex.h | 1 + clangd/index/dex/Iterator.cpp | 67 --------------- clangd/index/dex/Iterator.h | 19 ----- clangd/index/dex/PostingList.cpp | 84 +++++++++++++++++++ clangd/index/dex/PostingList.h | 52 ++++++++++++ unittests/clangd/DexTests.cpp | 135 +++++++++++++++---------------- 8 files changed, 216 insertions(+), 161 deletions(-) create mode 100644 clangd/index/dex/PostingList.cpp create mode 100644 clangd/index/dex/PostingList.h diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index 7991aba89..08af301ae 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -48,6 +48,7 @@ add_clang_library(clangDaemon index/dex/Dex.cpp index/dex/Iterator.cpp + index/dex/PostingList.cpp index/dex/Trigram.cpp LINK_LIBS diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 0f22c7f92..3a80f7327 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -82,7 +82,7 @@ std::vector> createFileProximityIterators( // FIXME(kbobyrev): Append LIMIT on top of every BOOST iterator. PathProximitySignals.SymbolURI = ParentURI; BoostingIterators.push_back( - createBoost(create(It->second), PathProximitySignals.evaluate())); + createBoost(It->second.iterator(), PathProximitySignals.evaluate())); } } return BoostingIterators; @@ -112,13 +112,19 @@ void Dex::buildIndex() { Symbols[I] = ScoredSymbols[I].second; } - // Populate TempInvertedIndex with posting lists for index symbols. + // Populate TempInvertedIndex with lists for index symbols. + llvm::DenseMap> TempInvertedIndex; for (DocID SymbolRank = 0; SymbolRank < Symbols.size(); ++SymbolRank) { const auto *Sym = Symbols[SymbolRank]; for (const auto &Token : generateSearchTokens(*Sym)) - InvertedIndex[Token].push_back(SymbolRank); + TempInvertedIndex[Token].push_back(SymbolRank); } + // Convert lists of items to posting lists. + for (const auto &TokenToPostingList : TempInvertedIndex) + InvertedIndex.insert({TokenToPostingList.first, + PostingList(move(TokenToPostingList.second))}); + vlog("Built Dex with estimated memory usage {0} bytes.", estimateMemoryUsage()); } @@ -142,7 +148,7 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, for (const auto &Trigram : TrigramTokens) { const auto It = InvertedIndex.find(Trigram); if (It != InvertedIndex.end()) - TrigramIterators.push_back(create(It->second)); + TrigramIterators.push_back(It->second.iterator()); } if (!TrigramIterators.empty()) TopLevelChildren.push_back(createAnd(move(TrigramIterators))); @@ -152,7 +158,7 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, for (const auto &Scope : Req.Scopes) { const auto It = InvertedIndex.find(Token(Token::Kind::Scope, Scope)); if (It != InvertedIndex.end()) - ScopeIterators.push_back(create(It->second)); + ScopeIterators.push_back(It->second.iterator()); } // Add OR iterator for scopes if there are any Scope Iterators. if (!ScopeIterators.empty()) @@ -233,7 +239,7 @@ size_t Dex::estimateMemoryUsage() const { Bytes += LookupTable.getMemorySize(); Bytes += InvertedIndex.getMemorySize(); for (const auto &P : InvertedIndex) - Bytes += P.second.size() * sizeof(DocID); + Bytes += P.second.bytes(); return Bytes + BackingDataSize; } diff --git a/clangd/index/dex/Dex.h b/clangd/index/dex/Dex.h index 4d9baa7a5..79b771a86 100644 --- a/clangd/index/dex/Dex.h +++ b/clangd/index/dex/Dex.h @@ -21,6 +21,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEX_H #include "Iterator.h" +#include "PostingList.h" #include "Token.h" #include "Trigram.h" #include "index/Index.h" diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index 8d5527205..06d778dd5 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -18,69 +18,6 @@ namespace dex { namespace { -/// Implements Iterator over a PostingList. DocumentIterator is the most basic -/// iterator: it doesn't have any children (hence it is the leaf of iterator -/// tree) and is simply a wrapper around PostingList::const_iterator. -class DocumentIterator : public Iterator { -public: - explicit DocumentIterator(PostingListRef Documents) - : Documents(Documents), Index(std::begin(Documents)) {} - - bool reachedEnd() const override { return Index == std::end(Documents); } - - /// Advances cursor to the next item. - void advance() override { - assert(!reachedEnd() && "DOCUMENT iterator can't advance() at the end."); - ++Index; - } - - /// Applies binary search to advance cursor to the next item with DocID equal - /// or higher than the given one. - void advanceTo(DocID ID) override { - assert(!reachedEnd() && "DOCUMENT iterator can't advance() at the end."); - // If current ID is beyond requested one, iterator is already in the right - // state. - if (peek() < ID) - Index = std::lower_bound(Index, std::end(Documents), ID); - } - - DocID peek() const override { - assert(!reachedEnd() && "DOCUMENT iterator can't peek() at the end."); - return *Index; - } - - float consume() override { - assert(!reachedEnd() && "DOCUMENT iterator can't consume() at the end."); - return DEFAULT_BOOST_SCORE; - } - - size_t estimateSize() const override { return Documents.size(); } - -private: - llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { - OS << '['; - auto Separator = ""; - for (auto It = std::begin(Documents); It != std::end(Documents); ++It) { - OS << Separator; - if (It == Index) - OS << '{' << *It << '}'; - else - OS << *It; - Separator = ", "; - } - OS << Separator; - if (Index == std::end(Documents)) - OS << "{END}"; - else - OS << "END"; - OS << ']'; - return OS; - } - - PostingListRef Documents; - PostingListRef::const_iterator Index; -}; - /// Implements Iterator over the intersection of other iterators. /// /// AndIterator iterates through common items among all children. It becomes @@ -399,10 +336,6 @@ std::vector> consume(Iterator &It) { return Result; } -std::unique_ptr create(PostingListRef Documents) { - return llvm::make_unique(Documents); -} - std::unique_ptr createAnd(std::vector> Children) { // If there is exactly one child, pull it one level up: AND(Child) -> Child. diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 06adaefaf..9b002b3c1 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -44,20 +44,6 @@ namespace dex { /// Symbol position in the list of all index symbols sorted by a pre-computed /// symbol quality. using DocID = uint32_t; -/// Contains sorted sequence of DocIDs all of which belong to symbols matching -/// certain criteria, i.e. containing a Search Token. PostingLists are values -/// for the inverted index. -// FIXME(kbobyrev): Posting lists for incomplete trigrams (one/two symbols) are -// likely to be very dense and hence require special attention so that the index -// doesn't use too much memory. Possible solution would be to construct -// compressed posting lists which consist of ranges of DocIDs instead of -// distinct DocIDs. A special case would be the empty query: for that case -// TrueIterator should be implemented - an iterator which doesn't actually store -// any PostingList within itself, but "contains" all DocIDs in range -// [0, IndexSize). -using PostingList = std::vector; -/// Immutable reference to PostingList object. -using PostingListRef = llvm::ArrayRef; /// Iterator is the interface for Query Tree node. The simplest type of Iterator /// is DocumentIterator which is simply a wrapper around PostingList iterator @@ -131,11 +117,6 @@ class Iterator { /// to acquire preliminary scores of requested items. std::vector> consume(Iterator &It); -/// Returns a document iterator over given PostingList. -/// -/// DocumentIterator returns DEFAULT_BOOST_SCORE for each processed item. -std::unique_ptr create(PostingListRef Documents); - /// Returns AND Iterator which performs the intersection of the PostingLists of /// its children. /// diff --git a/clangd/index/dex/PostingList.cpp b/clangd/index/dex/PostingList.cpp new file mode 100644 index 000000000..99e534d1a --- /dev/null +++ b/clangd/index/dex/PostingList.cpp @@ -0,0 +1,84 @@ +//===--- PostingList.cpp - Symbol identifiers storage interface -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PostingList.h" +#include "Iterator.h" + +namespace clang { +namespace clangd { +namespace dex { + +namespace { + +/// Implements Iterator over std::vector. This is the most basic +/// iterator and is simply a wrapper around +/// std::vector::const_iterator. +class PlainIterator : public Iterator { +public: + explicit PlainIterator(llvm::ArrayRef Documents) + : Documents(Documents), Index(std::begin(Documents)) {} + + bool reachedEnd() const override { return Index == std::end(Documents); } + + /// Advances cursor to the next item. + void advance() override { + assert(!reachedEnd() && + "Posting List iterator can't advance() at the end."); + ++Index; + } + + /// Applies binary search to advance cursor to the next item with DocID + /// equal or higher than the given one. + void advanceTo(DocID ID) override { + assert(!reachedEnd() && + "Posting List iterator can't advance() at the end."); + // If current ID is beyond requested one, iterator is already in the right + // state. + if (peek() < ID) + Index = std::lower_bound(Index, std::end(Documents), ID); + } + + DocID peek() const override { + assert(!reachedEnd() && + "Posting List iterator can't peek() at the end."); + return *Index; + } + + float consume() override { + assert(!reachedEnd() && + "Posting List iterator can't consume() at the end."); + return DEFAULT_BOOST_SCORE; + } + + size_t estimateSize() const override { return Documents.size(); } + +private: + llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { + OS << '['; + if (Index != std::end(Documents)) + OS << *Index; + else + OS << "END"; + OS << ']'; + return OS; + } + + llvm::ArrayRef Documents; + llvm::ArrayRef::const_iterator Index; +}; + +} // namespace + +std::unique_ptr PostingList::iterator() const { + return llvm::make_unique(Documents); +} + +} // namespace dex +} // namespace clangd +} // namespace clang diff --git a/clangd/index/dex/PostingList.h b/clangd/index/dex/PostingList.h new file mode 100644 index 000000000..63762e4f7 --- /dev/null +++ b/clangd/index/dex/PostingList.h @@ -0,0 +1,52 @@ +//===--- PostingList.h - Symbol identifiers storage interface --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines posting list interface: a storage for identifiers of symbols +// which can be characterized by a specific feature (such as fuzzy-find trigram, +// scope, type or any other Search Token). Posting lists can be traversed in +// order using an iterator and are values for inverted index, which maps search +// tokens to corresponding posting lists. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_POSTINGLIST_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_POSTINGLIST_H + +#include "Iterator.h" +#include "llvm/ADT/ArrayRef.h" +#include +#include + +namespace clang { +namespace clangd { +namespace dex { + +class Iterator; + +/// PostingList is the storage of DocIDs which can be inserted to the Query +/// Tree as a leaf by constructing Iterator over the PostingList object. +// FIXME(kbobyrev): Use VByte algorithm to compress underlying data. +class PostingList { +public: + explicit PostingList(const std::vector &&Documents) + : Documents(std::move(Documents)) {} + + std::unique_ptr iterator() const; + + size_t bytes() const { return Documents.size() * sizeof(DocID); } + +private: + const std::vector Documents; +}; + +} // namespace dex +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_POSTINGLIST_H diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 5ce4da282..2deec6411 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -47,8 +47,8 @@ std::vector consumeIDs(Iterator &It) { } TEST(DexIterators, DocumentIterator) { - const PostingList L = {4, 7, 8, 20, 42, 100}; - auto DocIterator = create(L); + const PostingList L({4, 7, 8, 20, 42, 100}); + auto DocIterator = L.iterator(); EXPECT_EQ(DocIterator->peek(), 4U); EXPECT_FALSE(DocIterator->reachedEnd()); @@ -70,28 +70,28 @@ TEST(DexIterators, DocumentIterator) { } TEST(DexIterators, AndWithEmpty) { - const PostingList L0; - const PostingList L1 = {0, 5, 7, 10, 42, 320, 9000}; + const PostingList L0({}); + const PostingList L1({0, 5, 7, 10, 42, 320, 9000}); - auto AndEmpty = createAnd(create(L0)); + auto AndEmpty = createAnd(L0.iterator()); EXPECT_TRUE(AndEmpty->reachedEnd()); - auto AndWithEmpty = createAnd(create(L0), create(L1)); + auto AndWithEmpty = createAnd(L0.iterator(), L1.iterator()); EXPECT_TRUE(AndWithEmpty->reachedEnd()); EXPECT_THAT(consumeIDs(*AndWithEmpty), ElementsAre()); } TEST(DexIterators, AndTwoLists) { - const PostingList L0 = {0, 5, 7, 10, 42, 320, 9000}; - const PostingList L1 = {0, 4, 7, 10, 30, 60, 320, 9000}; + const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); + const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); - auto And = createAnd(create(L1), create(L0)); + auto And = createAnd(L1.iterator(), L0.iterator()); EXPECT_FALSE(And->reachedEnd()); EXPECT_THAT(consumeIDs(*And), ElementsAre(0U, 7U, 10U, 320U, 9000U)); - And = createAnd(create(L0), create(L1)); + And = createAnd(L0.iterator(), L1.iterator()); And->advanceTo(0); EXPECT_EQ(And->peek(), 0U); @@ -107,11 +107,11 @@ TEST(DexIterators, AndTwoLists) { } TEST(DexIterators, AndThreeLists) { - const PostingList L0 = {0, 5, 7, 10, 42, 320, 9000}; - const PostingList L1 = {0, 4, 7, 10, 30, 60, 320, 9000}; - const PostingList L2 = {1, 4, 7, 11, 30, 60, 320, 9000}; + const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); + const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); + const PostingList L2({1, 4, 7, 11, 30, 60, 320, 9000}); - auto And = createAnd(create(L0), create(L1), create(L2)); + auto And = createAnd(L0.iterator(), L1.iterator(), L2.iterator()); EXPECT_EQ(And->peek(), 7U); And->advanceTo(300); EXPECT_EQ(And->peek(), 320U); @@ -121,13 +121,13 @@ TEST(DexIterators, AndThreeLists) { } TEST(DexIterators, OrWithEmpty) { - const PostingList L0; - const PostingList L1 = {0, 5, 7, 10, 42, 320, 9000}; + const PostingList L0({}); + const PostingList L1({0, 5, 7, 10, 42, 320, 9000}); - auto OrEmpty = createOr(create(L0)); + auto OrEmpty = createOr(L0.iterator()); EXPECT_TRUE(OrEmpty->reachedEnd()); - auto OrWithEmpty = createOr(create(L0), create(L1)); + auto OrWithEmpty = createOr(L0.iterator(), L1.iterator()); EXPECT_FALSE(OrWithEmpty->reachedEnd()); EXPECT_THAT(consumeIDs(*OrWithEmpty), @@ -135,10 +135,10 @@ TEST(DexIterators, OrWithEmpty) { } TEST(DexIterators, OrTwoLists) { - const PostingList L0 = {0, 5, 7, 10, 42, 320, 9000}; - const PostingList L1 = {0, 4, 7, 10, 30, 60, 320, 9000}; + const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); + const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); - auto Or = createOr(create(L0), create(L1)); + auto Or = createOr(L0.iterator(), L1.iterator()); EXPECT_FALSE(Or->reachedEnd()); EXPECT_EQ(Or->peek(), 0U); @@ -161,18 +161,18 @@ TEST(DexIterators, OrTwoLists) { Or->advanceTo(9001); EXPECT_TRUE(Or->reachedEnd()); - Or = createOr(create(L0), create(L1)); + Or = createOr(L0.iterator(), L1.iterator()); EXPECT_THAT(consumeIDs(*Or), ElementsAre(0U, 4U, 5U, 7U, 10U, 30U, 42U, 60U, 320U, 9000U)); } TEST(DexIterators, OrThreeLists) { - const PostingList L0 = {0, 5, 7, 10, 42, 320, 9000}; - const PostingList L1 = {0, 4, 7, 10, 30, 60, 320, 9000}; - const PostingList L2 = {1, 4, 7, 11, 30, 60, 320, 9000}; + const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); + const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); + const PostingList L2({1, 4, 7, 11, 30, 60, 320, 9000}); - auto Or = createOr(create(L0), create(L1), create(L2)); + auto Or = createOr(L0.iterator(), L1.iterator(), L2.iterator()); EXPECT_FALSE(Or->reachedEnd()); EXPECT_EQ(Or->peek(), 0U); @@ -221,19 +221,19 @@ TEST(DexIterators, QueryTree) { // |1, 5, 7, 9| |1, 5| |0, 3, 5| // +----------+ +----+ +-------+ // - const PostingList L0 = {1, 3, 5, 8, 9}; - const PostingList L1 = {1, 5, 7, 9}; - const PostingList L3; - const PostingList L4 = {1, 5}; - const PostingList L5 = {0, 3, 5}; + const PostingList L0({1, 3, 5, 8, 9}); + const PostingList L1({1, 5, 7, 9}); + const PostingList L3({}); + const PostingList L4({1, 5}); + const PostingList L5({0, 3, 5}); // Root of the query tree: [1, 5] auto Root = createAnd( // Lower And Iterator: [1, 5, 9] - createAnd(create(L0), createBoost(create(L1), 2U)), + createAnd(L0.iterator(), createBoost(L1.iterator(), 2U)), // Lower Or Iterator: [0, 1, 5] - createOr(create(L3), createBoost(create(L4), 3U), - createBoost(create(L5), 4U))); + createOr(L3.iterator(), createBoost(L4.iterator(), 3U), + createBoost(L5.iterator(), 4U))); EXPECT_FALSE(Root->reachedEnd()); EXPECT_EQ(Root->peek(), 1U); @@ -255,40 +255,39 @@ TEST(DexIterators, QueryTree) { } TEST(DexIterators, StringRepresentation) { - const PostingList L0 = {4, 7, 8, 20, 42, 100}; - const PostingList L1 = {1, 3, 5, 8, 9}; - const PostingList L2 = {1, 5, 7, 9}; - const PostingList L3 = {0, 5}; - const PostingList L4 = {0, 1, 5}; - const PostingList L5; + const PostingList L0({4, 7, 8, 20, 42, 100}); + const PostingList L1({1, 3, 5, 8, 9}); + const PostingList L2({1, 5, 7, 9}); + const PostingList L3({0, 5}); + const PostingList L4({0, 1, 5}); + const PostingList L5({}); - EXPECT_EQ(llvm::to_string(*(create(L0))), "[{4}, 7, 8, 20, 42, 100, END]"); + EXPECT_EQ(llvm::to_string(*(L0.iterator())), "[4]"); - auto Nested = createAnd(createAnd(create(L1), create(L2)), - createOr(create(L3), create(L4), create(L5))); + auto Nested = + createAnd(createAnd(L1.iterator(), L2.iterator()), + createOr(L3.iterator(), L4.iterator(), L5.iterator())); - EXPECT_EQ(llvm::to_string(*Nested), - "(& (| [0, {5}, END] [0, {1}, 5, END] [{END}]) (& [{1}, 5, 7, 9, " - "END] [{1}, 3, 5, 8, 9, END]))"); + EXPECT_EQ(llvm::to_string(*Nested), "(& (| [5] [1] [END]) (& [1] [1]))"); } TEST(DexIterators, Limit) { - const PostingList L0 = {3, 6, 7, 20, 42, 100}; - const PostingList L1 = {1, 3, 5, 6, 7, 30, 100}; - const PostingList L2 = {0, 3, 5, 7, 8, 100}; + const PostingList L0({3, 6, 7, 20, 42, 100}); + const PostingList L1({1, 3, 5, 6, 7, 30, 100}); + const PostingList L2({0, 3, 5, 7, 8, 100}); - auto DocIterator = createLimit(create(L0), 42); + auto DocIterator = createLimit(L0.iterator(), 42); EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(3, 6, 7, 20, 42, 100)); - DocIterator = createLimit(create(L0), 3); + DocIterator = createLimit(L0.iterator(), 3); EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(3, 6, 7)); - DocIterator = createLimit(create(L0), 0); + DocIterator = createLimit(L0.iterator(), 0); EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre()); - auto AndIterator = - createAnd(createLimit(createTrue(9000), 343), createLimit(create(L0), 2), - createLimit(create(L1), 3), createLimit(create(L2), 42)); + auto AndIterator = createAnd( + createLimit(createTrue(9000), 343), createLimit(L0.iterator(), 2), + createLimit(L1.iterator(), 3), createLimit(L2.iterator(), 42)); EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(3, 7)); } @@ -297,10 +296,10 @@ TEST(DexIterators, True) { EXPECT_TRUE(TrueIterator->reachedEnd()); EXPECT_THAT(consumeIDs(*TrueIterator), ElementsAre()); - PostingList L0 = {1, 2, 5, 7}; + const PostingList L0({1, 2, 5, 7}); TrueIterator = createTrue(7U); EXPECT_THAT(TrueIterator->peek(), 0); - auto AndIterator = createAnd(create(L0), move(TrueIterator)); + auto AndIterator = createAnd(L0.iterator(), move(TrueIterator)); EXPECT_FALSE(AndIterator->reachedEnd()); EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(1, 2, 5)); } @@ -311,10 +310,10 @@ TEST(DexIterators, Boost) { auto ElementBoost = BoostIterator->consume(); EXPECT_THAT(ElementBoost, 42U); - const PostingList L0 = {2, 4}; - const PostingList L1 = {1, 4}; - auto Root = createOr(createTrue(5U), createBoost(create(L0), 2U), - createBoost(create(L1), 3U)); + const PostingList L0({2, 4}); + const PostingList L1({1, 4}); + auto Root = createOr(createTrue(5U), createBoost(L0.iterator(), 2U), + createBoost(L1.iterator(), 3U)); ElementBoost = Root->consume(); EXPECT_THAT(ElementBoost, Iterator::DEFAULT_BOOST_SCORE); @@ -453,10 +452,10 @@ TEST(Dex, Lookup) { } TEST(Dex, FuzzyFind) { - auto Index = Dex::build( - generateSymbols({"ns::ABC", "ns::BCD", "::ABC", "ns::nested::ABC", - "other::ABC", "other::A"}), - URISchemes); + auto Index = + Dex::build(generateSymbols({"ns::ABC", "ns::BCD", "::ABC", + "ns::nested::ABC", "other::ABC", "other::A"}), + URISchemes); FuzzyFindRequest Req; Req.Query = "ABC"; Req.Scopes = {"ns::"}; @@ -526,16 +525,14 @@ TEST(DexTest, FuzzyMatch) { } TEST(DexTest, MatchQualifiedNamesWithoutSpecificScope) { - auto I = - Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), URISchemes); + auto I = Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3")); } TEST(DexTest, MatchQualifiedNamesWithGlobalScope) { - auto I = - Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), URISchemes); + auto I = Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {""}; From 8e9fe29c87a3a90cf9036eaa380835b3a63ea72e Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Thu, 13 Sep 2018 23:50:20 +0000 Subject: [PATCH 197/686] Update a clang-tidy test for r342194 The location of implicit captures has changed. Update a use-after-move checker test to reflect that. This fixes a bot failure: http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-ubuntu-fast/builds/36500 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342195 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clang-tidy/bugprone-use-after-move.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/clang-tidy/bugprone-use-after-move.cpp b/test/clang-tidy/bugprone-use-after-move.cpp index 2d747b47c..e4596c8ea 100644 --- a/test/clang-tidy/bugprone-use-after-move.cpp +++ b/test/clang-tidy/bugprone-use-after-move.cpp @@ -339,7 +339,7 @@ void lambdas() { A a; std::move(a); auto lambda = [=]() { a.foo(); }; - // CHECK-MESSAGES: [[@LINE-1]]:27: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-1]]:20: warning: 'a' used after it was moved // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here } // Same tests but for capture by reference. @@ -354,7 +354,7 @@ void lambdas() { A a; std::move(a); auto lambda = [&]() { a.foo(); }; - // CHECK-MESSAGES: [[@LINE-1]]:27: warning: 'a' used after it was moved + // CHECK-MESSAGES: [[@LINE-1]]:20: warning: 'a' used after it was moved // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here } // But don't warn if the move happened after the capture. From ed4a450d45cbd6692e065085dd57834d7bbc156c Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Fri, 14 Sep 2018 00:56:11 +0000 Subject: [PATCH 198/686] [clangd] Fix TUScheduler typos git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342198 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/TUScheduler.cpp | 12 ++++++------ clangd/TUScheduler.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clangd/TUScheduler.cpp b/clangd/TUScheduler.cpp index 0ae2a3c9c..6ac21341f 100644 --- a/clangd/TUScheduler.cpp +++ b/clangd/TUScheduler.cpp @@ -18,7 +18,7 @@ // preamble. However, unlike AST, the same preamble can be read concurrently, so // we run each of async preamble reads on its own thread. // -// To limit the concurrent load that clangd produces we mantain a semaphore that +// To limit the concurrent load that clangd produces we maintain a semaphore that // keeps more than a fixed number of threads from running concurrently. // // Rationale for cancelling updates. @@ -165,7 +165,7 @@ class ASTWorker { /// The processing thread is spawned using \p Tasks. However, when \p Tasks /// is null, all requests will be processed on the calling thread /// synchronously instead. \p Barrier is acquired when processing each - /// request, it is be used to limit the number of actively running threads. + /// request, it is used to limit the number of actively running threads. static ASTWorkerHandle create(PathRef FileName, TUScheduler::ASTCache &IdleASTs, AsyncTaskRunner *Tasks, Semaphore &Barrier, @@ -188,7 +188,7 @@ class ASTWorker { void getCurrentPreamble( llvm::unique_function)>); /// Wait for the first build of preamble to finish. Preamble itself can be - /// accessed via getPossibleStalePreamble(). Note that this function will + /// accessed via getPossiblyStalePreamble(). Note that this function will /// return after an unsuccessful build of the preamble too, i.e. result of /// getPossiblyStalePreamble() can be null even after this function returns. void waitForFirstPreamble() const; @@ -207,7 +207,7 @@ class ASTWorker { void startTask(llvm::StringRef Name, llvm::unique_function Task, llvm::Optional UpdateType); /// Determines the next action to perform. - /// All actions that should never run are disarded. + /// All actions that should never run are discarded. /// Returns a deadline for the next action. If it's expired, run now. /// scheduleLocked() is called again at the deadline, or if requests arrive. Deadline scheduleLocked(); @@ -227,7 +227,7 @@ class ASTWorker { const bool RunSync; /// Time to wait after an update to see whether another update obsoletes it. const steady_clock::duration UpdateDebounce; - /// File that ASTWorker is reponsible for. + /// File that ASTWorker is responsible for. const Path FileName; /// Whether to keep the built preambles in memory or on disk. const bool StorePreambleInMemory; @@ -417,7 +417,7 @@ void ASTWorker::update( // We want to report the diagnostics even if this update was cancelled. // It seems more useful than making the clients wait indefinitely if they // spam us with updates. - // Note *AST can be still be null if buildAST fails. + // Note *AST can still be null if buildAST fails. if (*AST) { OnUpdated((*AST)->getDiagnostics()); trace::Span Span("Running main AST callback"); diff --git a/clangd/TUScheduler.h b/clangd/TUScheduler.h index e049d3d8d..bfe495e2d 100644 --- a/clangd/TUScheduler.h +++ b/clangd/TUScheduler.h @@ -176,7 +176,7 @@ class TUScheduler { }; /// Runs \p Action asynchronously with a new std::thread. The context will be -/// propogated. +/// propagated. template std::future runAsync(llvm::unique_function Action) { return std::async(std::launch::async, From e36c0484d7fb9fcb3b520b76f1f43a00c0990bad Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Fri, 14 Sep 2018 11:39:05 +0000 Subject: [PATCH 199/686] [clangd] Update IndexerMain.cpp file comment after rename. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342226 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/indexer/IndexerMain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/indexer/IndexerMain.cpp b/clangd/indexer/IndexerMain.cpp index 918b58789..fc85c54e2 100644 --- a/clangd/indexer/IndexerMain.cpp +++ b/clangd/indexer/IndexerMain.cpp @@ -1,4 +1,4 @@ -//===--- GlobalSymbolBuilderMain.cpp -----------------------------*- C++-*-===// +//===--- IndexerMain.cpp -----------------------------------------*- C++-*-===// // // The LLVM Compiler Infrastructure // From 044c2b418766a23946732b4a73f63603f889e192 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Fri, 14 Sep 2018 12:21:09 +0000 Subject: [PATCH 200/686] [clangd] NFC: Fix IndexBenchmark CLI arguments handling git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342227 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/benchmarks/IndexBenchmark.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clangd/benchmarks/IndexBenchmark.cpp b/clangd/benchmarks/IndexBenchmark.cpp index 5714a7c62..dc868f8d2 100644 --- a/clangd/benchmarks/IndexBenchmark.cpp +++ b/clangd/benchmarks/IndexBenchmark.cpp @@ -101,9 +101,11 @@ int main(int argc, char *argv[]) { } IndexFilename = argv[1]; RequestsFilename = argv[2]; - // Trim first two arguments of the benchmark invocation. - argv += 3; - argc -= 3; + // Trim first two arguments of the benchmark invocation and pretend no + // arguments were passed in the first place. + argv[2] = argv[0]; + argv += 2; + argc -= 2; ::benchmark::Initialize(&argc, argv); ::benchmark::RunSpecifiedBenchmarks(); } From 7eb3c808a2ac1000dd73052ad4b106074dc65ea1 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Fri, 14 Sep 2018 12:32:08 +0000 Subject: [PATCH 201/686] [clangd] Don't double-infer compile commands after r342228 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342229 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/GlobalCompilationDatabase.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/clangd/GlobalCompilationDatabase.cpp b/clangd/GlobalCompilationDatabase.cpp index 6ed236e93..816eb8ff0 100644 --- a/clangd/GlobalCompilationDatabase.cpp +++ b/clangd/GlobalCompilationDatabase.cpp @@ -95,8 +95,6 @@ DirectoryBasedGlobalCompilationDatabase::getCDBInDirLocked(PathRef Dir) const { return CachedIt->second.get(); std::string Error = ""; auto CDB = tooling::CompilationDatabase::loadFromDirectory(Dir, Error); - if (CDB) - CDB = tooling::inferMissingCompileCommands(std::move(CDB)); auto Result = CDB.get(); CompilationDatabases.insert(std::make_pair(Dir, std::move(CDB))); return Result; From d01c9058283ac279107c389b3a1a71f380f5b150 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Fri, 14 Sep 2018 12:36:06 +0000 Subject: [PATCH 202/686] [clangd] Don't override the preamble while completing inside it, it doesn't work. Summary: To stay fast, enable single-file-mode instead. This is fine since completions in the preamble are simple. The net effect for now is to suppress the spurious TopLevel completions when completing inside the preamble. Once Sema has include directive completion, this will be more important. Reviewers: ilya-biryukov Subscribers: ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52071 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342230 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 13 +++++++++++-- unittests/clangd/CodeCompleteTests.cpp | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 64d38dd7f..0f3dab83a 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -40,6 +40,7 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Index/USRGeneration.h" +#include "clang/Lex/PreprocessorOptions.h" #include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Sema/Sema.h" #include "clang/Tooling/Core/Replacement.h" @@ -1053,11 +1054,19 @@ bool semaCodeComplete(std::unique_ptr Consumer, // We reuse the preamble whether it's valid or not. This is a // correctness/performance tradeoff: building without a preamble is slow, and // completion is latency-sensitive. + // However, if we're completing *inside* the preamble section of the draft, + // overriding the preamble will break sema completion. Fortunately we can just + // skip all includes in this case; these completions are really simple. + bool CompletingInPreamble = + ComputePreambleBounds(*CI->getLangOpts(), ContentsBuffer.get(), 0).Size > + *Offset; // NOTE: we must call BeginSourceFile after prepareCompilerInstance. Otherwise // the remapped buffers do not get freed. auto Clang = prepareCompilerInstance( - std::move(CI), Input.Preamble, std::move(ContentsBuffer), - std::move(Input.PCHs), std::move(Input.VFS), DummyDiagsConsumer); + std::move(CI), CompletingInPreamble ? nullptr : Input.Preamble, + std::move(ContentsBuffer), std::move(Input.PCHs), std::move(Input.VFS), + DummyDiagsConsumer); + Clang->getPreprocessorOpts().SingleFileParseMode = CompletingInPreamble; Clang->setCodeCompletionConsumer(Consumer.release()); SyntaxOnlyAction Action; diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 248dab8ca..9a7037612 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -657,6 +657,22 @@ TEST(CompletionTest, IndexSuppressesPreambleCompletions) { UnorderedElementsAre(Named("local"), Named("preamble"))); } +// This verifies that we get normal preprocessor completions in the preamble. +// This is a regression test for an old bug: if we override the preamble and +// try to complete inside it, clang kicks our completion point just outside the +// preamble, resulting in always getting top-level completions. +TEST(CompletionTest, CompletionInPreamble) { + EXPECT_THAT(completions(R"cpp( + #ifnd^ef FOO_H_ + #define BAR_H_ + #include + int foo() {} + #endif + )cpp") + .Completions, + ElementsAre(Named("ifndef"))); +}; + TEST(CompletionTest, DynamicIndexMultiFile) { MockFSProvider FS; MockCompilationDatabase CDB; From e3bc2b4333483ce018d2e9aef97bef02c66a0119 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Fri, 14 Sep 2018 18:05:30 +0000 Subject: [PATCH 203/686] [modernize-use-transparent-functors] TypeLocs can be implicitly created, don't crash when encountering those. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342252 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/modernize/UseTransparentFunctorsCheck.cpp | 2 ++ test/clang-tidy/modernize-use-transparent-functors.cpp | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp b/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp index 0389a5ed7..74f050bab 100644 --- a/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp +++ b/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp @@ -121,6 +121,8 @@ void UseTransparentFunctorsCheck::check( return; SourceLocation ReportLoc = FunctorLoc.getLocation(); + if (ReportLoc.isInvalid()) + return; diag(ReportLoc, Message) << (FuncClass->getName() + "<>").str() << FixItHint::CreateRemoval( FunctorTypeLoc.getArgLoc(0).getSourceRange()); diff --git a/test/clang-tidy/modernize-use-transparent-functors.cpp b/test/clang-tidy/modernize-use-transparent-functors.cpp index b700f54f4..3efa916b2 100644 --- a/test/clang-tidy/modernize-use-transparent-functors.cpp +++ b/test/clang-tidy/modernize-use-transparent-functors.cpp @@ -104,4 +104,7 @@ int main() { std::set2 control; } - +struct ImplicitTypeLoc : std::set2> { + // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: prefer transparent functors + ImplicitTypeLoc() {} +}; From 2a36e80f14a2636f43b3a2a2bdb6433a2ff2ede7 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Fri, 14 Sep 2018 18:49:16 +0000 Subject: [PATCH 204/686] [clangd] Work around compiler macro expansion bugs(?) in completion tests git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342261 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/CodeCompleteTests.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 9a7037612..e6720a31f 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -662,15 +662,15 @@ TEST(CompletionTest, IndexSuppressesPreambleCompletions) { // try to complete inside it, clang kicks our completion point just outside the // preamble, resulting in always getting top-level completions. TEST(CompletionTest, CompletionInPreamble) { - EXPECT_THAT(completions(R"cpp( + auto Results = completions(R"cpp( #ifnd^ef FOO_H_ #define BAR_H_ #include int foo() {} #endif )cpp") - .Completions, - ElementsAre(Named("ifndef"))); + .Completions; + EXPECT_THAT(Results, ElementsAre(Named("ifndef"))); }; TEST(CompletionTest, DynamicIndexMultiFile) { From af8c430e6ee8d2b844569a92f6983aa6dcff23ee Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Fri, 14 Sep 2018 19:42:37 +0000 Subject: [PATCH 205/686] [NFC][clangd] silence pedantic extra ';' warning git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342267 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/CodeCompleteTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index e6720a31f..29781428b 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -671,7 +671,7 @@ TEST(CompletionTest, CompletionInPreamble) { )cpp") .Completions; EXPECT_THAT(Results, ElementsAre(Named("ifndef"))); -}; +} TEST(CompletionTest, DynamicIndexMultiFile) { MockFSProvider FS; From dbc5a1d5087a2b7c068f6258e8f5c6304611aa6a Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Fri, 14 Sep 2018 20:51:07 +0000 Subject: [PATCH 206/686] Mark index-tools.test as REQUIRES: shell so that it does not run with the internal lit shell which does not support "if" git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342282 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clangd/index-tools.test | 1 + 1 file changed, 1 insertion(+) diff --git a/test/clangd/index-tools.test b/test/clangd/index-tools.test index e17d2fc84..93cf56fea 100644 --- a/test/clangd/index-tools.test +++ b/test/clangd/index-tools.test @@ -1,5 +1,6 @@ # RUN: clangd-indexer %p/Inputs/BenchmarkSource.cpp -- -I%p/Inputs > %t.index # FIXME: By default, benchmarks are excluded from the list of default targets hence not built. Find a way to depend on benchmarks to run the next command. +# REQUIRES: shell # RUN: if [ -f %clangd-benchmark-dir/IndexBenchmark ]; then %clangd-benchmark-dir/IndexBenchmark %t.index %p/Inputs/requests.json --benchmark_min_time=0.01 ; fi # Pass invalid JSON file and check that IndexBenchmark fails to parse it. # RUN: if [ -f %clangd-benchmark-dir/IndexBenchmark ]; then not %clangd-benchmark-dir/IndexBenchmark %t.index %t --benchmark_min_time=0.01 ; fi From b534b3e6bde4dea5d3057fa5d08e4f30c7e34c4b Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Mon, 17 Sep 2018 07:43:49 +0000 Subject: [PATCH 207/686] [clangd] Get rid of AST matchers in SymbolCollector. NFC Reviewers: ilya-biryukov, kadircet Subscribers: MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D52089 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342362 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/SymbolCollector.cpp | 49 +++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp index 2bf16f59c..35f3edb42 100644 --- a/clangd/index/SymbolCollector.cpp +++ b/clangd/index/SymbolCollector.cpp @@ -15,12 +15,16 @@ #include "Logger.h" #include "SourceCode.h" #include "URI.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" #include "clang/Index/IndexSymbol.h" #include "clang/Index/USRGeneration.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" @@ -235,6 +239,13 @@ RefKind toRefKind(index::SymbolRoleSet Roles) { return static_cast(static_cast(RefKind::All) & Roles); } +template bool explicitTemplateSpecialization(const NamedDecl &ND) { + if (const auto *TD = llvm::dyn_cast(&ND)) + if (TD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return true; + return false; +} + } // namespace SymbolCollector::SymbolCollector(Options Opts) : Opts(std::move(Opts)) {} @@ -271,21 +282,33 @@ bool SymbolCollector::shouldCollectSymbol(const NamedDecl &ND, // FunctionDecl, BlockDecl, ObjCMethodDecl and OMPDeclareReductionDecl. // FIXME: Need a matcher for ExportDecl in order to include symbols declared // within an export. - auto InNonLocalContext = hasDeclContext(anyOf( - translationUnitDecl(), namespaceDecl(), linkageSpecDecl(), recordDecl(), - enumDecl(), objcProtocolDecl(), objcInterfaceDecl(), objcCategoryDecl(), - objcCategoryImplDecl(), objcImplementationDecl())); - // Don't index template specializations and expansions in main files. - auto IsSpecialization = - anyOf(functionDecl(isExplicitTemplateSpecialization()), - cxxRecordDecl(isExplicitTemplateSpecialization()), - varDecl(isExplicitTemplateSpecialization())); - if (match(decl(allOf(unless(isExpansionInMainFile()), InNonLocalContext, - unless(IsSpecialization))), - ND, ASTCtx) - .empty()) + const auto *DeclCtx = ND.getDeclContext(); + switch (DeclCtx->getDeclKind()) { + case Decl::TranslationUnit: + case Decl::Namespace: + case Decl::LinkageSpec: + case Decl::Enum: + case Decl::ObjCProtocol: + case Decl::ObjCInterface: + case Decl::ObjCCategory: + case Decl::ObjCCategoryImpl: + case Decl::ObjCImplementation: + break; + default: + // Record has a few derivations (e.g. CXXRecord, Class specialization), it's + // easier to cast. + if (!llvm::isa(DeclCtx)) + return false; + } + if (explicitTemplateSpecialization(ND) || + explicitTemplateSpecialization(ND) || + explicitTemplateSpecialization(ND)) return false; + const auto &SM = ASTCtx.getSourceManager(); + // Skip decls in the main file. + if (SM.isInMainFile(SM.getExpansionLoc(ND.getBeginLoc()))) + return false; // Avoid indexing internal symbols in protobuf generated headers. if (isPrivateProtoDecl(ND)) return false; From 09ab9aed3211bd220350f4eb5b3319c86ed62803 Mon Sep 17 00:00:00 2001 From: Idriss Riouak Date: Mon, 17 Sep 2018 12:29:29 +0000 Subject: [PATCH 208/686] [Clang-Tidy: modernize] Fix for modernize-redundant-void-arg: complains about variable cast to void Summary: Hello, i would like to suggest a fix for one of the checks in clang-tidy.The bug was reported in https://bugs.llvm.org/show_bug.cgi?id=32575 where you can find more information. For example: ``` template struct S { template void g() const { int a; (void)a; } }; void f() { S().g(); } ``` this piece of code should not trigger any warning by the check modernize-redundant-void-arg but when we execute the following command ``` clang_tidy -checks=-*,modernize-redundant-void-arg test.cpp -- -std=c++11 ``` we obtain the following warning: /Users/eco419/Desktop/clang-tidy.project/void-redundand_2/test.cpp:6:6: warning: redundant void argument list in function declaration [modernize-redundant-void-arg] (void)a; ^~~~ Reviewers: aaron.ballman, hokein, alexfh, JonasToth Reviewed By: aaron.ballman, JonasToth Subscribers: JonasToth, lebedev.ri, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D52135 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342388 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../modernize/RedundantVoidArgCheck.cpp | 2 +- .../modernize-redundant-void-arg.cpp | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/clang-tidy/modernize/RedundantVoidArgCheck.cpp b/clang-tidy/modernize/RedundantVoidArgCheck.cpp index 51dc5a2cc..ea49ab7bb 100644 --- a/clang-tidy/modernize/RedundantVoidArgCheck.cpp +++ b/clang-tidy/modernize/RedundantVoidArgCheck.cpp @@ -49,7 +49,7 @@ void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) { return; Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()), - unless(isExternC())) + unless(isInstantiated()), unless(isExternC())) .bind(FunctionId), this); Finder->addMatcher(typedefNameDecl().bind(TypedefId), this); diff --git a/test/clang-tidy/modernize-redundant-void-arg.cpp b/test/clang-tidy/modernize-redundant-void-arg.cpp index 750e5c1b2..453a4816a 100644 --- a/test/clang-tidy/modernize-redundant-void-arg.cpp +++ b/test/clang-tidy/modernize-redundant-void-arg.cpp @@ -488,3 +488,64 @@ void lambda_expression_with_macro_test(){ // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant void argument list in lambda expression [modernize-redundant-void-arg] // CHECK-FIXES: []() BODY; } + +struct S_1 { + void g_1(void) const { + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg] + // CHECK-FIXES: void g_1() const { + int a; + (void)a; + } + + void g_2() const { + int a; + (void)a; + } +}; + +template +struct S_2 { + void g_1(void) const { + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg] + // CHECK-FIXES: void g_1() const { + int a; + (void)a; + } + + void g_2() const { + int a; + (void)a; + } +}; + +template +struct S_3 { + template + void g_1(void) const { + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg] + // CHECK-FIXES: void g_1() const { + int a; + (void)a; + } + template + void g_2() const { + int a; + (void)a; + } +}; + +template +void g_3(void) { + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant void argument list in function definition [modernize-redundant-void-arg] + // CHECK-FIXES: void g_3(){ + int a; + (void)a; +} + +//Template instantiation +void f_testTemplate() { + S_1(); + S_2(); + S_3(); + g_3(); +} From 77ed1cd838a249d6134de9a6bdbe17ef46ecf946 Mon Sep 17 00:00:00 2001 From: Idriss Riouak Date: Mon, 17 Sep 2018 12:58:19 +0000 Subject: [PATCH 209/686] Fix git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342389 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clang-tidy/modernize-redundant-void-arg.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/clang-tidy/modernize-redundant-void-arg.cpp b/test/clang-tidy/modernize-redundant-void-arg.cpp index 453a4816a..44a726b5c 100644 --- a/test/clang-tidy/modernize-redundant-void-arg.cpp +++ b/test/clang-tidy/modernize-redundant-void-arg.cpp @@ -491,7 +491,7 @@ void lambda_expression_with_macro_test(){ struct S_1 { void g_1(void) const { - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant void argument list in function definition [modernize-redundant-void-arg] // CHECK-FIXES: void g_1() const { int a; (void)a; @@ -506,7 +506,7 @@ struct S_1 { template struct S_2 { void g_1(void) const { - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant void argument list in function definition [modernize-redundant-void-arg] // CHECK-FIXES: void g_1() const { int a; (void)a; @@ -522,7 +522,7 @@ template struct S_3 { template void g_1(void) const { - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg] + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant void argument list in function definition [modernize-redundant-void-arg] // CHECK-FIXES: void g_1() const { int a; (void)a; @@ -537,7 +537,7 @@ struct S_3 { template void g_3(void) { // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant void argument list in function definition [modernize-redundant-void-arg] - // CHECK-FIXES: void g_3(){ + // CHECK-FIXES: void g_3() { int a; (void)a; } From 00a59116251af48ae58c301873e4397b0261daeb Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Mon, 17 Sep 2018 13:55:10 +0000 Subject: [PATCH 210/686] [clang-tidy] fix PR37913, templated exception factory diagnosed correctly Summary: PR37913 documents wrong behaviour for a templated exception factory function. The check does misidentify dependent types as not derived from std::exception. The fix to this problem is to ignore dependent types, the analysis works correctly on the instantiated function. Reviewers: aaron.ballman, alexfh, hokein, ilya-biryukov Reviewed By: alexfh Subscribers: lebedev.ri, nemanjai, mgorny, kbarton, xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D48714 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342393 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/hicpp/ExceptionBaseclassCheck.cpp | 34 +++- test/clang-tidy/hicpp-exception-baseclass.cpp | 172 ++++++++++++++---- 2 files changed, 163 insertions(+), 43 deletions(-) diff --git a/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp b/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp index 3ea56d2f7..b299151cc 100644 --- a/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp +++ b/clang-tidy/hicpp/ExceptionBaseclassCheck.cpp @@ -22,26 +22,44 @@ void ExceptionBaseclassCheck::registerMatchers(MatchFinder *Finder) { return; Finder->addMatcher( - cxxThrowExpr(allOf(has(expr(unless(hasType(qualType(hasCanonicalType( - hasDeclaration(cxxRecordDecl(isSameOrDerivedFrom( - hasName("std::exception")))))))))), - has(expr(unless(cxxUnresolvedConstructExpr()))), - eachOf(has(expr(hasType(namedDecl().bind("decl")))), - anything()))) + cxxThrowExpr( + allOf( + unless(has(expr(anyOf(isTypeDependent(), isValueDependent())))), + // The thrown value is not derived from 'std::exception'. + has(expr(unless(hasType( + qualType(hasCanonicalType(hasDeclaration(cxxRecordDecl( + isSameOrDerivedFrom(hasName("::std::exception")))))))))), + // This condition is always true, but will bind to the + // template value if the thrown type is templated. + anyOf(has(expr(hasType( + substTemplateTypeParmType().bind("templ_type")))), + anything()), + // Bind to the declaration of the type of the value that + // is thrown. 'anything()' is necessary to always suceed + // in the 'eachOf' because builtin types are not + // 'namedDecl'. + eachOf(has(expr(hasType(namedDecl().bind("decl")))), anything()))) .bind("bad_throw"), this); } void ExceptionBaseclassCheck::check(const MatchFinder::MatchResult &Result) { const auto *BadThrow = Result.Nodes.getNodeAs("bad_throw"); + assert(BadThrow && "Did not match the throw expression"); diag(BadThrow->getSubExpr()->getBeginLoc(), "throwing an exception whose " "type %0 is not derived from " "'std::exception'") << BadThrow->getSubExpr()->getType() << BadThrow->getSourceRange(); - const auto *TypeDecl = Result.Nodes.getNodeAs("decl"); - if (TypeDecl != nullptr) + if (const auto *Template = + Result.Nodes.getNodeAs("templ_type")) + diag(BadThrow->getSubExpr()->getBeginLoc(), + "type %0 is a template instantiation of %1", DiagnosticIDs::Note) + << BadThrow->getSubExpr()->getType() + << Template->getReplacedParameter()->getDecl(); + + if (const auto *TypeDecl = Result.Nodes.getNodeAs("decl")) diag(TypeDecl->getBeginLoc(), "type defined here", DiagnosticIDs::Note); } diff --git a/test/clang-tidy/hicpp-exception-baseclass.cpp b/test/clang-tidy/hicpp-exception-baseclass.cpp index fdf8093b7..b5e405a69 100644 --- a/test/clang-tidy/hicpp-exception-baseclass.cpp +++ b/test/clang-tidy/hicpp-exception-baseclass.cpp @@ -2,6 +2,7 @@ namespace std { class exception {}; +class invalid_argument : public exception {}; } // namespace std class derived_exception : public std::exception {}; @@ -36,12 +37,12 @@ void problematic() { try { throw non_derived_exception(); // CHECK-NOTES: [[@LINE-1]]:11: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' - // CHECK-NOTES: 9:1: note: type defined here + // CHECK-NOTES: 10:1: note: type defined here } catch (non_derived_exception &e) { } throw non_derived_exception(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' - // CHECK-NOTES: 9:1: note: type defined here + // CHECK-NOTES: 10:1: note: type defined here // FIXME: More complicated kinds of inheritance should be checked later, but there is // currently no way use ASTMatchers for this kind of task. @@ -49,25 +50,25 @@ void problematic() { // Handle private inheritance cases correctly. try { throw bad_inheritance(); - // CHECK MESSAGES: [[@LINE-1]]:11: warning: throwing an exception whose type 'bad_inheritance' is not derived from 'std::exception' - // CHECK MESSAGES: 10:1: note: type defined here + // CHECK NOTES: [[@LINE-1]]:11: warning: throwing an exception whose type 'bad_inheritance' is not derived from 'std::exception' + // CHECK NOTES: 11:1: note: type defined here throw no_good_inheritance(); - // CHECK MESSAGES: [[@LINE-1]]:11: warning: throwing an exception whose type 'no_good_inheritance' is not derived from 'std::exception' - // CHECK MESSAGES: 11:1: note: type defined here + // CHECK NOTES: [[@LINE-1]]:11: warning: throwing an exception whose type 'no_good_inheritance' is not derived from 'std::exception' + // CHECK NOTES: 12:1: note: type defined here throw really_creative(); - // CHECK MESSAGES: [[@LINE-1]]:11: warning: throwing an exception whose type 'really_creative' is not derived from 'std::exception' - // CHECK MESSAGES: 12:1: note: type defined here + // CHECK NOTES: [[@LINE-1]]:11: warning: throwing an exception whose type 'really_creative' is not derived from 'std::exception' + // CHECK NOTES: 13:1: note: type defined here } catch (...) { } throw bad_inheritance(); - // CHECK MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_inheritance' is not derived from 'std::exception' - // CHECK MESSAGES: 10:1: note: type defined here + // CHECK NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_inheritance' is not derived from 'std::exception' + // CHECK NOTES: 11:1: note: type defined here throw no_good_inheritance(); - // CHECK MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'no_good_inheritance' is not derived from 'std::exception' - // CHECK MESSAGES: 11:1: note: type defined here + // CHECK NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'no_good_inheritance' is not derived from 'std::exception' + // CHECK NOTES: 12:1: note: type defined here throw really_creative(); - // CHECK MESSAGES: [[@LINE-1]]:9: warning: throwing an exception whose type 'really_creative' is not derived from 'std::exception' - // CHECK MESSAGES: 12:1: note: type defined here + // CHECK NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'really_creative' is not derived from 'std::exception' + // CHECK NOTES: 13:1: note: type defined here #endif } @@ -91,24 +92,40 @@ void allowed_throws() { throw deep_hierarchy(); // Ok try { - throw terrible_idea(); // Ok, but multiple inheritance isn't clean + throw terrible_idea(); // Ok, but multiple inheritance isn't clean } catch (std::exception &e) { // Can be caught as std::exception, even with multiple inheritance } throw terrible_idea(); // Ok, but multiple inheritance } +void test_lambdas() { + auto BadLambda = []() { throw int(42); }; + // CHECK-NOTES: [[@LINE-1]]:33: warning: throwing an exception whose type 'int' is not derived from 'std::exception' + auto GoodLambda = []() { throw derived_exception(); }; +} + // Templated function that throws exception based on template type template void ThrowException() { throw T(); } // CHECK-NOTES: [[@LINE-1]]:31: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' -// CHECK-NOTES: 120:1: note: type defined here -// CHECK-NOTES: [[@LINE-3]]:31: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' -// CHECK-NOTES: 120:1: note: type defined here -// CHECK-NOTES: [[@LINE-5]]:31: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' -// CHECK-NOTES: 123:1: note: type defined here -// CHECK-NOTES: [[@LINE-7]]:31: warning: throwing an exception whose type 'int' is not derived from 'std::exception' -// CHECK-NOTES: [[@LINE-8]]:31: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' -// CHECK-NOTES: 9:1: note: type defined here +// CHECK-NOTES: [[@LINE-2]]:31: note: type 'bad_generic_exception' is a template instantiation of 'T' +// CHECK-NOTES: [[@LINE+25]]:1: note: type defined here + +// CHECK-NOTES: [[@LINE-5]]:31: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' +// CHECK-NOTES: [[@LINE-6]]:31: note: type 'bad_generic_exception' is a template instantiation of 'T' +// CHECK-NOTES: [[@LINE+21]]:1: note: type defined here + +// CHECK-NOTES: [[@LINE-9]]:31: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' +// CHECK-NOTES: [[@LINE-10]]:31: note: type 'exotic_exception' is a template instantiation of 'T' +// CHECK-NOTES: [[@LINE+20]]:1: note: type defined here + +// CHECK-NOTES: [[@LINE-13]]:31: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +// CHECK-NOTES: [[@LINE-14]]:31: note: type 'int' is a template instantiation of 'T' + +// CHECK-NOTES: [[@LINE-16]]:31: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' +// CHECK-NOTES: [[@LINE-17]]:31: note: type 'non_derived_exception' is a template instantiation of 'T' +// CHECK-NOTES: 10:1: note: type defined here + #define THROW_EXCEPTION(CLASS) ThrowException() #define THROW_BAD_EXCEPTION throw int(42); #define THROW_GOOD_EXCEPTION throw std::exception(); @@ -125,17 +142,14 @@ class exotic_exception : public T {}; void generic_exceptions() { THROW_EXCEPTION(int); - // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'int' is not derived from 'std::exception' THROW_EXCEPTION(non_derived_exception); - // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'non_derived_exception' is not derived from 'std::exception' - // CHECK MESSAGES: 9:1: note: type defined here THROW_EXCEPTION(std::exception); // Ok THROW_EXCEPTION(derived_exception); // Ok THROW_EXCEPTION(deep_hierarchy); // Ok THROW_BAD_EXCEPTION; // CHECK-NOTES: [[@LINE-1]]:3: warning: throwing an exception whose type 'int' is not derived from 'std::exception' - // CHECK-NOTES: [[@LINE-25]]:35: note: expanded from macro 'THROW_BAD_EXCEPTION' + // CHECK-NOTES: [[@LINE-22]]:35: note: expanded from macro 'THROW_BAD_EXCEPTION' THROW_GOOD_EXCEPTION; THROW_DERIVED_EXCEPTION; @@ -144,20 +158,17 @@ void generic_exceptions() { throw bad_generic_exception(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' - // CHECK-NOTES: 120:1: note: type defined here + // CHECK-NOTES: [[@LINE-24]]:1: note: type defined here throw bad_generic_exception(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' - // CHECK-NOTES: 120:1: note: type defined here + // CHECK-NOTES: [[@LINE-27]]:1: note: type defined here THROW_EXCEPTION(bad_generic_exception); - // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' THROW_EXCEPTION(bad_generic_exception); - // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'bad_generic_exception' is not derived from 'std::exception' throw exotic_exception(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' - // CHECK-NOTES: 123:1: note: type defined here + // CHECK-NOTES: [[@LINE-30]]:1: note: type defined here THROW_EXCEPTION(exotic_exception); - // CHECK MESSAGES: [[@LINE-1]]:3: warning: throwing an exception whose type 'exotic_exception' is not derived from 'std::exception' throw exotic_exception(); // Ok THROW_EXCEPTION(exotic_exception); // Ok @@ -172,11 +183,102 @@ using UsingGood = deep_hierarchy; void typedefed() { throw TypedefedBad(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'TypedefedBad' (aka 'int') is not derived from 'std::exception' - // CHECK-NOTES: 167:1: note: type defined here + // CHECK-NOTES: [[@LINE-8]]:1: note: type defined here throw TypedefedGood(); // Ok throw UsingBad(); // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'UsingBad' (aka 'int') is not derived from 'std::exception' - // CHECK-NOTES: 169:1: note: type defined here + // CHECK-NOTES: [[@LINE-11]]:1: note: type defined here throw UsingGood(); // Ok } + +// Fix PR37913 +struct invalid_argument_maker { + ::std::invalid_argument operator()() const; +}; +struct int_maker { + int operator()() const; +}; + +template +void templated_thrower() { + throw T{}(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +} +template +void templated_thrower2() { + T ExceptionFactory; // This test found a which did not happend with 'throw T{}()' + throw ExceptionFactory(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +} + +void exception_created_with_function() { + templated_thrower(); + templated_thrower(); + + templated_thrower2(); + templated_thrower2(); + + throw invalid_argument_maker{}(); + throw int_maker{}(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +} + +struct invalid_argument_factory { + ::std::invalid_argument make_exception() const; +}; + +struct int_factory { + int make_exception() const; +}; + +template +void templated_factory() { + T f; + throw f.make_exception(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +} +template +void templated_factory2() { + throw T().make_exception(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +} + +void exception_from_factory() { + templated_factory(); + templated_factory(); + + templated_factory2(); + templated_factory2(); + + throw invalid_argument_factory().make_exception(); + throw int_factory().make_exception(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' + + invalid_argument_factory inv_f; + throw inv_f.make_exception(); + + int_factory int_f; + throw int_f.make_exception(); + // CHECK-NOTES: [[@LINE-1]]:9: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +} + +template +struct ThrowClassTemplateParam { + ThrowClassTemplateParam() { throw T(); } + // CHECK-NOTES: [[@LINE-1]]:37: warning: throwing an exception whose type 'int' is not derived from 'std::exception' + // CHECK-NOTES: [[@LINE-2]]:37: note: type 'int' is a template instantiation of 'T' +}; + +template +struct ThrowValueTemplate { + ThrowValueTemplate() { throw V; } + // CHECK-NOTES: [[@LINE-1]]:32: warning: throwing an exception whose type 'int' is not derived from 'std::exception' +}; + +void class_templates() { + ThrowClassTemplateParam IntThrow; + ThrowClassTemplateParam ArgThrow; + + ThrowValueTemplate<42> ValueThrow; +} From 07eed56c96e3b2151f78166f3a430f9415a3a110 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Mon, 17 Sep 2018 17:59:51 +0000 Subject: [PATCH 211/686] [clang-tidy] Remove duplicated logic in UnnecessaryValueParamCheck and use FunctionParmMutationAnalyzer instead. Reviewers: alexfh, JonasToth, george.karpenkov Subscribers: xazax.hun, kristof.beyls, chrib, a.sidorin, Szelethus, cfe-commits Differential Revision: https://reviews.llvm.org/D52158 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342403 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../UnnecessaryValueParamCheck.cpp | 23 +++++++------------ .../performance/UnnecessaryValueParamCheck.h | 4 ++++ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp index 29a8b84cc..db5a12f9b 100644 --- a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp +++ b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp @@ -13,7 +13,6 @@ #include "../utils/FixItHintUtils.h" #include "../utils/Matchers.h" #include "../utils/TypeTraits.h" -#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/Preprocessor.h" @@ -92,21 +91,11 @@ void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) { const auto *Param = Result.Nodes.getNodeAs("param"); const auto *Function = Result.Nodes.getNodeAs("functionDecl"); - // Do not trigger on non-const value parameters when they are mutated either - // within the function body or within init expression(s) when the function is - // a ctor. - if (ExprMutationAnalyzer(*Function->getBody(), *Result.Context) - .isMutated(Param)) + FunctionParmMutationAnalyzer &Analyzer = + MutationAnalyzers.try_emplace(Function, *Function, *Result.Context) + .first->second; + if (Analyzer.isMutated(Param)) return; - // CXXCtorInitializer might also mutate Param but they're not part of function - // body, so check them separately here. - if (const auto *Ctor = dyn_cast(Function)) { - for (const auto *Init : Ctor->inits()) { - if (ExprMutationAnalyzer(*Init->getInit(), *Result.Context) - .isMutated(Param)) - return; - } - } const bool IsConstQualified = Param->getType().getCanonicalType().isConstQualified(); @@ -186,6 +175,10 @@ void UnnecessaryValueParamCheck::storeOptions( utils::IncludeSorter::toString(IncludeStyle)); } +void UnnecessaryValueParamCheck::onEndOfTranslationUnit() { + MutationAnalyzers.clear(); +} + void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument, const ASTContext &Context) { diff --git a/clang-tidy/performance/UnnecessaryValueParamCheck.h b/clang-tidy/performance/UnnecessaryValueParamCheck.h index cbf0a3bbe..ae5ef7e96 100644 --- a/clang-tidy/performance/UnnecessaryValueParamCheck.h +++ b/clang-tidy/performance/UnnecessaryValueParamCheck.h @@ -12,6 +12,7 @@ #include "../ClangTidy.h" #include "../utils/IncludeInserter.h" +#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" namespace clang { namespace tidy { @@ -29,11 +30,14 @@ class UnnecessaryValueParamCheck : public ClangTidyCheck { void check(const ast_matchers::MatchFinder::MatchResult &Result) override; void registerPPCallbacks(CompilerInstance &Compiler) override; void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void onEndOfTranslationUnit() override; private: void handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument, const ASTContext &Context); + llvm::DenseMap + MutationAnalyzers; std::unique_ptr Inserter; const utils::IncludeSorter::IncludeStyle IncludeStyle; }; From 86c9adace8956c069897198248a150cf4a9b0b02 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Mon, 17 Sep 2018 20:10:33 +0000 Subject: [PATCH 212/686] Fix build failure caused by D52157 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342408 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clang-query/QueryEngineTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/clang-query/QueryEngineTest.cpp b/unittests/clang-query/QueryEngineTest.cpp index 631a53ebb..f95d315c6 100644 --- a/unittests/clang-query/QueryEngineTest.cpp +++ b/unittests/clang-query/QueryEngineTest.cpp @@ -115,7 +115,7 @@ TEST_F(QueryEngineTest, Basic) { Str.clear(); - EXPECT_FALSE(MatchQuery(isArrow()).run(OS, S)); + EXPECT_FALSE(MatchQuery(isMain()).run(OS, S)); EXPECT_EQ("Not a valid top-level matcher.\n", OS.str()); } From 50deb9affa7e4a0b574070a07a5110cc718b4735 Mon Sep 17 00:00:00 2001 From: Shuai Wang Date: Mon, 17 Sep 2018 21:28:08 +0000 Subject: [PATCH 213/686] [clang-tidy] Fix tests for performance-for-range-copy Test failed as D52120 made ExprMutationAnalyzer smarter, fixed by: - Add move-ctor for `Mutable` to make it actually movable. - Properly implement `remove_reference`. The failed test case is: void negativeVarIsMoved() { for (auto M : View>()) { auto Moved = std::move(M); } } Before D52120, `std::move(M)` itself is considered as a mutation to `M`, while after D52120 it's only considered as a cast to rvalue, the move-assignment is what causes the actual mutation. The test case didn't mock things properly so the intended move-assignement was actually a copy-assignment. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342417 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clang-tidy/performance-for-range-copy.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/clang-tidy/performance-for-range-copy.cpp b/test/clang-tidy/performance-for-range-copy.cpp index 8a43ac0c2..5e174fad9 100644 --- a/test/clang-tidy/performance-for-range-copy.cpp +++ b/test/clang-tidy/performance-for-range-copy.cpp @@ -4,6 +4,10 @@ namespace std { template struct remove_reference { typedef _Tp type; }; +template +struct remove_reference<_Tp&> { typedef _Tp type; }; +template +struct remove_reference<_Tp&&> { typedef _Tp type; }; template constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) { @@ -103,6 +107,7 @@ void f() { struct Mutable { Mutable() {} Mutable(const Mutable &) = default; + Mutable(Mutable&&) = default; Mutable(const Mutable &, const Mutable &) {} void setBool(bool B) {} bool constMethod() const { From b6ac5022284ee7fcdba4c01e7d3a57b0d1918994 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Tue, 18 Sep 2018 06:57:58 +0000 Subject: [PATCH 214/686] [pp-trace] Remove unused using directives git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342445 91177308-0d34-0410-b5e6-96231b3b80d8 --- pp-trace/PPTrace.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/pp-trace/PPTrace.cpp b/pp-trace/PPTrace.cpp index d49bc20e7..08e0f5144 100644 --- a/pp-trace/PPTrace.cpp +++ b/pp-trace/PPTrace.cpp @@ -73,11 +73,8 @@ #include using namespace clang; -using namespace clang::driver; -using namespace clang::driver::options; using namespace clang::tooling; using namespace llvm; -using namespace llvm::opt; // Options: From 9090c5be4aab94b0b1bbc7abf936fe9f021083ab Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 18 Sep 2018 08:52:14 +0000 Subject: [PATCH 215/686] [clangd] Adapt API change after 342451. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342452 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/FindSymbols.cpp | 5 +++-- clangd/XRefs.cpp | 8 ++++---- clangd/index/FileIndex.cpp | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/clangd/FindSymbols.cpp b/clangd/FindSymbols.cpp index 054e63d93..0b000121e 100644 --- a/clangd/FindSymbols.cpp +++ b/clangd/FindSymbols.cpp @@ -270,8 +270,9 @@ getDocumentSymbols(ParsedAST &AST) { IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly; IndexOpts.IndexFunctionLocals = false; - indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(), - DocumentSymbolsCons, IndexOpts); + indexTopLevelDecls(AST.getASTContext(), AST.getPreprocessor(), + AST.getLocalTopLevelDecls(), DocumentSymbolsCons, + IndexOpts); return DocumentSymbolsCons.takeSymbols(); } diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index 33181576a..da98d88f7 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -206,8 +206,8 @@ IdentifiedSymbol getSymbolAtPosition(ParsedAST &AST, SourceLocation Pos) { IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All; IndexOpts.IndexFunctionLocals = true; - indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(), - DeclMacrosFinder, IndexOpts); + indexTopLevelDecls(AST.getASTContext(), AST.getPreprocessor(), + AST.getLocalTopLevelDecls(), DeclMacrosFinder, IndexOpts); return {DeclMacrosFinder.getFoundDecls(), DeclMacrosFinder.takeMacroInfos()}; } @@ -414,8 +414,8 @@ findRefs(const std::vector &Decls, ParsedAST &AST) { IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All; IndexOpts.IndexFunctionLocals = true; - indexTopLevelDecls(AST.getASTContext(), AST.getLocalTopLevelDecls(), - RefFinder, IndexOpts); + indexTopLevelDecls(AST.getASTContext(), AST.getPreprocessor(), + AST.getLocalTopLevelDecls(), RefFinder, IndexOpts); return std::move(RefFinder).take(); } diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 20bfb871f..6b546747a 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -54,7 +54,7 @@ indexAST(ASTContext &AST, std::shared_ptr PP, SymbolCollector Collector(std::move(CollectorOpts)); Collector.setPreprocessor(PP); - index::indexTopLevelDecls(AST, DeclsToIndex, Collector, IndexOpts); + index::indexTopLevelDecls(AST, *PP, DeclsToIndex, Collector, IndexOpts); const auto &SM = AST.getSourceManager(); const auto *MainFileEntry = SM.getFileEntryForID(SM.getMainFileID()); From 0faf959bf47b4f6c66c82192f066057dfe9c542b Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 18 Sep 2018 09:08:28 +0000 Subject: [PATCH 216/686] [clangd] Update code completion for #include completions in r342449 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342453 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 0f3dab83a..ca083964e 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -116,9 +116,12 @@ CompletionItemKind toCompletionItemKind(index::SymbolKind Kind) { CompletionItemKind toCompletionItemKind(CodeCompletionResult::ResultKind ResKind, - const NamedDecl *Decl) { + const NamedDecl *Decl, + CodeCompletionContext::Kind CtxKind) { if (Decl) return toCompletionItemKind(index::getSymbolInfo(Decl).Kind); + if (CtxKind == CodeCompletionContext::CCC_IncludedFile) + return CompletionItemKind::File; switch (ResKind) { case CodeCompletionResult::RK_Declaration: llvm_unreachable("RK_Declaration without Decl"); @@ -328,6 +331,7 @@ struct CodeCompletionBuilder { CodeCompletionBuilder(ASTContext &ASTCtx, const CompletionCandidate &C, CodeCompletionString *SemaCCS, const IncludeInserter &Includes, StringRef FileName, + CodeCompletionContext::Kind ContextKind, const CodeCompleteOptions &Opts) : ASTCtx(ASTCtx), ExtractDocumentation(Opts.IncludeComments), EnableFunctionArgSnippets(Opts.EnableFunctionArgSnippets) { @@ -343,8 +347,8 @@ struct CodeCompletionBuilder { Completion.Scope = splitQualifiedName(printQualifiedName(*ND)).first; } - Completion.Kind = - toCompletionItemKind(C.SemaResult->Kind, C.SemaResult->Declaration); + Completion.Kind = toCompletionItemKind( + C.SemaResult->Kind, C.SemaResult->Declaration, ContextKind); for (const auto &FixIt : C.SemaResult->FixIts) { Completion.FixIts.push_back( toTextEdit(FixIt, ASTCtx.getSourceManager(), ASTCtx.getLangOpts())); @@ -653,6 +657,7 @@ bool contextAllowsIndex(enum CodeCompletionContext::Kind K) { case CodeCompletionContext::CCC_TypeQualifiers: case CodeCompletionContext::CCC_ObjCInstanceMessage: case CodeCompletionContext::CCC_ObjCClassMessage: + case CodeCompletionContext::CCC_IncludedFile: case CodeCompletionContext::CCC_Recovery: return false; } @@ -1547,7 +1552,8 @@ class CodeCompleteFlow { : nullptr; if (!Builder) Builder.emplace(Recorder->CCSema->getASTContext(), Item, SemaCCS, - *Inserter, FileName, Opts); + *Inserter, FileName, Recorder->CCContext.getKind(), + Opts); else Builder->add(Item, SemaCCS); } From 0b7a2bc837d70f417a3ca2d56783ded6f86aea38 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 18 Sep 2018 09:49:57 +0000 Subject: [PATCH 217/686] [clangd] dexp tool uses llvm::cl to parse its flags. Summary: We can use cl::ResetCommandLineParser() to support different types of command-lines, as long as we're careful about option lifetimes. (I tried using subcommands, but the error messages were bad) I found a mostly-reasonable pattern to isolate the fiddly parts. Added -scope and -limit flags to the `find` command to demonstrate. (Note that scope support seems to be broken in dex?) Fixed symbol lookup to parse symbol IDs. Caveats: - with command help (e.g. `find -help`), you also get some spam about required arguments. This is a bug in llvm::cl, which prints these to errs() rather than the designated stream. Reviewers: kbobyrev Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D51989 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342456 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/dexp/Dexp.cpp | 205 ++++++++++++++++++++++----------- test/CMakeLists.txt | 1 + 2 files changed, 136 insertions(+), 70 deletions(-) diff --git a/clangd/index/dex/dexp/Dexp.cpp b/clangd/index/dex/dexp/Dexp.cpp index 41f9bf00e..09c310ff5 100644 --- a/clangd/index/dex/dexp/Dexp.cpp +++ b/clangd/index/dex/dexp/Dexp.cpp @@ -25,7 +25,7 @@ using clang::clangd::FuzzyFindRequest; using clang::clangd::loadIndex; using clang::clangd::Symbol; using clang::clangd::SymbolIndex; -using llvm::StringRef; +using namespace llvm; namespace { @@ -52,51 +52,42 @@ void reportTime(StringRef Name, llvm::function_ref F) { llvm::outs() << llvm::formatv("{0} took {1:ms+n}.\n", Name, Duration); } -void fuzzyFind(llvm::StringRef UnqualifiedName, const SymbolIndex &Index) { - FuzzyFindRequest Request; - Request.Limit = 10; - Request.Query = UnqualifiedName; - // FIXME(kbobyrev): Print symbol final scores to see the distribution. - static const auto OutputFormat = "{0,-4} | {1,-40} | {2,-25}\n"; - llvm::outs() << llvm::formatv(OutputFormat, "Rank", "Symbol ID", - "Symbol Name"); - size_t Rank = 0; - Index.fuzzyFind(Request, [&](const Symbol &Sym) { - llvm::outs() << llvm::formatv(OutputFormat, Rank++, Sym.ID.str(), Sym.Name); - }); -} - -static const std::string HelpMessage = R"(dexp commands: - -> find Name - -Constructs fuzzy find request given unqualified symbol name and returns top 10 -symbols retrieved from index. - -> lookup SymbolID - -Retrieves symbol names given USR. -)"; - -void help() { llvm::outs() << HelpMessage; } - -void lookup(StringRef USR, const SymbolIndex &Index) { - llvm::DenseSet IDs{clang::clangd::SymbolID{USR}}; - clang::clangd::LookupRequest Request{IDs}; - bool FoundSymbol = false; - Index.lookup(Request, [&](const Symbol &Sym) { - if (!FoundSymbol) - FoundSymbol = true; - llvm::outs() << SymbolToYAML(Sym); - }); - if (!FoundSymbol) - llvm::outs() << "not found\n"; -} +// REPL commands inherit from Command and contain their options as members. +// Creating a Command populates parser options, parseAndRun() resets them. +class Command { + // By resetting the parser options, we lost the standard -help flag. + cl::opt> Help{ + "help", cl::desc("Display available options"), cl::ValueDisallowed, + cl::cat(cl::GeneralCategory)}; + virtual void run() = 0; + +protected: + const SymbolIndex *Index; + +public: + virtual ~Command() = default; + virtual void parseAndRun(ArrayRef Argv, const char *Overview, + const SymbolIndex &Index) { + std::string ParseErrs; + llvm::raw_string_ostream OS(ParseErrs); + bool Ok = + cl::ParseCommandLineOptions(Argv.size(), Argv.data(), Overview, &OS); + if (Help.getNumOccurrences() > 0) { + // Avoid printing parse errors in this case. + // (Well, in theory. A bunch get printed to llvm::errs() regardless!) + cl::PrintHelpMessage(); + } else { + outs() << OS.str(); + if (Ok) { + this->Index = &Index; + reportTime(Argv[0], [&] { run(); }); + } + } + cl::ResetCommandLineParser(); // must do this before opts are destroyed. + } +}; -// FIXME(kbobyrev): Make this an actual REPL: probably use LLVM Command Line -// library for parsing flags and arguments. -// FIXME(kbobyrev): Ideas for commands: -// * symbol lookup: print out symbol in YAML format given SymbolID +// FIXME(kbobyrev): Ideas for more commands: // * find symbol references: print set of reference locations // * load/swap/reload index: this would make it possible to get rid of llvm::cl // usages in the tool driver and actually use llvm::cl library in the REPL. @@ -105,39 +96,86 @@ void lookup(StringRef USR, const SymbolIndex &Index) { // * show number of tokens of each kind // * print out tokens with the most dense posting lists // * print out tokens with least dense posting lists -void dispatch(StringRef Request, const SymbolIndex &Index) { - llvm::SmallVector Arguments; - Request.split(Arguments, ' '); - if (Arguments.empty()) { - llvm::outs() << "Request can not be empty.\n"; - help(); - return; - } - if (Arguments.front() == "find") { - if (Arguments.size() != 2) { - llvm::outs() << "find request must specify unqualified symbol name.\n"; - return; +class FuzzyFind : public Command { + cl::opt Query{ + "query", + cl::Positional, + cl::Required, + cl::desc("Query string to be fuzzy-matched"), + }; + cl::opt Scopes{ + "scopes", + cl::desc("Allowed symbol scopes (comma-separated list)"), + }; + cl::opt Limit{ + "limit", + cl::init(10), + cl::desc("Max results to display"), + }; + + void run() override { + FuzzyFindRequest Request; + Request.Limit = Limit; + Request.Query = Query; + if (Scopes.getNumOccurrences() > 0) { + llvm::SmallVector Scopes; + StringRef(this->Scopes).split(Scopes, ','); + Request.Scopes = {Scopes.begin(), Scopes.end()}; } - reportTime("fuzzy find request", - [&]() { fuzzyFind(Arguments.back(), Index); }); - } else if (Arguments.front() == "lookup") { - if (Arguments.size() != 2) { - llvm::outs() << "lookup request must specify symbol ID .\n"; + // FIXME(kbobyrev): Print symbol final scores to see the distribution. + static const auto OutputFormat = "{0,-4} | {1,-40} | {2,-25}\n"; + llvm::outs() << llvm::formatv(OutputFormat, "Rank", "Symbol ID", + "Symbol Name"); + size_t Rank = 0; + Index->fuzzyFind(Request, [&](const Symbol &Sym) { + llvm::outs() << llvm::formatv(OutputFormat, Rank++, Sym.ID.str(), + Sym.Name); + }); + } +}; + +class Lookup : public Command { + cl::opt ID{ + "id", + cl::Positional, + cl::Required, + cl::desc("Symbol ID to look up (hex)"), + }; + + void run() override { + auto Raw = fromHex(ID); + if (Raw.size() != clang::clangd::SymbolID::RawSize) { + llvm::outs() << "invalid SymbolID\n"; return; } - reportTime("lookup request", [&]() { lookup(Arguments.back(), Index); }); - } else if (Arguments.front() == "help") { - help(); - } else { - llvm::outs() << "Unknown command. Try 'help'\n"; + + clang::clangd::LookupRequest Request; + Request.IDs = {clang::clangd::SymbolID::fromRaw(Raw)}; + bool FoundSymbol = false; + Index->lookup(Request, [&](const Symbol &Sym) { + FoundSymbol = true; + llvm::outs() << SymbolToYAML(Sym); + }); + if (!FoundSymbol) + llvm::outs() << "not found\n"; } -} +}; + +struct { + const char *Name; + const char *Description; + std::function()> Implementation; +} CommandInfo[] = { + {"find", "Search for symbols with fuzzyFind", llvm::make_unique}, + {"lookup", "Dump symbol details by ID", llvm::make_unique}, +}; } // namespace int main(int argc, const char *argv[]) { llvm::cl::ParseCommandLineOptions(argc, argv, Overview); + llvm::cl::ResetCommandLineParser(); // We reuse it for REPL commands. llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); std::unique_ptr Index; @@ -154,8 +192,35 @@ int main(int argc, const char *argv[]) { llvm::LineEditor LE("dexp"); - while (llvm::Optional Request = LE.readLine()) - dispatch(Request.getValue(), *Index); + while (llvm::Optional Request = LE.readLine()) { + // Split on spaces and add required null-termination. + std::replace(Request->begin(), Request->end(), ' ', '\0'); + SmallVector Args; + StringRef(*Request).split(Args, '\0', /*MaxSplit=*/-1, /*KeepEmpty=*/false); + if (Args.empty()) + continue; + if (Args.front() == "help") { + outs() << "dexp - Index explorer\nCommands:\n"; + for (const auto &C : CommandInfo) + outs() << llvm::formatv("{0,16} - {1}\n", C.Name, C.Description); + outs() << "Get detailed command help with e.g. `find -help`.\n"; + continue; + } + SmallVector FakeArgv; + for (StringRef S : Args) + FakeArgv.push_back(S.data()); // Terminated by separator or end of string. + + bool Recognized = false; + for (const auto &Cmd : CommandInfo) { + if (Cmd.Name == Args.front()) { + Recognized = true; + Cmd.Implementation()->parseAndRun(FakeArgv, Cmd.Description, *Index); + break; + } + } + if (!Recognized) + outs() << "Unknown command. Try 'help'.\n"; + } return 0; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e0ad2c3b6..cc03ff74f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -56,6 +56,7 @@ set(CLANG_TOOLS_TEST_DEPS # These individual tools have no tests, add them here to make them compile # together with check-clang-tools, so that we won't break them in the future. clangd-indexer + dexp # Unit tests ExtraToolsUnitTests From 927ecfda8e5e383bd1b5251cfb988a2ae2d7891d Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Tue, 18 Sep 2018 10:15:15 +0000 Subject: [PATCH 218/686] [clang-tidy] use CHECK-NOTES in tests for bugprone-argument-comment Summary: This patch uses CHECK-NOTES for the tests. Its part of an effort to test *ALL* generated diagnostics in clang-tidy, as emitted notes were previously ignored. Reviewers: alexfh, aaron.ballman, hokein Reviewed By: alexfh Subscribers: xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D52178 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342458 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../bugprone-argument-comment-gmock.cpp | 37 ++++++++++++++++--- .../bugprone-argument-comment-strict.cpp | 17 +++++++-- test/clang-tidy/bugprone-argument-comment.cpp | 29 ++++++++++----- 3 files changed, 64 insertions(+), 19 deletions(-) diff --git a/test/clang-tidy/bugprone-argument-comment-gmock.cpp b/test/clang-tidy/bugprone-argument-comment-gmock.cpp index e79395e7c..8b67a915b 100644 --- a/test/clang-tidy/bugprone-argument-comment-gmock.cpp +++ b/test/clang-tidy/bugprone-argument-comment-gmock.cpp @@ -83,17 +83,41 @@ class MockStandalone { void test_gmock_expectations() { MockDerived m; EXPECT_CALL(m, Method(/*param_one=*/1, /*param_tw=*/2)); -// CHECK-MESSAGES: [[@LINE-1]]:42: warning: argument name 'param_tw' in comment does not match parameter name 'param_two' +// CHECK-NOTES: [[@LINE-1]]:42: warning: argument name 'param_tw' in comment does not match parameter name 'param_two' +// CHECK-NOTES: [[@LINE-2]]:42: note: FIX-IT applied suggested code changes +// CHECK-NOTES: [[@LINE-19]]:42: note: 'param_two' declared here +// CHECK-NOTES: [[@LINE-15]]:3: note: actual callee ('gmock_Method') is declared here +// CHECK-NOTES: [[@LINE-33]]:30: note: expanded from macro 'MOCK_METHOD2' +// CHECK-NOTES: [[@LINE-36]]:7: note: expanded from macro 'GMOCK_METHOD2_' +// CHECK-NOTES: note: expanded from here // CHECK-FIXES: EXPECT_CALL(m, Method(/*param_one=*/1, /*param_two=*/2)); EXPECT_CALL(m, Method2(/*p_on=*/3, /*p_two=*/4)); -// CHECK-MESSAGES: [[@LINE-1]]:26: warning: argument name 'p_on' in comment does not match parameter name 'p_one' +// CHECK-NOTES: [[@LINE-1]]:26: warning: argument name 'p_on' in comment does not match parameter name 'p_one' +// CHECK-NOTES: [[@LINE-2]]:26: note: FIX-IT applied suggested code changes +// CHECK-NOTES: [[@LINE-27]]:28: note: 'p_one' declared here +// CHECK-NOTES: [[@LINE-23]]:3: note: actual callee ('gmock_Method2') is declared here +// CHECK-NOTES: [[@LINE-41]]:36: note: expanded from macro 'MOCK_CONST_METHOD2' +// CHECK-NOTES: [[@LINE-45]]:7: note: expanded from macro 'GMOCK_METHOD2_' +// CHECK-NOTES: note: expanded from here // CHECK-FIXES: EXPECT_CALL(m, Method2(/*p_one=*/3, /*p_two=*/4)); #define PARAM1 11 #define PARAM2 22 EXPECT_CALL(m, Method2(/*p_on1=*/PARAM1, /*p_tw2=*/PARAM2)); -// CHECK-MESSAGES: [[@LINE-1]]:26: warning: argument name 'p_on1' in comment does not match parameter name 'p_one' -// CHECK-MESSAGES: [[@LINE-2]]:44: warning: argument name 'p_tw2' in comment does not match parameter name 'p_two' +// CHECK-NOTES: [[@LINE-1]]:26: warning: argument name 'p_on1' in comment does not match parameter name 'p_one' +// CHECK-NOTES: [[@LINE-2]]:26: note: FIX-IT applied suggested code changes +// CHECK-NOTES: [[@LINE-39]]:28: note: 'p_one' declared here +// CHECK-NOTES: [[@LINE-35]]:3: note: actual callee ('gmock_Method2') is declared here +// CHECK-NOTES: [[@LINE-53]]:36: note: expanded from macro 'MOCK_CONST_METHOD2' +// CHECK-NOTES: [[@LINE-57]]:7: note: expanded from macro 'GMOCK_METHOD2_' +// CHECK-NOTES: note: expanded from here +// CHECK-NOTES: [[@LINE-8]]:44: warning: argument name 'p_tw2' in comment does not match parameter name 'p_two' +// CHECK-NOTES: [[@LINE-9]]:44: note: FIX-IT applied suggested code changes +// CHECK-NOTES: [[@LINE-46]]:39: note: 'p_two' declared here +// CHECK-NOTES: [[@LINE-42]]:3: note: actual callee ('gmock_Method2') is declared here +// CHECK-NOTES: [[@LINE-60]]:36: note: expanded from macro 'MOCK_CONST_METHOD2' +// CHECK-NOTES: [[@LINE-64]]:7: note: expanded from macro 'GMOCK_METHOD2_' +// CHECK-NOTES: note: expanded from here // CHECK-FIXES: EXPECT_CALL(m, Method2(/*p_one=*/PARAM1, /*p_two=*/PARAM2)); MockStandalone m2; @@ -103,6 +127,9 @@ void test_gmock_expectations() { void test_gmock_direct_calls() { MockDerived m; m.Method(/*param_one=*/1, /*param_tw=*/2); -// CHECK-MESSAGES: [[@LINE-1]]:29: warning: argument name 'param_tw' in comment does not match parameter name 'param_two' +// CHECK-NOTES: [[@LINE-1]]:29: warning: argument name 'param_tw' in comment does not match parameter name 'param_two' +// CHECK-NOTES: [[@LINE-2]]:29: note: FIX-IT applied suggested code changes +// CHECK-NOTES: [[@LINE-63]]:42: note: 'param_two' declared here +// CHECK-NOTES: [[@LINE-59]]:16: note: actual callee ('Method') is declared here // CHECK-FIXES: m.Method(/*param_one=*/1, /*param_two=*/2); } diff --git a/test/clang-tidy/bugprone-argument-comment-strict.cpp b/test/clang-tidy/bugprone-argument-comment-strict.cpp index 0e06a4ce6..68f8da364 100644 --- a/test/clang-tidy/bugprone-argument-comment-strict.cpp +++ b/test/clang-tidy/bugprone-argument-comment-strict.cpp @@ -5,15 +5,24 @@ void f(int _with_underscores_); void g(int x_); void ignores_underscores() { f(/*With_Underscores=*/0); -// CHECK-MESSAGES: [[@LINE-1]]:5: warning: argument name 'With_Underscores' in comment does not match parameter name '_with_underscores_' +// CHECK-NOTES: [[@LINE-1]]:5: warning: argument name 'With_Underscores' in comment does not match parameter name '_with_underscores_' +// CHECK-NOTES: [[@LINE-2]]:5: note: FIX-IT applied suggested code changes +// CHECK-NOTES: [[@LINE-6]]:12: note: '_with_underscores_' declared here // CHECK-FIXES: f(/*_with_underscores_=*/0); + f(/*with_underscores=*/1); -// CHECK-MESSAGES: [[@LINE-1]]:5: warning: argument name 'with_underscores' in comment does not match parameter name '_with_underscores_' +// CHECK-NOTES: [[@LINE-1]]:5: warning: argument name 'with_underscores' in comment does not match parameter name '_with_underscores_' +// CHECK-NOTES: [[@LINE-2]]:5: note: FIX-IT applied suggested code changes +// CHECK-NOTES: [[@LINE-12]]:12: note: '_with_underscores_' declared here // CHECK-FIXES: f(/*_with_underscores_=*/1); f(/*_With_Underscores_=*/2); -// CHECK-MESSAGES: [[@LINE-1]]:5: warning: argument name '_With_Underscores_' in comment does not match parameter name '_with_underscores_' +// CHECK-NOTES: [[@LINE-1]]:5: warning: argument name '_With_Underscores_' in comment does not match parameter name '_with_underscores_' +// CHECK-NOTES: [[@LINE-2]]:5: note: FIX-IT applied suggested code changes +// CHECK-NOTES: [[@LINE-17]]:12: note: '_with_underscores_' declared here // CHECK-FIXES: f(/*_with_underscores_=*/2); g(/*X=*/3); -// CHECK-MESSAGES: [[@LINE-1]]:5: warning: argument name 'X' in comment does not match parameter name 'x_' +// CHECK-NOTES: [[@LINE-1]]:5: warning: argument name 'X' in comment does not match parameter name 'x_' +// CHECK-NOTES: [[@LINE-2]]:5: note: FIX-IT applied suggested code changes +// CHECK-NOTES: [[@LINE-21]]:12: note: 'x_' declared here // CHECK-FIXES: g(/*x_=*/3); } diff --git a/test/clang-tidy/bugprone-argument-comment.cpp b/test/clang-tidy/bugprone-argument-comment.cpp index 64527911e..53b9726f5 100644 --- a/test/clang-tidy/bugprone-argument-comment.cpp +++ b/test/clang-tidy/bugprone-argument-comment.cpp @@ -7,10 +7,10 @@ void ffff(int xxxx, int yyyy); void f(int x, int y); void g() { - // CHECK-MESSAGES: [[@LINE+4]]:5: warning: argument name 'y' in comment does not match parameter name 'x' - // CHECK-MESSAGES: :[[@LINE-3]]:12: note: 'x' declared here - // CHECK-MESSAGES: [[@LINE+2]]:14: warning: argument name 'z' in comment does not match parameter name 'y' - // CHECK-MESSAGES: :[[@LINE-5]]:19: note: 'y' declared here + // CHECK-NOTES: [[@LINE+4]]:5: warning: argument name 'y' in comment does not match parameter name 'x' + // CHECK-NOTES: [[@LINE-3]]:12: note: 'x' declared here + // CHECK-NOTES: [[@LINE+2]]:14: warning: argument name 'z' in comment does not match parameter name 'y' + // CHECK-NOTES: [[@LINE-5]]:19: note: 'y' declared here f(/*y=*/0, /*z=*/0); // CHECK-FIXES: {{^}} f(/*y=*/0, /*z=*/0); @@ -46,14 +46,17 @@ void variadic2(int zzz, Args&&... args); void templates() { variadic(/*xxx=*/0, /*yyy=*/1); variadic2(/*zzU=*/0, /*xxx=*/1, /*yyy=*/2); - // CHECK-MESSAGES: [[@LINE-1]]:13: warning: argument name 'zzU' in comment does not match parameter name 'zzz' + // CHECK-NOTES: [[@LINE-1]]:13: warning: argument name 'zzU' in comment does not match parameter name 'zzz' + // CHECK-NOTES: [[@LINE-2]]:13: note: FIX-IT applied suggested code changes + // CHECK-NOTES: :[[@LINE-7]]:20: note: 'zzz' declared here // CHECK-FIXES: variadic2(/*zzz=*/0, /*xxx=*/1, /*yyy=*/2); } #define FALSE 0 void qqq(bool aaa); void f2() { qqq(/*bbb=*/FALSE); } -// CHECK-MESSAGES: [[@LINE-1]]:17: warning: argument name 'bbb' in comment does not match parameter name 'aaa' +// CHECK-NOTES: [[@LINE-1]]:17: warning: argument name 'bbb' in comment does not match parameter name 'aaa' +// CHECK-NOTES: [[@LINE-3]]:15: note: 'aaa' declared here // CHECK-FIXES: void f2() { qqq(/*bbb=*/FALSE); } void f3(bool _with_underscores_); @@ -64,26 +67,32 @@ void ignores_underscores() { namespace ThisEditDistanceAboveThreshold { void f4(int xxx); void g() { f4(/*xyz=*/0); } -// CHECK-MESSAGES: [[@LINE-1]]:15: warning: argument name 'xyz' in comment does not match parameter name 'xxx' +// CHECK-NOTES: [[@LINE-1]]:15: warning: argument name 'xyz' in comment does not match parameter name 'xxx' +// CHECK-NOTES: [[@LINE-3]]:13: note: 'xxx' declared here // CHECK-FIXES: void g() { f4(/*xyz=*/0); } } namespace OtherEditDistanceAboveThreshold { void f5(int xxx, int yyy); void g() { f5(/*Zxx=*/0, 0); } -// CHECK-MESSAGES: [[@LINE-1]]:15: warning: argument name 'Zxx' in comment does not match parameter name 'xxx' +// CHECK-NOTES: [[@LINE-1]]:15: warning: argument name 'Zxx' in comment does not match parameter name 'xxx' +// CHECK-NOTES: [[@LINE-2]]:15: note: FIX-IT applied suggested code changes +// CHECK-NOTES: [[@LINE-4]]:13: note: 'xxx' declared here // CHECK-FIXES: void g() { f5(/*xxx=*/0, 0); } struct C2 { C2(int xxx, int yyy); }; C2 c2(/*Zxx=*/0, 0); -// CHECK-MESSAGES: [[@LINE-1]]:7: warning: argument name 'Zxx' in comment does not match parameter name 'xxx' +// CHECK-NOTES: [[@LINE-1]]:7: warning: argument name 'Zxx' in comment does not match parameter name 'xxx' +// CHECK-NOTES: [[@LINE-2]]:7: note: FIX-IT applied suggested code changes +// CHECK-NOTES: [[@LINE-5]]:10: note: 'xxx' declared here // CHECK-FIXES: C2 c2(/*xxx=*/0, 0); } namespace OtherEditDistanceBelowThreshold { void f6(int xxx, int yyy); void g() { f6(/*xxy=*/0, 0); } -// CHECK-MESSAGES: [[@LINE-1]]:15: warning: argument name 'xxy' in comment does not match parameter name 'xxx' +// CHECK-NOTES: [[@LINE-1]]:15: warning: argument name 'xxy' in comment does not match parameter name 'xxx' +// CHECK-NOTES: [[@LINE-3]]:13: note: 'xxx' declared here // CHECK-FIXES: void g() { f6(/*xxy=*/0, 0); } } From 8c5306bccf6c51fdd72231b93280d0457f45cf87 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Tue, 18 Sep 2018 10:21:33 +0000 Subject: [PATCH 219/686] [clang-tidy] use CHECK-NOTES in bugprone-forwarding-reference-overload Reviewers: aaron.ballman, alexfh, hokein Reviewed By: alexfh Subscribers: xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D52186 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342459 91177308-0d34-0410-b5e6-96231b3b80d8 --- ...bugprone-forwarding-reference-overload.cpp | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/test/clang-tidy/bugprone-forwarding-reference-overload.cpp b/test/clang-tidy/bugprone-forwarding-reference-overload.cpp index 35106852f..cb842e3e8 100644 --- a/test/clang-tidy/bugprone-forwarding-reference-overload.cpp +++ b/test/clang-tidy/bugprone-forwarding-reference-overload.cpp @@ -20,27 +20,34 @@ template constexpr bool just_true = true; class Test1 { public: template Test1(T &&n); - // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the copy and move constructors [bugprone-forwarding-reference-overload] + // CHECK-NOTES: [[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the copy and move constructors [bugprone-forwarding-reference-overload] + // CHECK-NOTES: 48:3: note: copy constructor declared here + // CHECK-NOTES: 49:3: note: copy constructor declared here + // CHECK-NOTES: 50:3: note: move constructor declared here template Test1(T &&n, int i = 5, ...); - // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the copy and move constructors + // CHECK-NOTES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the copy and move constructors + // CHECK-NOTES: 48:3: note: copy constructor declared here + // CHECK-NOTES: 49:3: note: copy constructor declared here + // CHECK-NOTES: 50:3: note: move constructor declared here template ::type> Test1(T &&n); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors + // CHECK-NOTES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors + // CHECK-NOTES: 48:3: note: copy constructor declared here + // CHECK-NOTES: 49:3: note: copy constructor declared here + // CHECK-NOTES: 50:3: note: move constructor declared here template Test1(T &&n, typename foo::enable_if::type i = 5, ...); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors + // CHECK-NOTES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors + // CHECK-NOTES: 48:3: note: copy constructor declared here + // CHECK-NOTES: 49:3: note: copy constructor declared here + // CHECK-NOTES: 50:3: note: move constructor declared here Test1(const Test1 &other) {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here - Test1(Test1 &other) {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here - Test1(Test1 &&other) {} - // CHECK-MESSAGES: :[[@LINE-1]]:3: note: move constructor declared here }; template class Test2 { @@ -96,10 +103,10 @@ class Test3 { class Test4 { public: template Test4(T &&n); - // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the copy and move constructors + // CHECK-NOTES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the copy and move constructors Test4(const Test4 &rhs); - // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here + // CHECK-NOTES: :[[@LINE-1]]:3: note: copy constructor declared here }; // Nothing can be hidden, the copy constructor is implicitly deleted. @@ -114,10 +121,10 @@ class Test5 { class Test6 { public: template Test6(T &&n); - // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the move constructor + // CHECK-NOTES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the move constructor Test6(Test6 &&rhs); - // CHECK-MESSAGES: :[[@LINE-1]]:3: note: move constructor declared here + // CHECK-NOTES: :[[@LINE-1]]:3: note: move constructor declared here private: Test6(const Test6 &rhs); }; @@ -141,5 +148,5 @@ class variant { public: template > constexpr variant(_Arg&& __arg) {} - // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: constructor accepting a forwarding reference can hide the copy and move constructors + // CHECK-NOTES: :[[@LINE-1]]:13: warning: constructor accepting a forwarding reference can hide the copy and move constructors }; From 5e69828c40c705dab32942310d16b1f1e63996be Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 18 Sep 2018 10:30:44 +0000 Subject: [PATCH 220/686] [clangd] Merge ClangdServer::DynamicIndex into FileIndex. NFC. Summary: FileIndex now provides explicit interfaces for preamble and main file updates. This avoids growing parameter list when preamble and main symbols diverge further (e.g. D52078). This also gets rid of the hack in `indexAST` that inferred main file index based on `TopLevelDecls`. Also separate `indexMainDecls` from `indexAST`. Reviewers: sammccall Reviewed By: sammccall Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52222 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342460 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 79 +++++++------------------- clangd/ClangdServer.h | 4 +- clangd/index/FileIndex.cpp | 86 +++++++++++++++++------------ clangd/index/FileIndex.h | 68 ++++++++++++++++------- unittests/clangd/FileIndexTests.cpp | 42 ++++---------- unittests/clangd/IndexTests.cpp | 15 ++--- unittests/clangd/TestTU.cpp | 6 +- 7 files changed, 140 insertions(+), 160 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index fb4f1be52..6f2d13e0b 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -14,6 +14,7 @@ #include "SourceCode.h" #include "Trace.h" #include "XRefs.h" +#include "index/FileIndex.h" #include "index/Merge.h" #include "clang/Format/Format.h" #include "clang/Frontend/CompilerInstance.h" @@ -70,59 +71,23 @@ class RefactoringResultCollector final }; } // namespace -/// The dynamic index tracks symbols visible in open files. -/// For boring reasons, it doesn't implement SymbolIndex directly - use index(). -class ClangdServer::DynamicIndex { -public: - DynamicIndex(std::vector URISchemes) - : PreambleIdx(URISchemes), MainFileIdx(URISchemes), - MergedIndex(mergeIndex(&MainFileIdx, &PreambleIdx)) {} - - const SymbolIndex &index() const { return *MergedIndex; } - - // Returns callbacks that can be used to update the index with new ASTs. - // Index() presents a merged view of the supplied main-file and preamble ASTs. - std::unique_ptr makeUpdateCallbacks() { - struct CB : public ParsingCallbacks { - CB(ClangdServer::DynamicIndex *This) : This(This) {} - DynamicIndex *This; - - void onPreambleAST(PathRef Path, ASTContext &Ctx, - std::shared_ptr PP) override { - This->PreambleIdx.update(Path, &Ctx, std::move(PP)); - } +// Returns callbacks that can be used to update the FileIndex with new ASTs. +std::unique_ptr makeUpdateCallbacks(FileIndex *FIndex) { + struct CB : public ParsingCallbacks { + CB(FileIndex *FIndex) : FIndex(FIndex) {} + FileIndex *FIndex; - void onMainAST(PathRef Path, ParsedAST &AST) override { - This->MainFileIdx.update(Path, &AST.getASTContext(), - AST.getPreprocessorPtr(), - AST.getLocalTopLevelDecls()); - } - }; - return llvm::make_unique(this); - }; + void onPreambleAST(PathRef Path, ASTContext &Ctx, + std::shared_ptr PP) override { + FIndex->updatePreamble(Path, Ctx, std::move(PP)); + } -private: - // Contains information from each file's preamble only. - // These are large, but update fairly infrequently (preambles are stable). - // Missing information: - // - symbol refs (these are always "from the main file") - // - definition locations in the main file - // - // FIXME: Because the preambles for different TUs have large overlap and - // FileIndex doesn't deduplicate, this uses lots of extra RAM. - // The biggest obstacle in fixing this: the obvious approach of partitioning - // by declaring file (rather than main file) fails if headers provide - // different symbols based on preprocessor state. - FileIndex PreambleIdx; - // Contains information from each file's main AST. - // These are updated frequently (on file change), but are relatively small. - // Mostly contains: - // - refs to symbols declared in the preamble and referenced from main - // - symbols declared both in the main file and the preamble - // (Note that symbols *only* in the main file are not indexed). - FileIndex MainFileIdx; - std::unique_ptr MergedIndex; -}; + void onMainAST(PathRef Path, ParsedAST &AST) override { + FIndex->updateMain(Path, AST, AST.getLocalTopLevelDecls()); + } + }; + return llvm::make_unique(FIndex); +} ClangdServer::Options ClangdServer::optsForTest() { ClangdServer::Options Opts; @@ -139,9 +104,8 @@ ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB, : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider), ResourceDir(Opts.ResourceDir ? Opts.ResourceDir->str() : getStandardResourceDir()), - DynamicIdx(Opts.BuildDynamicSymbolIndex - ? new DynamicIndex(Opts.URISchemes) - : nullptr), + DynamicIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex(Opts.URISchemes) + : nullptr), PCHs(std::make_shared()), // Pass a callback into `WorkScheduler` to extract symbols from a newly // parsed file and rebuild the file index synchronously each time an AST @@ -149,7 +113,8 @@ ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB, // FIXME(ioeric): this can be slow and we may be able to index on less // critical paths. WorkScheduler(Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory, - DynamicIdx ? DynamicIdx->makeUpdateCallbacks() : nullptr, + DynamicIdx ? makeUpdateCallbacks(DynamicIdx.get()) + : nullptr, Opts.UpdateDebounce, Opts.RetentionPolicy) { if (DynamicIdx && Opts.StaticIndex) { MergedIndex = mergeIndex(&DynamicIdx->index(), Opts.StaticIndex); @@ -162,10 +127,6 @@ ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB, Index = nullptr; } -// Destructor has to be in .cpp file to see the definition of -// ClangdServer::DynamicIndex. -ClangdServer::~ClangdServer() = default; - const SymbolIndex *ClangdServer::dynamicIndex() const { return DynamicIdx ? &DynamicIdx->index() : nullptr; } diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h index afbda4bd8..1706ebb0e 100644 --- a/clangd/ClangdServer.h +++ b/clangd/ClangdServer.h @@ -111,7 +111,6 @@ class ClangdServer { /// synchronize access to shared state. ClangdServer(GlobalCompilationDatabase &CDB, FileSystemProvider &FSProvider, DiagnosticsConsumer &DiagConsumer, const Options &Opts); - ~ClangdServer(); /// Set the root path of the workspace. void setRootPath(PathRef RootPath); @@ -221,7 +220,6 @@ class ClangdServer { formatCode(llvm::StringRef Code, PathRef File, ArrayRef Ranges); - class DynamicIndex; typedef uint64_t DocVersion; void consumeDiagnostics(PathRef File, DocVersion Version, @@ -244,7 +242,7 @@ class ClangdServer { // - a merged view of a static and dynamic index (MergedIndex) const SymbolIndex *Index; // If present, an index of symbols in open files. Read via *Index. - std::unique_ptr DynamicIdx; + std::unique_ptr DynamicIdx; // If present, storage for the merged static/dynamic index. Read via *Index. std::unique_ptr MergedIndex; diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 6b546747a..ad7f6345f 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -8,18 +8,22 @@ //===----------------------------------------------------------------------===// #include "FileIndex.h" +#include "ClangdUnit.h" #include "Logger.h" #include "SymbolCollector.h" +#include "index/Index.h" +#include "index/Merge.h" #include "clang/Index/IndexingAction.h" #include "clang/Lex/Preprocessor.h" +#include namespace clang { namespace clangd { -std::pair -indexAST(ASTContext &AST, std::shared_ptr PP, - llvm::Optional> TopLevelDecls, - llvm::ArrayRef URISchemes) { +static std::pair +indexSymbols(ASTContext &AST, std::shared_ptr PP, + llvm::ArrayRef DeclsToIndex, bool IsIndexMainAST, + llvm::ArrayRef URISchemes) { SymbolCollector::Options CollectorOpts; // FIXME(ioeric): we might also want to collect include headers. We would need // to make sure all includes are canonicalized (with CanonicalIncludes), which @@ -28,9 +32,9 @@ indexAST(ASTContext &AST, std::shared_ptr PP, // CommentHandler for IWYU pragma) to canonicalize includes. CollectorOpts.CollectIncludePath = false; CollectorOpts.CountReferences = false; + CollectorOpts.Origin = SymbolOrigin::Dynamic; if (!URISchemes.empty()) CollectorOpts.URISchemes = URISchemes; - CollectorOpts.Origin = SymbolOrigin::Dynamic; index::IndexingOptions IndexOpts; // We only need declarations, because we don't count references. @@ -38,18 +42,8 @@ indexAST(ASTContext &AST, std::shared_ptr PP, index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly; IndexOpts.IndexFunctionLocals = false; - std::vector DeclsToIndex; - if (TopLevelDecls) - DeclsToIndex.assign(TopLevelDecls->begin(), TopLevelDecls->end()); - else - DeclsToIndex.assign(AST.getTranslationUnitDecl()->decls().begin(), - AST.getTranslationUnitDecl()->decls().end()); - - // We only collect refs when indexing main AST. - // FIXME: this is a hacky way to detect whether we are indexing preamble AST - // or main AST, we should make it explicitly. - bool IsIndexMainAST = TopLevelDecls.hasValue(); if (IsIndexMainAST) + // We only collect refs when indexing main AST. CollectorOpts.RefFilter = RefKind::All; SymbolCollector Collector(std::move(CollectorOpts)); @@ -62,17 +56,30 @@ indexAST(ASTContext &AST, std::shared_ptr PP, auto Syms = Collector.takeSymbols(); auto Refs = Collector.takeRefs(); - vlog("index {0}AST for {1}: \n" + vlog("index AST for {0} (main={1}): \n" " symbol slab: {2} symbols, {3} bytes\n" " ref slab: {4} symbols, {5} bytes", - IsIndexMainAST ? "Main" : "Preamble", FileName, Syms.size(), - Syms.bytes(), Refs.size(), Refs.bytes()); + FileName, IsIndexMainAST, Syms.size(), Syms.bytes(), Refs.size(), + Refs.bytes()); return {std::move(Syms), std::move(Refs)}; } -FileIndex::FileIndex(std::vector URISchemes) - : URISchemes(std::move(URISchemes)) { - reset(FSymbols.buildMemIndex()); +std::pair +indexMainDecls(ParsedAST &AST, llvm::ArrayRef TopLevelDecls, + llvm::ArrayRef URISchemes) { + return indexSymbols(AST.getASTContext(), AST.getPreprocessorPtr(), + TopLevelDecls, + /*IsIndexMainAST=*/true, URISchemes); +} + +SymbolSlab indexHeaderSymbols(ASTContext &AST, std::shared_ptr PP, + llvm::ArrayRef URISchemes) { + std::vector DeclsToIndex( + AST.getTranslationUnitDecl()->decls().begin(), + AST.getTranslationUnitDecl()->decls().end()); + return indexSymbols(AST, std::move(PP), DeclsToIndex, + /*IsIndexMainAST=*/false, URISchemes) + .first; } void FileSymbols::update(PathRef Path, std::unique_ptr Symbols, @@ -141,19 +148,28 @@ std::unique_ptr FileSymbols::buildMemIndex() { StorageSize); } -void FileIndex::update(PathRef Path, ASTContext *AST, - std::shared_ptr PP, - llvm::Optional> TopLevelDecls) { - if (!AST) { - FSymbols.update(Path, nullptr, nullptr); - } else { - assert(PP); - auto Contents = indexAST(*AST, PP, TopLevelDecls, URISchemes); - FSymbols.update(Path, - llvm::make_unique(std::move(Contents.first)), - llvm::make_unique(std::move(Contents.second))); - } - reset(FSymbols.buildMemIndex()); +FileIndex::FileIndex(std::vector URISchemes) + : URISchemes(std::move(URISchemes)), + PreambleIndex(PreambleSymbols.buildMemIndex()), + MainFileIndex(MainFileSymbols.buildMemIndex()), + MergedIndex(mergeIndex(&MainFileIndex, &PreambleIndex)) {} + +void FileIndex::updatePreamble(PathRef Path, ASTContext &AST, + std::shared_ptr PP) { + auto Symbols = indexHeaderSymbols(AST, std::move(PP), URISchemes); + PreambleSymbols.update(Path, + llvm::make_unique(std::move(Symbols)), + llvm::make_unique()); + PreambleIndex.reset(PreambleSymbols.buildMemIndex()); +} + +void FileIndex::updateMain(PathRef Path, ParsedAST &AST, + llvm::ArrayRef TopLevelDecls) { + auto Contents = indexMainDecls(AST, TopLevelDecls, URISchemes); + MainFileSymbols.update( + Path, llvm::make_unique(std::move(Contents.first)), + llvm::make_unique(std::move(Contents.second))); + MainFileIndex.reset(MainFileSymbols.buildMemIndex()); } } // namespace clangd diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index 3b0b6fa8d..421cfa4e8 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -20,6 +20,7 @@ #include "Index.h" #include "MemIndex.h" #include "clang/Lex/Preprocessor.h" +#include namespace clang { namespace clangd { @@ -57,39 +58,66 @@ class FileSymbols { }; /// This manages symbols from files and an in-memory index on all symbols. -class FileIndex : public SwapIndex { +/// FIXME: Expose an interface to remove files that are closed. +class FileIndex { public: /// If URISchemes is empty, the default schemes in SymbolCollector will be /// used. FileIndex(std::vector URISchemes = {}); - /// Update symbols in \p Path with symbols in \p AST. If \p AST is - /// nullptr, this removes all symbols in the file. - /// If \p AST is not null, \p PP cannot be null and it should be the - /// preprocessor that was used to build \p AST. - /// If \p TopLevelDecls is set, only these decls are indexed. Otherwise, all - /// top level decls obtained from \p AST are indexed. - void - update(PathRef Path, ASTContext *AST, std::shared_ptr PP, - llvm::Optional> TopLevelDecls = llvm::None); + // Presents a merged view of the supplied main-file and preamble ASTs. + const SymbolIndex &index() const { return *MergedIndex; } -private: - // Only update() should swap the index. - using SwapIndex::reset; + /// Update preamble symbols of file \p Path with all declarations in \p AST + /// and macros in \p PP. + void updatePreamble(PathRef Path, ASTContext &AST, + std::shared_ptr PP); + + /// Update symbols from main file \p Path with symbols in \p TopLevelDecls. + void updateMain(PathRef Path, ParsedAST &AST, + llvm::ArrayRef TopLevelDecls); - FileSymbols FSymbols; +private: std::vector URISchemes; + + // Contains information from each file's preamble only. + // These are large, but update fairly infrequently (preambles are stable). + // Missing information: + // - symbol refs (these are always "from the main file") + // - definition locations in the main file + // + // FIXME: Because the preambles for different TUs have large overlap and + // FileIndex doesn't deduplicate, this uses lots of extra RAM. + // The biggest obstacle in fixing this: the obvious approach of partitioning + // by declaring file (rather than main file) fails if headers provide + // different symbols based on preprocessor state. + FileSymbols PreambleSymbols; + SwapIndex PreambleIndex; + + // Contains information from each file's main AST. + // These are updated frequently (on file change), but are relatively small. + // Mostly contains: + // - refs to symbols declared in the preamble and referenced from main + // - symbols declared both in the main file and the preamble + // (Note that symbols *only* in the main file are not indexed). + FileSymbols MainFileSymbols; + SwapIndex MainFileIndex; + + std::unique_ptr MergedIndex; // Merge preamble and main index. }; -/// Retrieves symbols and refs in \p AST. +/// Retrieves symbols and refs of \p Decls in \p AST. /// Exposed to assist in unit tests. /// If URISchemes is empty, the default schemes in SymbolCollector will be used. -/// If \p TopLevelDecls is set, only these decls are indexed. Otherwise, all top -/// level decls obtained from \p AST are indexed. std::pair -indexAST(ASTContext &AST, std::shared_ptr PP, - llvm::Optional> TopLevelDecls = llvm::None, - llvm::ArrayRef URISchemes = {}); +indexMainDecls(ParsedAST &AST, llvm::ArrayRef Decls, + llvm::ArrayRef URISchemes = {}); + +/// Idex declarations from \p AST and macros from \p PP that are declared in +/// included headers. +/// If URISchemes is empty, the default schemes in SymbolCollector will be used. +SymbolSlab indexHeaderSymbols(ASTContext &AST, std::shared_ptr PP, + llvm::ArrayRef URISchemes = {}); } // namespace clangd } // namespace clang diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index 096ccd9e6..346560acc 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -119,10 +119,10 @@ TEST(FileSymbolsTest, SnapshotAliveAfterRemove) { EXPECT_THAT(getRefs(*Symbols, ID), RefsAre({FileURI("f1.cc")})); } -std::vector match(const SymbolIndex &I, +std::vector match(const FileIndex &I, const FuzzyFindRequest &Req) { std::vector Matches; - I.fuzzyFind(Req, [&](const Symbol &Sym) { + I.index().fuzzyFind(Req, [&](const Symbol &Sym) { Matches.push_back((Sym.Scope + Sym.Name).str()); }); return Matches; @@ -135,7 +135,8 @@ void update(FileIndex &M, llvm::StringRef Basename, llvm::StringRef Code) { File.HeaderFilename = (Basename + ".h").str(); File.HeaderCode = Code; auto AST = File.build(); - M.update(File.Filename, &AST.getASTContext(), AST.getPreprocessorPtr()); + M.updatePreamble(File.Filename, AST.getASTContext(), + AST.getPreprocessorPtr()); } TEST(FileIndexTest, CustomizedURIScheme) { @@ -145,7 +146,7 @@ TEST(FileIndexTest, CustomizedURIScheme) { FuzzyFindRequest Req; Req.Query = ""; bool SeenSymbol = false; - M.fuzzyFind(Req, [&](const Symbol &Sym) { + M.index().fuzzyFind(Req, [&](const Symbol &Sym) { EXPECT_EQ(Sym.CanonicalDeclaration.FileURI, "unittest:///f.h"); SeenSymbol = true; }); @@ -182,25 +183,6 @@ TEST(FileIndexTest, IndexMultiASTAndDeduplicate) { EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns::f", "ns::X", "ns::ff")); } -TEST(FileIndexTest, RemoveAST) { - FileIndex M; - update(M, "f1", "namespace ns { void f() {} class X {}; }"); - - FuzzyFindRequest Req; - Req.Query = ""; - Req.Scopes = {"ns::"}; - EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns::f", "ns::X")); - - M.update("f1.cpp", nullptr, nullptr); - EXPECT_THAT(match(M, Req), UnorderedElementsAre()); -} - -TEST(FileIndexTest, RemoveNonExisting) { - FileIndex M; - M.update("no.cpp", nullptr, nullptr); - EXPECT_THAT(match(M, FuzzyFindRequest()), UnorderedElementsAre()); -} - TEST(FileIndexTest, ClassMembers) { FileIndex M; update(M, "f1", "class X { static int m1; int m2; static void f(); };"); @@ -218,7 +200,7 @@ TEST(FileIndexTest, NoIncludeCollected) { FuzzyFindRequest Req; Req.Query = ""; bool SeenSymbol = false; - M.fuzzyFind(Req, [&](const Symbol &Sym) { + M.index().fuzzyFind(Req, [&](const Symbol &Sym) { EXPECT_TRUE(Sym.IncludeHeaders.empty()); SeenSymbol = true; }); @@ -242,7 +224,7 @@ vector make_vector(Arg A) {} Req.Query = ""; bool SeenVector = false; bool SeenMakeVector = false; - M.fuzzyFind(Req, [&](const Symbol &Sym) { + M.index().fuzzyFind(Req, [&](const Symbol &Sym) { if (Sym.Name == "vector") { EXPECT_EQ(Sym.Signature, ""); EXPECT_EQ(Sym.CompletionSnippetSuffix, "<${1:class Ty}>"); @@ -296,7 +278,7 @@ TEST(FileIndexTest, RebuildWithPreamble) { [&](ASTContext &Ctx, std::shared_ptr PP) { EXPECT_FALSE(IndexUpdated) << "Expected only a single index update"; IndexUpdated = true; - Index.update(FooCpp, &Ctx, std::move(PP)); + Index.updatePreamble(FooCpp, Ctx, std::move(PP)); }); ASSERT_TRUE(IndexUpdated); @@ -332,18 +314,16 @@ TEST(FileIndexTest, Refs) { Test.Code = MainCode.code(); Test.Filename = "test.cc"; auto AST = Test.build(); - Index.update(Test.Filename, &AST.getASTContext(), AST.getPreprocessorPtr(), - AST.getLocalTopLevelDecls()); + Index.updateMain(Test.Filename, AST, AST.getLocalTopLevelDecls()); // Add test2.cc TestTU Test2; Test2.HeaderCode = HeaderCode; Test2.Code = MainCode.code(); Test2.Filename = "test2.cc"; AST = Test2.build(); - Index.update(Test2.Filename, &AST.getASTContext(), AST.getPreprocessorPtr(), - AST.getLocalTopLevelDecls()); + Index.updateMain(Test2.Filename, AST, AST.getLocalTopLevelDecls()); - EXPECT_THAT(getRefs(Index, Foo.ID), + EXPECT_THAT(getRefs(Index.index(), Foo.ID), RefsAre({AllOf(RefRange(MainCode.range("foo")), FileURI("unittest:///test.cc")), AllOf(RefRange(MainCode.range("foo")), diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp index 187b3c556..1f8129f52 100644 --- a/unittests/clangd/IndexTests.cpp +++ b/unittests/clangd/IndexTests.cpp @@ -231,7 +231,7 @@ TEST(MergeTest, PreferSymbolWithDefn) { TEST(MergeIndexTest, Refs) { FileIndex Dyn({"unittest"}); FileIndex StaticIndex({"unittest"}); - auto MergedIndex = mergeIndex(&Dyn, &StaticIndex); + auto MergedIndex = mergeIndex(&Dyn.index(), &StaticIndex.index()); const char *HeaderCode = "class Foo;"; auto HeaderSymbols = TestTU::withHeaderCode("class Foo;").headerSymbols(); @@ -244,8 +244,7 @@ TEST(MergeIndexTest, Refs) { Test.Code = Test1Code.code(); Test.Filename = "test.cc"; auto AST = Test.build(); - Dyn.update(Test.Filename, &AST.getASTContext(), AST.getPreprocessorPtr(), - AST.getLocalTopLevelDecls()); + Dyn.updateMain(Test.Filename, AST, AST.getLocalTopLevelDecls()); // Build static index for test.cc. Test.HeaderCode = HeaderCode; @@ -253,9 +252,8 @@ TEST(MergeIndexTest, Refs) { Test.Filename = "test.cc"; auto StaticAST = Test.build(); // Add stale refs for test.cc. - StaticIndex.update(Test.Filename, &StaticAST.getASTContext(), - StaticAST.getPreprocessorPtr(), - StaticAST.getLocalTopLevelDecls()); + StaticIndex.updateMain(Test.Filename, StaticAST, + StaticAST.getLocalTopLevelDecls()); // Add refs for test2.cc Annotations Test2Code(R"(class $Foo[[Foo]] {};)"); @@ -264,9 +262,8 @@ TEST(MergeIndexTest, Refs) { Test2.Code = Test2Code.code(); Test2.Filename = "test2.cc"; StaticAST = Test2.build(); - StaticIndex.update(Test2.Filename, &StaticAST.getASTContext(), - StaticAST.getPreprocessorPtr(), - StaticAST.getLocalTopLevelDecls()); + StaticIndex.updateMain(Test2.Filename, StaticAST, + StaticAST.getLocalTopLevelDecls()); RefsRequest Request; Request.IDs = {Foo.ID}; diff --git a/unittests/clangd/TestTU.cpp b/unittests/clangd/TestTU.cpp index e181f689c..847f79657 100644 --- a/unittests/clangd/TestTU.cpp +++ b/unittests/clangd/TestTU.cpp @@ -45,13 +45,13 @@ ParsedAST TestTU::build() const { SymbolSlab TestTU::headerSymbols() const { auto AST = build(); - return indexAST(AST.getASTContext(), AST.getPreprocessorPtr()).first; + return indexHeaderSymbols(AST.getASTContext(), AST.getPreprocessorPtr()); } +// FIXME: This should return a FileIndex with both preamble and main index. std::unique_ptr TestTU::index() const { auto AST = build(); - auto Content = indexAST(AST.getASTContext(), AST.getPreprocessorPtr(), - AST.getLocalTopLevelDecls()); + auto Content = indexMainDecls(AST, AST.getLocalTopLevelDecls()); return MemIndex::build(std::move(Content.first), std::move(Content.second)); } From 5060576a82310b85d33bf38fd28059e3bad8f55f Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Tue, 18 Sep 2018 11:49:20 +0000 Subject: [PATCH 221/686] [clang-tidy] use CHECK-NOTES in bugprone-unused-return-value Reviewers: aaron.ballman, alexfh, hokein Reviewed By: alexfh Subscribers: xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D52187 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342468 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../bugprone-unused-return-value-custom.cpp | 24 ++++--- .../bugprone-unused-return-value.cpp | 69 ++++++++++++------- 2 files changed, 62 insertions(+), 31 deletions(-) diff --git a/test/clang-tidy/bugprone-unused-return-value-custom.cpp b/test/clang-tidy/bugprone-unused-return-value-custom.cpp index efe3705e3..53ace6358 100644 --- a/test/clang-tidy/bugprone-unused-return-value-custom.cpp +++ b/test/clang-tidy/bugprone-unused-return-value-custom.cpp @@ -47,32 +47,40 @@ void fun(int); void warning() { fun(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value returned by this function should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast the expression to void to silence this warning (fun()); - // CHECK-MESSAGES: [[@LINE-1]]:4: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:4: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:4: note: cast {{.*}} this warning ns::Outer::Inner ObjA1; ObjA1.memFun(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning ns::AliasName::Inner ObjA2; ObjA2.memFun(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning ns::Derived ObjA3; ObjA3.memFun(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning ns::Type::staticFun(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning ns::ClassTemplate ObjA4; ObjA4.memFun(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning ns::ClassTemplate::staticFun(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning } void noWarning() { diff --git a/test/clang-tidy/bugprone-unused-return-value.cpp b/test/clang-tidy/bugprone-unused-return-value.cpp index b4f280eba..797f56d9f 100644 --- a/test/clang-tidy/bugprone-unused-return-value.cpp +++ b/test/clang-tidy/bugprone-unused-return-value.cpp @@ -74,93 +74,116 @@ void useFuture(const std::future &fut); void warning() { std::async(increment, 42); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value returned by this function should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast the expression to void to silence this warning std::async(std::launch::async, increment, 42); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning Foo F; std::launder(&F); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning std::remove(nullptr, nullptr, 1); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning std::remove_if(nullptr, nullptr, nullptr); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning std::unique(nullptr, nullptr); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning std::unique_ptr UPtr; UPtr.release(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning std::string Str; Str.empty(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning std::vector Vec; Vec.empty(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning // test discarding return values inside different kinds of statements auto Lambda = [] { std::remove(nullptr, nullptr, 1); }; - // CHECK-MESSAGES: [[@LINE-1]]:22: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:22: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:22: note: cast {{.*}} this warning if (true) std::remove(nullptr, nullptr, 1); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning else if (true) std::remove(nullptr, nullptr, 1); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning else std::remove(nullptr, nullptr, 1); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning while (true) std::remove(nullptr, nullptr, 1); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning do std::remove(nullptr, nullptr, 1); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning while (true); for (;;) std::remove(nullptr, nullptr, 1); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning for (std::remove(nullptr, nullptr, 1);;) - // CHECK-MESSAGES: [[@LINE-1]]:8: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:8: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:8: note: cast {{.*}} this warning ; for (;; std::remove(nullptr, nullptr, 1)) - // CHECK-MESSAGES: [[@LINE-1]]:11: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:11: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:11: note: cast {{.*}} this warning ; for (auto C : "foo") std::remove(nullptr, nullptr, 1); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning switch (1) { case 1: std::remove(nullptr, nullptr, 1); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning break; default: std::remove(nullptr, nullptr, 1); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning break; } try { std::remove(nullptr, nullptr, 1); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning } catch (...) { std::remove(nullptr, nullptr, 1); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: the value returned by this function should be used [bugprone-unused-return-value] + // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning } } From d9a480a96a51a582c45fc6a94c86ed0a612f58b3 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 18 Sep 2018 13:35:16 +0000 Subject: [PATCH 222/686] [clangd] Get rid of Decls parameter in indexMainDecls. NFC It's already available in ParsedAST. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342473 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 2 +- clangd/index/FileIndex.cpp | 10 ++++------ clangd/index/FileIndex.h | 12 ++++++------ unittests/clangd/FileIndexTests.cpp | 4 ++-- unittests/clangd/IndexTests.cpp | 8 +++----- unittests/clangd/TestTU.cpp | 2 +- 6 files changed, 17 insertions(+), 21 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 6f2d13e0b..153dce294 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -83,7 +83,7 @@ std::unique_ptr makeUpdateCallbacks(FileIndex *FIndex) { } void onMainAST(PathRef Path, ParsedAST &AST) override { - FIndex->updateMain(Path, AST, AST.getLocalTopLevelDecls()); + FIndex->updateMain(Path, AST); } }; return llvm::make_unique(FIndex); diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index ad7f6345f..22a32ccab 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -65,10 +65,9 @@ indexSymbols(ASTContext &AST, std::shared_ptr PP, } std::pair -indexMainDecls(ParsedAST &AST, llvm::ArrayRef TopLevelDecls, - llvm::ArrayRef URISchemes) { +indexMainDecls(ParsedAST &AST, llvm::ArrayRef URISchemes) { return indexSymbols(AST.getASTContext(), AST.getPreprocessorPtr(), - TopLevelDecls, + AST.getLocalTopLevelDecls(), /*IsIndexMainAST=*/true, URISchemes); } @@ -163,9 +162,8 @@ void FileIndex::updatePreamble(PathRef Path, ASTContext &AST, PreambleIndex.reset(PreambleSymbols.buildMemIndex()); } -void FileIndex::updateMain(PathRef Path, ParsedAST &AST, - llvm::ArrayRef TopLevelDecls) { - auto Contents = indexMainDecls(AST, TopLevelDecls, URISchemes); +void FileIndex::updateMain(PathRef Path, ParsedAST &AST) { + auto Contents = indexMainDecls(AST, URISchemes); MainFileSymbols.update( Path, llvm::make_unique(std::move(Contents.first)), llvm::make_unique(std::move(Contents.second))); diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index 421cfa4e8..7226e1668 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -73,9 +73,9 @@ class FileIndex { void updatePreamble(PathRef Path, ASTContext &AST, std::shared_ptr PP); - /// Update symbols from main file \p Path with symbols in \p TopLevelDecls. - void updateMain(PathRef Path, ParsedAST &AST, - llvm::ArrayRef TopLevelDecls); + /// Update symbols and references from main file \p Path with + /// `indexMainDecls`. + void updateMain(PathRef Path, ParsedAST &AST); private: std::vector URISchemes; @@ -106,12 +106,12 @@ class FileIndex { std::unique_ptr MergedIndex; // Merge preamble and main index. }; -/// Retrieves symbols and refs of \p Decls in \p AST. +/// Retrieves symbols and refs of local top level decls in \p AST (i.e. +/// `AST.getLocalTopLevelDecls()`). /// Exposed to assist in unit tests. /// If URISchemes is empty, the default schemes in SymbolCollector will be used. std::pair -indexMainDecls(ParsedAST &AST, llvm::ArrayRef Decls, - llvm::ArrayRef URISchemes = {}); +indexMainDecls(ParsedAST &AST, llvm::ArrayRef URISchemes = {}); /// Idex declarations from \p AST and macros from \p PP that are declared in /// included headers. diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index 346560acc..f88355194 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -314,14 +314,14 @@ TEST(FileIndexTest, Refs) { Test.Code = MainCode.code(); Test.Filename = "test.cc"; auto AST = Test.build(); - Index.updateMain(Test.Filename, AST, AST.getLocalTopLevelDecls()); + Index.updateMain(Test.Filename, AST); // Add test2.cc TestTU Test2; Test2.HeaderCode = HeaderCode; Test2.Code = MainCode.code(); Test2.Filename = "test2.cc"; AST = Test2.build(); - Index.updateMain(Test2.Filename, AST, AST.getLocalTopLevelDecls()); + Index.updateMain(Test2.Filename, AST); EXPECT_THAT(getRefs(Index.index(), Foo.ID), RefsAre({AllOf(RefRange(MainCode.range("foo")), diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp index 1f8129f52..b774742c3 100644 --- a/unittests/clangd/IndexTests.cpp +++ b/unittests/clangd/IndexTests.cpp @@ -244,7 +244,7 @@ TEST(MergeIndexTest, Refs) { Test.Code = Test1Code.code(); Test.Filename = "test.cc"; auto AST = Test.build(); - Dyn.updateMain(Test.Filename, AST, AST.getLocalTopLevelDecls()); + Dyn.updateMain(Test.Filename, AST); // Build static index for test.cc. Test.HeaderCode = HeaderCode; @@ -252,8 +252,7 @@ TEST(MergeIndexTest, Refs) { Test.Filename = "test.cc"; auto StaticAST = Test.build(); // Add stale refs for test.cc. - StaticIndex.updateMain(Test.Filename, StaticAST, - StaticAST.getLocalTopLevelDecls()); + StaticIndex.updateMain(Test.Filename, StaticAST); // Add refs for test2.cc Annotations Test2Code(R"(class $Foo[[Foo]] {};)"); @@ -262,8 +261,7 @@ TEST(MergeIndexTest, Refs) { Test2.Code = Test2Code.code(); Test2.Filename = "test2.cc"; StaticAST = Test2.build(); - StaticIndex.updateMain(Test2.Filename, StaticAST, - StaticAST.getLocalTopLevelDecls()); + StaticIndex.updateMain(Test2.Filename, StaticAST); RefsRequest Request; Request.IDs = {Foo.ID}; diff --git a/unittests/clangd/TestTU.cpp b/unittests/clangd/TestTU.cpp index 847f79657..4b610c8f9 100644 --- a/unittests/clangd/TestTU.cpp +++ b/unittests/clangd/TestTU.cpp @@ -51,7 +51,7 @@ SymbolSlab TestTU::headerSymbols() const { // FIXME: This should return a FileIndex with both preamble and main index. std::unique_ptr TestTU::index() const { auto AST = build(); - auto Content = indexMainDecls(AST, AST.getLocalTopLevelDecls()); + auto Content = indexMainDecls(AST); return MemIndex::build(std::move(Content.first), std::move(Content.second)); } From 96b05ccba72740d382b7866763ea2e28b3f5aec3 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 18 Sep 2018 19:00:59 +0000 Subject: [PATCH 223/686] [clangd] Fix error handling for SymbolID parsing (notably YAML and dexp) git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342505 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Index.cpp | 9 +++++++-- clangd/index/Index.h | 12 +++--------- clangd/index/SymbolYAML.cpp | 11 +++++++---- clangd/index/dex/dexp/Dexp.cpp | 8 ++++---- clangd/indexer/IndexerMain.cpp | 3 +-- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index cfc49406c..dec95fcf9 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -41,8 +41,13 @@ SymbolID SymbolID::fromRaw(llvm::StringRef Raw) { std::string SymbolID::str() const { return toHex(raw()); } -void operator>>(StringRef Str, SymbolID &ID) { - ID = SymbolID::fromRaw(fromHex(Str)); +llvm::Expected SymbolID::fromStr(llvm::StringRef Str) { + if (Str.size() != RawSize * 2) + return createStringError(llvm::inconvertibleErrorCode(), "Bad ID length"); + for (char C : Str) + if (!isHexDigit(C)) + return createStringError(llvm::inconvertibleErrorCode(), "Bad hex ID"); + return fromRaw(fromHex(Str)); } raw_ostream &operator<<(raw_ostream &OS, SymbolOrigin O) { diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 8a18c6197..746638229 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -91,12 +91,12 @@ class SymbolID { return StringRef(reinterpret_cast(HashValue.data()), RawSize); } static SymbolID fromRaw(llvm::StringRef); + // Returns a 40-bytes hex encoded string. std::string str() const; + static llvm::Expected fromStr(llvm::StringRef); private: - friend void operator>>(llvm::StringRef Str, SymbolID &ID); - std::array HashValue; }; @@ -108,15 +108,9 @@ inline llvm::hash_code hash_value(const SymbolID &ID) { return llvm::hash_code(Result); } -// Write SymbolID into the given stream. SymbolID is encoded as a 40-bytes -// hex string. +// Write SymbolID into the given stream. SymbolID is encoded as ID.str(). llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SymbolID &ID); -// Construct SymbolID from a hex string. -// The HexStr is required to be a 40-bytes hex string, which is encoded from the -// "<<" operator. -void operator>>(llvm::StringRef HexStr, SymbolID &ID); - } // namespace clangd } // namespace clang namespace llvm { diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/SymbolYAML.cpp index 01c7987ac..4dec54782 100644 --- a/clangd/index/SymbolYAML.cpp +++ b/clangd/index/SymbolYAML.cpp @@ -40,10 +40,13 @@ struct NormalizedSymbolID { OS << ID; } - SymbolID denormalize(IO &) { - SymbolID ID; - HexString >> ID; - return ID; + SymbolID denormalize(IO &I) { + auto ID = SymbolID::fromStr(HexString); + if (!ID) { + I.setError(llvm::toString(ID.takeError())); + return SymbolID(); + } + return *ID; } std::string HexString; diff --git a/clangd/index/dex/dexp/Dexp.cpp b/clangd/index/dex/dexp/Dexp.cpp index 09c310ff5..afaace3ac 100644 --- a/clangd/index/dex/dexp/Dexp.cpp +++ b/clangd/index/dex/dexp/Dexp.cpp @@ -144,14 +144,14 @@ class Lookup : public Command { }; void run() override { - auto Raw = fromHex(ID); - if (Raw.size() != clang::clangd::SymbolID::RawSize) { - llvm::outs() << "invalid SymbolID\n"; + auto SID = clang::clangd::SymbolID::fromStr(ID); + if (!SID) { + llvm::outs() << llvm::toString(SID.takeError()) << "\n"; return; } clang::clangd::LookupRequest Request; - Request.IDs = {clang::clangd::SymbolID::fromRaw(Raw)}; + Request.IDs = {*SID}; bool FoundSymbol = false; Index->lookup(Request, [&](const Symbol &Sym) { FoundSymbol = true; diff --git a/clangd/indexer/IndexerMain.cpp b/clangd/indexer/IndexerMain.cpp index fc85c54e2..24551bf61 100644 --- a/clangd/indexer/IndexerMain.cpp +++ b/clangd/indexer/IndexerMain.cpp @@ -172,8 +172,7 @@ class ToolExecutorConsumer : public SymbolsConsumer { [&](llvm::StringRef Key, llvm::StringRef Value) { llvm::yaml::Input Yin(Value); auto Sym = clang::clangd::SymbolFromYAML(Yin); - clang::clangd::SymbolID ID; - Key >> ID; + auto ID = cantFail(clang::clangd::SymbolID::fromStr(Key)); if (const auto *Existing = UniqueSymbols.find(ID)) UniqueSymbols.insert(mergeSymbol(*Existing, Sym)); else From 75e098bf43c0d997e3280b7eb8f95843b85b3358 Mon Sep 17 00:00:00 2001 From: Artem Belevich Date: Tue, 18 Sep 2018 21:51:02 +0000 Subject: [PATCH 224/686] [clang-tidy] Replace redundant checks with an assert(). findStyleKind is only called if D is an explicit identifier with a name, so the checks for operators will never return true. The explicit assert() enforces this invariant. Differential Revision: https://reviews.llvm.org/D52179 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342514 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/readability/IdentifierNamingCheck.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tidy/readability/IdentifierNamingCheck.cpp index 90c34b372..e34fcbb3a 100644 --- a/clang-tidy/readability/IdentifierNamingCheck.cpp +++ b/clang-tidy/readability/IdentifierNamingCheck.cpp @@ -385,6 +385,9 @@ static StyleKind findStyleKind( const NamedDecl *D, const std::vector> &NamingStyles) { + assert(D && D->getIdentifier() && !D->getName().empty() && !D->isImplicit() && + "Decl must be an explicit identifier with a name."); + if (isa(D) && NamingStyles[SK_ObjcIvar]) return SK_ObjcIvar; @@ -548,8 +551,6 @@ static StyleKind findStyleKind( if (const auto *Decl = dyn_cast(D)) { if (Decl->isMain() || !Decl->isUserProvided() || - Decl->isUsualDeallocationFunction() || - Decl->isCopyAssignmentOperator() || Decl->isMoveAssignmentOperator() || Decl->size_overridden_methods() > 0) return SK_Invalid; From 309bf470260533fe878dde31064597c4ddc36c56 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Wed, 19 Sep 2018 09:35:04 +0000 Subject: [PATCH 225/686] [clangd] Store preamble macros in dynamic index. Summary: Pros: o Loading macros from preamble for every completion is slow (see profile). o Calculating macro USR is also slow (see profile). o Sema can provide a lot of macro completion results (e.g. when filter is empty, 60k for some large TUs!). Cons: o Slight memory increase in dynamic index (~1%). o Some extra work during preamble build (should be fine as preamble build and indexAST is way slower). Before: {F7195645} After: {F7195646} Reviewers: ilya-biryukov, sammccall Reviewed By: sammccall Subscribers: MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52078 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342529 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/FileIndex.cpp | 8 ++++++-- unittests/clangd/CodeCompleteTests.cpp | 28 +++++++++++++++++++++----- unittests/clangd/FileIndexTests.cpp | 16 +++++++++++++++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 22a32ccab..447a96c22 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -14,6 +14,7 @@ #include "index/Index.h" #include "index/Merge.h" #include "clang/Index/IndexingAction.h" +#include "clang/Lex/MacroInfo.h" #include "clang/Lex/Preprocessor.h" #include @@ -41,10 +42,13 @@ indexSymbols(ASTContext &AST, std::shared_ptr PP, IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly; IndexOpts.IndexFunctionLocals = false; - - if (IsIndexMainAST) + if (IsIndexMainAST) { // We only collect refs when indexing main AST. CollectorOpts.RefFilter = RefKind::All; + }else { + IndexOpts.IndexMacrosInPreprocessor = true; + CollectorOpts.CollectMacro = true; + } SymbolCollector Collector(std::move(CollectorOpts)); Collector.setPreprocessor(PP); diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 29781428b..c1e8404c7 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -223,12 +223,13 @@ TEST(CompletionTest, Filter) { void TestAfterDotCompletion(clangd::CodeCompleteOptions Opts) { auto Results = completions( R"cpp( - #define MACRO X - int global_var; int global_func(); + // Make sure this is not in preamble. + #define MACRO X + struct GlobalClass {}; struct ClassWithMembers { @@ -276,11 +277,12 @@ void TestAfterDotCompletion(clangd::CodeCompleteOptions Opts) { void TestGlobalScopeCompletion(clangd::CodeCompleteOptions Opts) { auto Results = completions( R"cpp( - #define MACRO X - int global_var; int global_func(); + // Make sure this is not in preamble. + #define MACRO X + struct GlobalClass {}; struct ClassWithMembers { @@ -430,10 +432,11 @@ TEST(CompletionTest, Snippets) { TEST(CompletionTest, Kinds) { auto Results = completions( R"cpp( - #define MACRO X int variable; struct Struct {}; int function(); + // make sure MACRO is not included in preamble. + #define MACRO 10 int X = ^ )cpp", {func("indexFunction"), var("indexVariable"), cls("indexClass")}); @@ -1921,6 +1924,21 @@ TEST(CompletionTest, MergeMacrosFromIndexAndSema) { UnorderedElementsAre(Named("Clangd_Macro_Test"))); } +TEST(CompletionTest, NoMacroFromPreambleIfIndexIsSet) { + auto Results = completions( + R"cpp(#define CLANGD_PREAMBLE x + + int x = 0; + #define CLANGD_MAIN x + void f() { CLANGD_^ } + )cpp", + {func("CLANGD_INDEX")}); + // Index is overriden in code completion options, so the preamble symbol is + // not seen. + EXPECT_THAT(Results.Completions, UnorderedElementsAre(Named("CLANGD_MAIN"), + Named("CLANGD_INDEX"))); +} + TEST(CompletionTest, DeprecatedResults) { std::string Body = R"cpp( void TestClangd(); diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index f88355194..d1858c5ff 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -15,6 +15,7 @@ #include "index/FileIndex.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/PCHContainerOperations.h" +#include "clang/Index/IndexSymbol.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/CompilationDatabase.h" #include "gtest/gtest.h" @@ -330,6 +331,21 @@ TEST(FileIndexTest, Refs) { FileURI("unittest:///test2.cc"))})); } +TEST(FileIndexTest, CollectMacros) { + FileIndex M; + update(M, "f", "#define CLANGD 1"); + + FuzzyFindRequest Req; + Req.Query = ""; + bool SeenSymbol = false; + M.index().fuzzyFind(Req, [&](const Symbol &Sym) { + EXPECT_EQ(Sym.Name, "CLANGD"); + EXPECT_EQ(Sym.SymInfo.Kind, index::SymbolKind::Macro); + SeenSymbol = true; + }); + EXPECT_TRUE(SeenSymbol); +} + } // namespace } // namespace clangd } // namespace clang From 0c2b65e809e6d110cfe1c15b76ecba045aa52e58 Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Wed, 19 Sep 2018 10:16:44 +0000 Subject: [PATCH 226/686] [clangd] Add option to enable/disable function argument snippets. Summary: Currently LSP clients cannot directly change EnableFunctionArgSnippets parameter. This patch is to provide them with a way to enable/disable that functionality. Reviewers: hokein, ioeric, ilya-biryukov Reviewed By: ilya-biryukov Subscribers: sammccall, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D51214 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342533 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/tool/ClangdMain.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index b3b34def0..b6839431c 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -169,6 +169,13 @@ static llvm::cl::opt CompileArgsFrom( "'compile_commands.json' files")), llvm::cl::init(FilesystemCompileArgs), llvm::cl::Hidden); +static llvm::cl::opt EnableFunctionArgSnippets( + "function-arg-placeholders", + llvm::cl::desc("When disabled, completions contain only parentheses for " + "function calls. When enabled, completions also contain " + "placeholders for method parameters."), + llvm::cl::init(clangd::CodeCompleteOptions().EnableFunctionArgSnippets)); + int main(int argc, char *argv[]) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) { @@ -296,6 +303,7 @@ int main(int argc, char *argv[]) { CCOpts.IncludeIndicator.NoInsert.clear(); } CCOpts.SpeculativeIndexRequest = Opts.StaticIndex; + CCOpts.EnableFunctionArgSnippets = EnableFunctionArgSnippets; // Initialize and run ClangdLSPServer. ClangdLSPServer LSPServer( From c2e6f37e1b110f5253d1bc9077b68adb4d8b054c Mon Sep 17 00:00:00 2001 From: Andi-Bogdan Postelnicu Date: Wed, 19 Sep 2018 11:52:20 +0000 Subject: [PATCH 227/686] [clang-tidy] run-clang-tidy.py - fails using python 3.7 Differential Revision: https://reviews.llvm.org/D51220 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342540 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/tool/run-clang-tidy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang-tidy/tool/run-clang-tidy.py b/clang-tidy/tool/run-clang-tidy.py index 3852ba2c0..93635cbef 100755 --- a/clang-tidy/tool/run-clang-tidy.py +++ b/clang-tidy/tool/run-clang-tidy.py @@ -167,9 +167,9 @@ def run_tidy(args, tmpdir, build_path, queue, lock, failed_files): if proc.returncode != 0: failed_files.append(name) with lock: - sys.stdout.write(' '.join(invocation) + '\n' + output + '\n') - if err > 0: - sys.stderr.write(err + '\n') + sys.stdout.write(' '.join(invocation) + '\n' + output.decode('utf-8') + '\n') + if len(err) > 0: + sys.stderr.write(err.decode('utf-8') + '\n') queue.task_done() From 0840364b547a09782107480383a93f4277b4d63e Mon Sep 17 00:00:00 2001 From: Eugene Zelenko Date: Thu, 20 Sep 2018 00:02:55 +0000 Subject: [PATCH 228/686] [Clang-tidy] Alphabetical sort of files/checks. Add space after clang-tidy in source code headers. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342601 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/abseil/AbseilTidyModule.cpp | 4 ++-- clang-tidy/abseil/CMakeLists.txt | 2 +- clang-tidy/add_new_check.py | 8 ++++---- clang-tidy/android/AndroidTidyModule.cpp | 6 +++--- clang-tidy/android/CMakeLists.txt | 2 +- clang-tidy/cppcoreguidelines/CMakeLists.txt | 2 +- clang-tidy/hicpp/CMakeLists.txt | 2 +- clang-tidy/misc/CMakeLists.txt | 4 ++-- clang-tidy/misc/MiscTidyModule.cpp | 6 +++--- clang-tidy/modernize/CMakeLists.txt | 2 +- clang-tidy/readability/CMakeLists.txt | 2 +- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tidy/abseil/AbseilTidyModule.cpp index b3de5c079..f9ed4e1bd 100644 --- a/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tidy/abseil/AbseilTidyModule.cpp @@ -34,10 +34,10 @@ class AbseilModule : public ClangTidyModule { CheckFactories.registerCheck("abseil-no-namespace"); CheckFactories.registerCheck( "abseil-redundant-strcat-calls"); - CheckFactories.registerCheck( - "abseil-string-find-startswith"); CheckFactories.registerCheck( "abseil-str-cat-append"); + CheckFactories.registerCheck( + "abseil-string-find-startswith"); } }; diff --git a/clang-tidy/abseil/CMakeLists.txt b/clang-tidy/abseil/CMakeLists.txt index e268b34bf..e3191b74f 100644 --- a/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tidy/abseil/CMakeLists.txt @@ -7,8 +7,8 @@ add_clang_library(clangTidyAbseilModule NoInternalDependenciesCheck.cpp NoNamespaceCheck.cpp RedundantStrcatCallsCheck.cpp - StringFindStartswithCheck.cpp StrCatAppendCheck.cpp + StringFindStartswithCheck.cpp LINK_LIBS clangAST diff --git a/clang-tidy/add_new_check.py b/clang-tidy/add_new_check.py index 4d811aa1f..eb175b4ac 100755 --- a/clang-tidy/add_new_check.py +++ b/clang-tidy/add_new_check.py @@ -56,8 +56,8 @@ def write_header(module_path, module, check_name, check_name_camel): + check_name_camel.upper() + '_H') f.write('//===--- ') f.write(os.path.basename(filename)) - f.write(' - clang-tidy') - f.write('-' * max(0, 43 - len(os.path.basename(filename)))) + f.write(' - clang-tidy ') + f.write('-' * max(0, 42 - len(os.path.basename(filename)))) f.write('*- C++ -*-===//') f.write(""" // @@ -107,8 +107,8 @@ def write_implementation(module_path, module, check_name_camel): with open(filename, 'w') as f: f.write('//===--- ') f.write(os.path.basename(filename)) - f.write(' - clang-tidy') - f.write('-' * max(0, 52 - len(os.path.basename(filename)))) + f.write(' - clang-tidy ') + f.write('-' * max(0, 51 - len(os.path.basename(filename)))) f.write('-===//') f.write(""" // diff --git a/clang-tidy/android/AndroidTidyModule.cpp b/clang-tidy/android/AndroidTidyModule.cpp index 64d35c701..bbb4b715b 100644 --- a/clang-tidy/android/AndroidTidyModule.cpp +++ b/clang-tidy/android/AndroidTidyModule.cpp @@ -37,16 +37,16 @@ class AndroidModule : public ClangTidyModule { CheckFactories.registerCheck("android-cloexec-accept4"); CheckFactories.registerCheck("android-cloexec-accept"); CheckFactories.registerCheck("android-cloexec-creat"); + CheckFactories.registerCheck("android-cloexec-dup"); CheckFactories.registerCheck( "android-cloexec-epoll-create1"); CheckFactories.registerCheck( "android-cloexec-epoll-create"); - CheckFactories.registerCheck("android-cloexec-dup"); CheckFactories.registerCheck("android-cloexec-fopen"); - CheckFactories.registerCheck( - "android-cloexec-inotify-init"); CheckFactories.registerCheck( "android-cloexec-inotify-init1"); + CheckFactories.registerCheck( + "android-cloexec-inotify-init"); CheckFactories.registerCheck( "android-cloexec-memfd-create"); CheckFactories.registerCheck("android-cloexec-open"); diff --git a/clang-tidy/android/CMakeLists.txt b/clang-tidy/android/CMakeLists.txt index 37aebacf2..9733e2290 100644 --- a/clang-tidy/android/CMakeLists.txt +++ b/clang-tidy/android/CMakeLists.txt @@ -6,9 +6,9 @@ add_clang_library(clangTidyAndroidModule CloexecAcceptCheck.cpp CloexecCheck.cpp CloexecCreatCheck.cpp + CloexecDupCheck.cpp CloexecEpollCreate1Check.cpp CloexecEpollCreateCheck.cpp - CloexecDupCheck.cpp CloexecFopenCheck.cpp CloexecInotifyInit1Check.cpp CloexecInotifyInitCheck.cpp diff --git a/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tidy/cppcoreguidelines/CMakeLists.txt index ceec7aac8..26dbb01ff 100644 --- a/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ b/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -17,8 +17,8 @@ add_clang_library(clangTidyCppCoreGuidelinesModule ProTypeStaticCastDowncastCheck.cpp ProTypeUnionAccessCheck.cpp ProTypeVarargCheck.cpp - SpecialMemberFunctionsCheck.cpp SlicingCheck.cpp + SpecialMemberFunctionsCheck.cpp LINK_LIBS clangAST diff --git a/clang-tidy/hicpp/CMakeLists.txt b/clang-tidy/hicpp/CMakeLists.txt index eeccf621a..3e8dd28c4 100644 --- a/clang-tidy/hicpp/CMakeLists.txt +++ b/clang-tidy/hicpp/CMakeLists.txt @@ -2,9 +2,9 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyHICPPModule ExceptionBaseclassCheck.cpp + HICPPTidyModule.cpp MultiwayPathsCoveredCheck.cpp NoAssemblerCheck.cpp - HICPPTidyModule.cpp SignedBitwiseCheck.cpp LINK_LIBS diff --git a/clang-tidy/misc/CMakeLists.txt b/clang-tidy/misc/CMakeLists.txt index 3806398e6..08d5ceb9b 100644 --- a/clang-tidy/misc/CMakeLists.txt +++ b/clang-tidy/misc/CMakeLists.txt @@ -1,15 +1,15 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyMiscModule - MisplacedConstCheck.cpp - UnconventionalAssignOperatorCheck.cpp DefinitionsInHeadersCheck.cpp MiscTidyModule.cpp + MisplacedConstCheck.cpp NewDeleteOverloadsCheck.cpp NonCopyableObjects.cpp RedundantExpressionCheck.cpp StaticAssertCheck.cpp ThrowByValueCatchByReferenceCheck.cpp + UnconventionalAssignOperatorCheck.cpp UniqueptrResetReleaseCheck.cpp UnusedAliasDeclsCheck.cpp UnusedParametersCheck.cpp diff --git a/clang-tidy/misc/MiscTidyModule.cpp b/clang-tidy/misc/MiscTidyModule.cpp index 241689a2b..ebf38941d 100644 --- a/clang-tidy/misc/MiscTidyModule.cpp +++ b/clang-tidy/misc/MiscTidyModule.cpp @@ -30,11 +30,9 @@ namespace misc { class MiscModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { - CheckFactories.registerCheck("misc-misplaced-const"); - CheckFactories.registerCheck( - "misc-unconventional-assign-operator"); CheckFactories.registerCheck( "misc-definitions-in-headers"); + CheckFactories.registerCheck("misc-misplaced-const"); CheckFactories.registerCheck( "misc-new-delete-overloads"); CheckFactories.registerCheck( @@ -44,6 +42,8 @@ class MiscModule : public ClangTidyModule { CheckFactories.registerCheck("misc-static-assert"); CheckFactories.registerCheck( "misc-throw-by-value-catch-by-reference"); + CheckFactories.registerCheck( + "misc-unconventional-assign-operator"); CheckFactories.registerCheck( "misc-uniqueptr-reset-release"); CheckFactories.registerCheck( diff --git a/clang-tidy/modernize/CMakeLists.txt b/clang-tidy/modernize/CMakeLists.txt index f02b2ae9d..907eb6c30 100644 --- a/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tidy/modernize/CMakeLists.txt @@ -5,8 +5,8 @@ add_clang_library(clangTidyModernizeModule DeprecatedHeadersCheck.cpp LoopConvertCheck.cpp LoopConvertUtils.cpp - MakeSmartPtrCheck.cpp MakeSharedCheck.cpp + MakeSmartPtrCheck.cpp MakeUniqueCheck.cpp ModernizeTidyModule.cpp PassByValueCheck.cpp diff --git a/clang-tidy/readability/CMakeLists.txt b/clang-tidy/readability/CMakeLists.txt index e7f56c558..da2320ea0 100644 --- a/clang-tidy/readability/CMakeLists.txt +++ b/clang-tidy/readability/CMakeLists.txt @@ -22,8 +22,8 @@ add_clang_library(clangTidyReadabilityModule RedundantDeclarationCheck.cpp RedundantFunctionPtrDereferenceCheck.cpp RedundantMemberInitCheck.cpp - RedundantStringCStrCheck.cpp RedundantSmartptrGetCheck.cpp + RedundantStringCStrCheck.cpp RedundantStringInitCheck.cpp SimplifyBooleanExprCheck.cpp SimplifySubscriptExprCheck.cpp From 71581471a637c19d8742f61292db642abf7887c3 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Fri, 21 Sep 2018 13:04:57 +0000 Subject: [PATCH 229/686] [clangd] Remember to serialize symbol origin in YAML. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342730 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/SymbolYAML.cpp | 15 +++++++++++++++ unittests/clangd/SerializationTests.cpp | 3 +++ 2 files changed, 18 insertions(+) diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/SymbolYAML.cpp index 4dec54782..6254187fd 100644 --- a/clangd/index/SymbolYAML.cpp +++ b/clangd/index/SymbolYAML.cpp @@ -27,6 +27,7 @@ namespace yaml { using clang::clangd::Symbol; using clang::clangd::SymbolID; +using clang::clangd::SymbolOrigin; using clang::clangd::SymbolLocation; using clang::index::SymbolInfo; using clang::index::SymbolKind; @@ -65,6 +66,17 @@ struct NormalizedSymbolFlag { uint8_t Flag = 0; }; +struct NormalizedSymbolOrigin { + NormalizedSymbolOrigin(IO &) {} + NormalizedSymbolOrigin(IO &, SymbolOrigin O) { + Origin = static_cast(O); + } + + SymbolOrigin denormalize(IO &) { return static_cast(Origin); } + + uint8_t Origin = 0; +}; + template <> struct MappingTraits { static void mapping(IO &IO, SymbolLocation::Position &Value) { IO.mapRequired("Line", Value.Line); @@ -102,6 +114,8 @@ template <> struct MappingTraits { MappingNormalization NSymbolID(IO, Sym.ID); MappingNormalization NSymbolFlag( IO, Sym.Flags); + MappingNormalization NSymbolOrigin( + IO, Sym.Origin); IO.mapRequired("ID", NSymbolID->HexString); IO.mapRequired("Name", Sym.Name); IO.mapRequired("Scope", Sym.Scope); @@ -110,6 +124,7 @@ template <> struct MappingTraits { SymbolLocation()); IO.mapOptional("Definition", Sym.Definition, SymbolLocation()); IO.mapOptional("References", Sym.References, 0u); + IO.mapOptional("Origin", NSymbolOrigin->Origin); IO.mapOptional("Flags", NSymbolFlag->Flag); IO.mapOptional("Signature", Sym.Signature); IO.mapOptional("CompletionSnippetSuffix", Sym.CompletionSnippetSuffix); diff --git a/unittests/clangd/SerializationTests.cpp b/unittests/clangd/SerializationTests.cpp index 4a50addec..0999c87f2 100644 --- a/unittests/clangd/SerializationTests.cpp +++ b/unittests/clangd/SerializationTests.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "index/Index.h" #include "index/Serialization.h" #include "index/SymbolYAML.h" #include "llvm/Support/ScopedPrinter.h" @@ -35,6 +36,7 @@ Scope: 'clang::' End: Line: 1 Column: 1 +Origin: 4 Flags: 1 Documentation: 'Foo doc' ReturnType: 'int' @@ -82,6 +84,7 @@ TEST(SerializationTest, YAMLConversions) { EXPECT_EQ(Sym1.Documentation, "Foo doc"); EXPECT_EQ(Sym1.ReturnType, "int"); EXPECT_EQ(Sym1.CanonicalDeclaration.FileURI, "file:///path/foo.h"); + EXPECT_EQ(Sym1.Origin, SymbolOrigin::Static); EXPECT_TRUE(Sym1.Flags & Symbol::IndexedForCodeCompletion); EXPECT_FALSE(Sym1.Flags & Symbol::Deprecated); EXPECT_THAT(Sym1.IncludeHeaders, From 98415a0fe5ad3accfcf3dc6278b6165387d8f868 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Mon, 24 Sep 2018 08:45:18 +0000 Subject: [PATCH 230/686] [clangd] Force Dex to respect symbol collector flags `Dex` should utilize `FuzzyFindRequest.RestrictForCodeCompletion` flags and omit symbols not meant for code completion when asked for it. The measurements below were conducted with setting `FuzzyFindRequest.RestrictForCodeCompletion` to `true` (so that it's more realistic). Sadly, the average latency goes down, I suspect that is mostly because of the empty queries where the number of posting lists is critical. | Metrics | Before | After | Relative difference | ----- | ----- | ----- | ----- | Cumulative query latency (7000 `FuzzyFindRequest`s over LLVM static index) | 6182735043 ns | 7202442053 ns | +16% | Whole Index size | 81.24 MB | 81.79 MB | +0.6% Out of 292252 symbols collected from LLVM codebase 136926 appear to be restricted for code completion. Reviewers: ioeric Differential Revision: https://reviews.llvm.org/D52357 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342866 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Dex.cpp | 10 ++++++++++ unittests/clangd/DexTests.cpp | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 3a80f7327..d3d7471d7 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -22,6 +22,10 @@ namespace dex { namespace { +// Mark symbols which are can be used for code completion. +static const Token RestrictedForCodeCompletion = + Token(Token::Kind::Sentinel, "Restricted For Code Completion"); + // Returns the tokens which are given symbol's characteristics. Currently, the // generated tokens only contain fuzzy matching trigrams and symbol's scope, // but in the future this will also return path proximity tokens and other @@ -39,6 +43,8 @@ std::vector generateSearchTokens(const Symbol &Sym) { for (const auto &ProximityURI : generateProximityURIs(Sym.CanonicalDeclaration.FileURI)) Result.emplace_back(Token::Kind::ProximityURI, ProximityURI); + if (Sym.Flags & Symbol::IndexedForCodeCompletion) + Result.emplace_back(RestrictedForCodeCompletion); return Result; } @@ -175,6 +181,10 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, TopLevelChildren.push_back(createOr(move(BoostingIterators))); } + if (Req.RestrictForCodeCompletion) + TopLevelChildren.push_back( + InvertedIndex.find(RestrictedForCodeCompletion)->second.iterator()); + // Use TRUE iterator if both trigrams and scopes from the query are not // present in the symbol index. auto QueryIterator = TopLevelChildren.empty() diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 2deec6411..f0cb783be 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -583,6 +583,20 @@ TEST(DexTest, Lookup) { EXPECT_THAT(lookup(*I, SymbolID("ns::nonono")), UnorderedElementsAre()); } +TEST(DexTest, SymbolIndexOptionsFilter) { + auto CodeCompletionSymbol = symbol("Completion"); + auto NonCodeCompletionSymbol = symbol("NoCompletion"); + CodeCompletionSymbol.Flags = Symbol::SymbolFlag::IndexedForCodeCompletion; + NonCodeCompletionSymbol.Flags = Symbol::SymbolFlag::None; + std::vector Symbols{CodeCompletionSymbol, NonCodeCompletionSymbol}; + Dex I(Symbols, URISchemes); + FuzzyFindRequest Req; + Req.RestrictForCodeCompletion = false; + EXPECT_THAT(match(I, Req), ElementsAre("Completion", "NoCompletion")); + Req.RestrictForCodeCompletion = true; + EXPECT_THAT(match(I, Req), ElementsAre("Completion")); +} + TEST(DexTest, ProximityPathsBoosting) { auto RootSymbol = symbol("root::abc"); RootSymbol.CanonicalDeclaration.FileURI = "unittest:///file.h"; From 866a0fff5331e5b075da207a697165a4f2c7c5d1 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Mon, 24 Sep 2018 14:51:15 +0000 Subject: [PATCH 231/686] [clangd] Do bounds checks while reading data, otherwise var-length records are too painful. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342888 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Serialization.cpp | 277 ++++++++++++++++----------------- 1 file changed, 138 insertions(+), 139 deletions(-) diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index e1bf3226a..919d4fc50 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -23,24 +23,83 @@ Error makeError(const Twine &Msg) { // IO PRIMITIVES // We use little-endian 32 bit ints, sometimes with variable-length encoding. +// +// Variable-length int encoding (varint) uses the bottom 7 bits of each byte +// to encode the number, and the top bit to indicate whether more bytes follow. +// e.g. 9a 2f means [0x1a and keep reading, 0x2f and stop]. +// This represents 0x1a | 0x2f<<7 = 6042. +// A 32-bit integer takes 1-5 bytes to encode; small numbers are more compact. -StringRef consume(StringRef &Data, int N) { - StringRef Ret = Data.take_front(N); - Data = Data.drop_front(N); - return Ret; -} +// Reads binary data from a StringRef, and keeps track of position. +class Reader { + const char *Begin, *End; + bool Err; -uint8_t consume8(StringRef &Data) { - uint8_t Ret = Data.front(); - Data = Data.drop_front(); - return Ret; -} +public: + Reader(StringRef Data) : Begin(Data.begin()), End(Data.end()) {} + // The "error" bit is set by reading past EOF or reading invalid data. + // When in an error state, reads may return zero values: callers should check. + bool err() const { return Err; } + // Did we read all the data, or encounter an error? + bool eof() const { return Begin == End || Err; } + // All the data we didn't read yet. + StringRef rest() const { return StringRef(Begin, End - Begin); } + + uint8_t consume8() { + if (LLVM_UNLIKELY(Begin == End)) { + Err = true; + return 0; + } + return *Begin++; + } -uint32_t consume32(StringRef &Data) { - auto Ret = support::endian::read32le(Data.bytes_begin()); - Data = Data.drop_front(4); - return Ret; -} + uint32_t consume32() { + if (LLVM_UNLIKELY(Begin + 4 > End)) { + Err = true; + return 0; + } + auto Ret = support::endian::read32le(Begin); + Begin += 4; + return Ret; + } + + StringRef consume(int N) { + if (LLVM_UNLIKELY(Begin + N > End)) { + Err = true; + return StringRef(); + } + StringRef Ret(Begin, N); + Begin += N; + return Ret; + } + + uint32_t consumeVar() { + constexpr static uint8_t More = 1 << 7; + uint8_t B = consume8(); + if (LLVM_LIKELY(!(B & More))) + return B; + uint32_t Val = B & ~More; + for (int Shift = 7; B & More && Shift < 32; Shift += 7) { + B = consume8(); + Val |= (B & ~More) << Shift; + } + return Val; + } + + StringRef consumeString(ArrayRef Strings) { + auto StringIndex = consumeVar(); + if (LLVM_UNLIKELY(StringIndex >= Strings.size())) { + Err = true; + return StringRef(); + } + return Strings[StringIndex]; + } + + SymbolID consumeID() { + StringRef Raw = consume(SymbolID::RawSize); // short if truncated. + return LLVM_UNLIKELY(err()) ? SymbolID() : SymbolID::fromRaw(Raw); + } +}; void write32(uint32_t I, raw_ostream &OS) { char buf[4]; @@ -48,11 +107,6 @@ void write32(uint32_t I, raw_ostream &OS) { OS.write(buf, sizeof(buf)); } -// Variable-length int encoding (varint) uses the bottom 7 bits of each byte -// to encode the number, and the top bit to indicate whether more bytes follow. -// e.g. 9a 2f means [0x1a and keep reading, 0x2f and stop]. -// This represents 0x1a | 0x2f<<7 = 6042. -// A 32-bit integer takes 1-5 bytes to encode; small numbers are more compact. void writeVar(uint32_t I, raw_ostream &OS) { constexpr static uint8_t More = 1 << 7; if (LLVM_LIKELY(I < 1 << 7)) { @@ -69,19 +123,6 @@ void writeVar(uint32_t I, raw_ostream &OS) { } } -uint32_t consumeVar(StringRef &Data) { - constexpr static uint8_t More = 1 << 7; - uint8_t B = consume8(Data); - if (LLVM_LIKELY(!(B & More))) - return B; - uint32_t Val = B & ~More; - for (int Shift = 7; B & More && Shift < 32; Shift += 7) { - B = consume8(Data); - Val |= (B & ~More) << Shift; - } - return Val; -} - // STRING TABLE ENCODING // Index data has many string fields, and many strings are identical. // We store each string once, and refer to them by index. @@ -146,30 +187,34 @@ struct StringTableIn { }; Expected readStringTable(StringRef Data) { - if (Data.size() < 4) - return makeError("Bad string table: not enough metadata"); - size_t UncompressedSize = consume32(Data); + Reader R(Data); + size_t UncompressedSize = R.consume32(); + if (R.err()) + return makeError("Truncated string table"); StringRef Uncompressed; SmallString<1> UncompressedStorage; if (UncompressedSize == 0) // No compression - Uncompressed = Data; + Uncompressed = R.rest(); else { - if (Error E = - llvm::zlib::uncompress(Data, UncompressedStorage, UncompressedSize)) + if (Error E = llvm::zlib::uncompress(R.rest(), UncompressedStorage, + UncompressedSize)) return std::move(E); Uncompressed = UncompressedStorage; } StringTableIn Table; StringSaver Saver(Table.Arena); - for (StringRef Rest = Uncompressed; !Rest.empty();) { - auto Len = Rest.find(0); + R = Reader(Uncompressed); + for (Reader R(Uncompressed); !R.eof();) { + auto Len = R.rest().find(0); if (Len == StringRef::npos) return makeError("Bad string table: not null terminated"); - Table.Strings.push_back(Saver.save(consume(Rest, Len))); - Rest = Rest.drop_front(); + Table.Strings.push_back(Saver.save(R.consume(Len))); + R.consume8(); } + if (R.err()) + return makeError("Truncated string table"); return std::move(Table); } @@ -179,27 +224,35 @@ Expected readStringTable(StringRef Data) { // - enums encode as the underlying type // - most numbers encode as varint -// It's useful to the implementation to assume symbols have a bounded size. -constexpr size_t SymbolSizeBound = 512; -// To ensure the bounded size, restrict the number of include headers stored. -constexpr unsigned MaxIncludes = 50; +void writeLocation(const SymbolLocation &Loc, const StringTableOut &Strings, + raw_ostream &OS) { + writeVar(Strings.index(Loc.FileURI), OS); + for (const auto &Endpoint : {Loc.Start, Loc.End}) { + writeVar(Endpoint.Line, OS); + writeVar(Endpoint.Column, OS); + } +} + +SymbolLocation readLocation(Reader &Data, ArrayRef Strings) { + SymbolLocation Loc; + Loc.FileURI = Data.consumeString(Strings); + for (auto *Endpoint : {&Loc.Start, &Loc.End}) { + Endpoint->Line = Data.consumeVar(); + Endpoint->Column = Data.consumeVar(); + } + return Loc; +} void writeSymbol(const Symbol &Sym, const StringTableOut &Strings, raw_ostream &OS) { - auto StartOffset = OS.tell(); OS << Sym.ID.raw(); // TODO: once we start writing xrefs and posting lists, // symbol IDs should probably be in a string table. OS.write(static_cast(Sym.SymInfo.Kind)); OS.write(static_cast(Sym.SymInfo.Lang)); writeVar(Strings.index(Sym.Name), OS); writeVar(Strings.index(Sym.Scope), OS); - for (const auto &Loc : {Sym.Definition, Sym.CanonicalDeclaration}) { - writeVar(Strings.index(Loc.FileURI), OS); - for (const auto &Endpoint : {Loc.Start, Loc.End}) { - writeVar(Endpoint.Line, OS); - writeVar(Endpoint.Column, OS); - } - } + writeLocation(Sym.Definition, Strings, OS); + writeLocation(Sym.CanonicalDeclaration, Strings, OS); writeVar(Sym.References, OS); OS.write(static_cast(Sym.Flags)); OS.write(static_cast(Sym.Origin)); @@ -212,86 +265,33 @@ void writeSymbol(const Symbol &Sym, const StringTableOut &Strings, writeVar(Strings.index(Include.IncludeHeader), OS); writeVar(Include.References, OS); }; - // There are almost certainly few includes, so we can just write them. - if (LLVM_LIKELY(Sym.IncludeHeaders.size() <= MaxIncludes)) { - writeVar(Sym.IncludeHeaders.size(), OS); - for (const auto &Include : Sym.IncludeHeaders) - WriteInclude(Include); - } else { - // If there are too many, make sure we truncate the least important. - using Pointer = const Symbol::IncludeHeaderWithReferences *; - std::vector Pointers; - for (const auto &Include : Sym.IncludeHeaders) - Pointers.push_back(&Include); - std::sort(Pointers.begin(), Pointers.end(), [](Pointer L, Pointer R) { - return L->References > R->References; - }); - Pointers.resize(MaxIncludes); - - writeVar(MaxIncludes, OS); - for (Pointer P : Pointers) - WriteInclude(*P); - } - - assert(OS.tell() - StartOffset < SymbolSizeBound && "Symbol length unsafe!"); - (void)StartOffset; // Unused in NDEBUG; + writeVar(Sym.IncludeHeaders.size(), OS); + for (const auto &Include : Sym.IncludeHeaders) + WriteInclude(Include); } -Expected readSymbol(StringRef &Data, const StringTableIn &Strings) { - // Usually we can skip bounds checks because the buffer is huge. - // Near the end of the buffer, this would be unsafe. In this rare case, copy - // the data into a bigger buffer so we can again skip the checks. - if (LLVM_UNLIKELY(Data.size() < SymbolSizeBound)) { - std::string Buf(Data); - Buf.resize(SymbolSizeBound); - StringRef ExtendedData = Buf; - auto Ret = readSymbol(ExtendedData, Strings); - unsigned BytesRead = Buf.size() - ExtendedData.size(); - if (BytesRead > Data.size()) - return makeError("read past end of data"); - Data = Data.drop_front(BytesRead); - return Ret; - } - -#define READ_STRING(Field) \ - do { \ - auto StringIndex = consumeVar(Data); \ - if (LLVM_UNLIKELY(StringIndex >= Strings.Strings.size())) \ - return makeError("Bad string index"); \ - Field = Strings.Strings[StringIndex]; \ - } while (0) - +Symbol readSymbol(Reader &Data, ArrayRef Strings) { Symbol Sym; - Sym.ID = SymbolID::fromRaw(consume(Data, 20)); - Sym.SymInfo.Kind = static_cast(consume8(Data)); - Sym.SymInfo.Lang = static_cast(consume8(Data)); - READ_STRING(Sym.Name); - READ_STRING(Sym.Scope); - for (SymbolLocation *Loc : {&Sym.Definition, &Sym.CanonicalDeclaration}) { - READ_STRING(Loc->FileURI); - for (auto &Endpoint : {&Loc->Start, &Loc->End}) { - Endpoint->Line = consumeVar(Data); - Endpoint->Column = consumeVar(Data); - } - } - Sym.References = consumeVar(Data); - Sym.Flags = static_cast(consume8(Data)); - Sym.Origin = static_cast(consume8(Data)); - READ_STRING(Sym.Signature); - READ_STRING(Sym.CompletionSnippetSuffix); - READ_STRING(Sym.Documentation); - READ_STRING(Sym.ReturnType); - unsigned IncludeHeaderN = consumeVar(Data); - if (IncludeHeaderN > MaxIncludes) - return makeError("too many IncludeHeaders"); - Sym.IncludeHeaders.resize(IncludeHeaderN); + Sym.ID = Data.consumeID(); + Sym.SymInfo.Kind = static_cast(Data.consume8()); + Sym.SymInfo.Lang = static_cast(Data.consume8()); + Sym.Name = Data.consumeString(Strings); + Sym.Scope = Data.consumeString(Strings); + Sym.Definition = readLocation(Data, Strings); + Sym.CanonicalDeclaration = readLocation(Data, Strings); + Sym.References = Data.consumeVar(); + Sym.Flags = static_cast(Data.consumeVar()); + Sym.Origin = static_cast(Data.consumeVar()); + Sym.Signature = Data.consumeString(Strings); + Sym.CompletionSnippetSuffix = Data.consumeString(Strings); + Sym.Documentation = Data.consumeString(Strings); + Sym.ReturnType = Data.consumeString(Strings); + Sym.IncludeHeaders.resize(Data.consumeVar()); for (auto &I : Sym.IncludeHeaders) { - READ_STRING(I.IncludeHeader); - I.References = consumeVar(Data); + I.IncludeHeader = Data.consumeString(Strings); + I.References = Data.consumeVar(); } - -#undef READ_STRING - return std::move(Sym); + return Sym; } } // namespace @@ -306,7 +306,7 @@ Expected readSymbol(StringRef &Data, const StringTableIn &Strings) { // The current versioning scheme is simple - non-current versions are rejected. // If you make a breaking change, bump this version number to invalidate stored // data. Later we may want to support some backward compatibility. -constexpr static uint32_t Version = 3; +constexpr static uint32_t Version = 4; Expected readIndexFile(StringRef Data) { auto RIFF = riff::readFile(Data); @@ -322,8 +322,8 @@ Expected readIndexFile(StringRef Data) { if (!Chunks.count(RequiredChunk)) return makeError("missing required chunk " + RequiredChunk); - StringRef Meta = Chunks.lookup("meta"); - if (Meta.size() < 4 || consume32(Meta) != Version) + Reader Meta(Chunks.lookup("meta")); + if (Meta.consume32() != Version) return makeError("wrong version"); auto Strings = readStringTable(Chunks.lookup("stri")); @@ -332,13 +332,12 @@ Expected readIndexFile(StringRef Data) { IndexFileIn Result; if (Chunks.count("symb")) { - StringRef SymbolData = Chunks.lookup("symb"); + Reader SymbolReader(Chunks.lookup("symb")); SymbolSlab::Builder Symbols; - while (!SymbolData.empty()) - if (auto Sym = readSymbol(SymbolData, *Strings)) - Symbols.insert(*Sym); - else - return Sym.takeError(); + while (!SymbolReader.eof()) + Symbols.insert(readSymbol(SymbolReader, Strings->Strings)); + if (SymbolReader.err()) + return makeError("malformed or truncated symbol"); Result.Symbols = std::move(Symbols).build(); } return std::move(Result); From 1eba77e91480ffb7034572d4ab5d533a59946b9e Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Mon, 24 Sep 2018 16:52:48 +0000 Subject: [PATCH 232/686] [clangd] Fix uninit bool in r342888 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342903 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Serialization.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index 919d4fc50..03be61f84 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -33,7 +33,7 @@ Error makeError(const Twine &Msg) { // Reads binary data from a StringRef, and keeps track of position. class Reader { const char *Begin, *End; - bool Err; + bool Err = false; public: Reader(StringRef Data) : Begin(Data.begin()), End(Data.end()) {} From 9804b7ac2fef8b18f0b7858772742382a79144a7 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 25 Sep 2018 08:24:07 +0000 Subject: [PATCH 233/686] Deduplicate replacements from diagnostics. Summary: After r329813, clang-apply-replacements stopped deduplicating identical replacements; however, tools like clang-tidy relies on the deduplication of identical dignostics replacements from different TUs to apply fixes correctly. This change partially roll back the behavior by deduplicating changes from diagnostics. Ideally, we should deduplicate on diagnostics level, but we need to figure out an effecient way. Reviewers: bkramer Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D52264 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342951 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../lib/Tooling/ApplyReplacements.cpp | 19 +++++++++++++++---- .../Inputs/identical/file1.yaml | 4 ---- .../Inputs/identical/file2.yaml | 14 ++++++++++++++ .../Inputs/identical/identical.cpp | 2 +- test/clang-apply-replacements/identical.cpp | 1 + 5 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 test/clang-apply-replacements/Inputs/identical/file2.yaml diff --git a/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp b/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp index 1970d05c6..b47992251 100644 --- a/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp +++ b/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp @@ -125,7 +125,8 @@ std::error_code collectReplacementsFromDirectory( } /// \brief Extract replacements from collected TranslationUnitReplacements and -/// TranslationUnitDiagnostics and group them per file. +/// TranslationUnitDiagnostics and group them per file. Identical replacements +/// from diagnostics are deduplicated. /// /// \param[in] TUs Collection of all found and deserialized /// TranslationUnitReplacements. @@ -142,10 +143,20 @@ groupReplacements(const TUReplacements &TUs, const TUDiagnostics &TUDs, llvm::DenseMap> GroupedReplacements; - auto AddToGroup = [&](const tooling::Replacement &R) { + // Deduplicate identical replacements in diagnostics. + // FIXME: Find an efficient way to deduplicate on diagnostics level. + llvm::DenseMap> + DiagReplacements; + + auto AddToGroup = [&](const tooling::Replacement &R, bool FromDiag) { // Use the file manager to deduplicate paths. FileEntries are // automatically canonicalized. if (const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath())) { + if (FromDiag) { + auto &Replaces = DiagReplacements[Entry]; + if (!Replaces.insert(R).second) + return; + } GroupedReplacements[Entry].push_back(R); } else if (Warned.insert(R.getFilePath()).second) { errs() << "Described file '" << R.getFilePath() @@ -155,13 +166,13 @@ groupReplacements(const TUReplacements &TUs, const TUDiagnostics &TUDs, for (const auto &TU : TUs) for (const tooling::Replacement &R : TU.Replacements) - AddToGroup(R); + AddToGroup(R, false); for (const auto &TU : TUDs) for (const auto &D : TU.Diagnostics) for (const auto &Fix : D.Fix) for (const tooling::Replacement &R : Fix.second) - AddToGroup(R); + AddToGroup(R, true); // Sort replacements per file to keep consistent behavior when // clang-apply-replacements run on differents machine. diff --git a/test/clang-apply-replacements/Inputs/identical/file1.yaml b/test/clang-apply-replacements/Inputs/identical/file1.yaml index 1ef5651c4..cf273c482 100644 --- a/test/clang-apply-replacements/Inputs/identical/file1.yaml +++ b/test/clang-apply-replacements/Inputs/identical/file1.yaml @@ -10,9 +10,5 @@ Diagnostics: Offset: 12 Length: 0 ReplacementText: '0' - - FilePath: $(path)/identical.cpp - Offset: 12 - Length: 0 - ReplacementText: '0' ... diff --git a/test/clang-apply-replacements/Inputs/identical/file2.yaml b/test/clang-apply-replacements/Inputs/identical/file2.yaml new file mode 100644 index 000000000..cf273c482 --- /dev/null +++ b/test/clang-apply-replacements/Inputs/identical/file2.yaml @@ -0,0 +1,14 @@ +--- +MainSourceFile: identical.cpp +Diagnostics: + - DiagnosticName: test-identical-insertion + Message: Fix + FilePath: $(path)/identical.cpp + FileOffset: 12 + Replacements: + - FilePath: $(path)/identical.cpp + Offset: 12 + Length: 0 + ReplacementText: '0' +... + diff --git a/test/clang-apply-replacements/Inputs/identical/identical.cpp b/test/clang-apply-replacements/Inputs/identical/identical.cpp index bdaab4fc8..bc740fa12 100644 --- a/test/clang-apply-replacements/Inputs/identical/identical.cpp +++ b/test/clang-apply-replacements/Inputs/identical/identical.cpp @@ -1,2 +1,2 @@ class MyType {}; -// CHECK: class MyType00 {}; +// CHECK: class MyType0 {}; diff --git a/test/clang-apply-replacements/identical.cpp b/test/clang-apply-replacements/identical.cpp index b513f3e63..ffbf2e373 100644 --- a/test/clang-apply-replacements/identical.cpp +++ b/test/clang-apply-replacements/identical.cpp @@ -1,5 +1,6 @@ // RUN: mkdir -p %T/Inputs/identical // RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/identical/identical.cpp > %T/Inputs/identical/identical.cpp // RUN: sed "s#\$(path)#%/T/Inputs/identical#" %S/Inputs/identical/file1.yaml > %T/Inputs/identical/file1.yaml +// RUN: sed "s#\$(path)#%/T/Inputs/identical#" %S/Inputs/identical/file2.yaml > %T/Inputs/identical/file2.yaml // RUN: clang-apply-replacements %T/Inputs/identical // RUN: FileCheck -input-file=%T/Inputs/identical/identical.cpp %S/Inputs/identical/identical.cpp From 4ad0857af75423ec52df41525acdbf58d4701f17 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 25 Sep 2018 09:47:01 +0000 Subject: [PATCH 234/686] [clangd] NFC: Remove test duplicate `FuzzyMatchQ` test was a duplicate of `FuzzyMatch` pulled from MemIndex tests. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342957 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/DexTests.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index f0cb783be..b9c1f07b0 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -475,17 +475,6 @@ TEST(Dex, FuzzyFind) { "other::A")); } -TEST(DexTest, FuzzyMatchQ) { - auto I = Dex::build( - generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}), - URISchemes); - FuzzyFindRequest Req; - Req.Query = "lol"; - Req.Limit = 2; - EXPECT_THAT(match(*I, Req), - UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); -} - // FIXME(kbobyrev): This test is different for Dex and MemIndex: while // MemIndex manages response deduplication, Dex simply returns all matched // symbols which means there might be equivalent symbols in the response. From 5a5ef9bcee40bbc03f9eca1064b1fcc9932827db Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Tue, 25 Sep 2018 10:36:57 +0000 Subject: [PATCH 235/686] Fix a typo in the help of clangd git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342960 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/tool/ClangdMain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index b6839431c..64a931265 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -184,7 +184,7 @@ int main(int argc, char *argv[]) { llvm::cl::ParseCommandLineOptions( argc, argv, "clangd is a language server that provides IDE-like features to editors. " - "\n\nIt should be used via an editor plugin rather than invoked directly." + "\n\nIt should be used via an editor plugin rather than invoked directly. " "For more information, see:" "\n\thttps://clang.llvm.org/extra/clangd.html" "\n\thttps://microsoft.github.io/language-server-protocol/"); From 42e82af35220477995c156c3ba3adcadaa828648 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 25 Sep 2018 10:47:46 +0000 Subject: [PATCH 236/686] [clangd] Check that scheme is valid when parsing URI. Reviewers: sammccall Reviewed By: sammccall Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52455 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342961 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/URI.cpp | 22 ++++++++++++++++++++-- unittests/clangd/URITests.cpp | 7 ++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/clangd/URI.cpp b/clangd/URI.cpp index d5fd481dd..8c297a324 100644 --- a/clangd/URI.cpp +++ b/clangd/URI.cpp @@ -11,8 +11,11 @@ #include "llvm/ADT/Twine.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" +#include #include +#include #include LLVM_INSTANTIATE_REGISTRY(clang::clangd::URISchemeRegistry) @@ -128,6 +131,17 @@ std::string percentDecode(llvm::StringRef Content) { return Result; } +bool isValidScheme(llvm::StringRef Scheme) { + if (Scheme.empty()) + return false; + if (!std::isalpha(Scheme[0])) + return false; + return std::all_of(Scheme.begin() + 1, Scheme.end(), [](char C) { + return std::isalpha(C) || std::isdigit(C) || C == '+' || C == '.' || + C == '-'; + }); +} + } // namespace URI::URI(llvm::StringRef Scheme, llvm::StringRef Authority, @@ -158,9 +172,13 @@ llvm::Expected URI::parse(llvm::StringRef OrigUri) { llvm::StringRef Uri = OrigUri; auto Pos = Uri.find(':'); - if (Pos == 0 || Pos == llvm::StringRef::npos) + if (Pos == llvm::StringRef::npos) return make_string_error("Scheme must be provided in URI: " + OrigUri); - U.Scheme = percentDecode(Uri.substr(0, Pos)); + auto SchemeStr = Uri.substr(0, Pos); + U.Scheme = percentDecode(SchemeStr); + if (!isValidScheme(U.Scheme)) + return make_string_error(llvm::formatv("Invalid scheme: {0} (decoded: {1})", + SchemeStr, U.Scheme)); Uri = Uri.substr(Pos + 1); if (Uri.consume_front("//")) { Pos = Uri.find('/'); diff --git a/unittests/clangd/URITests.cpp b/unittests/clangd/URITests.cpp index 84d5ca109..fab144086 100644 --- a/unittests/clangd/URITests.cpp +++ b/unittests/clangd/URITests.cpp @@ -51,9 +51,9 @@ TEST(PercentEncodingTest, Encode) { TEST(PercentEncodingTest, Decode) { EXPECT_EQ(parseOrDie("x:a/b/c").body(), "a/b/c"); - EXPECT_EQ(parseOrDie("%3a://%3a/%3").scheme(), ":"); - EXPECT_EQ(parseOrDie("%3a://%3a/%3").authority(), ":"); - EXPECT_EQ(parseOrDie("%3a://%3a/%3").body(), "/%3"); + EXPECT_EQ(parseOrDie("s%2b://%3a/%3").scheme(), "s+"); + EXPECT_EQ(parseOrDie("s%2b://%3a/%3").authority(), ":"); + EXPECT_EQ(parseOrDie("s%2b://%3a/%3").body(), "/%3"); EXPECT_EQ(parseOrDie("x:a%21b%3ac~").body(), "a!b:c~"); } @@ -132,6 +132,7 @@ TEST(URITest, ParseFailed) { // Empty. EXPECT_TRUE(FailedParse("")); EXPECT_TRUE(FailedParse(":/a/b/c")); + EXPECT_TRUE(FailedParse("\"/a/b/c\" IWYU pragma: abc")); } TEST(URITest, Resolve) { From 550f2efb391ae3be92850eb7a7c5181fe28916ba Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 25 Sep 2018 11:47:14 +0000 Subject: [PATCH 237/686] [clangd] Fix build bot after r342961 Use llvm::isAlpha instead of std::isalpha etc. This should fix bot failure: http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast/builds/20180 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342964 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/URI.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/clangd/URI.cpp b/clangd/URI.cpp index 8c297a324..0eab8f5f6 100644 --- a/clangd/URI.cpp +++ b/clangd/URI.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "URI.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" @@ -15,7 +16,6 @@ #include "llvm/Support/Path.h" #include #include -#include #include LLVM_INSTANTIATE_REGISTRY(clang::clangd::URISchemeRegistry) @@ -134,11 +134,10 @@ std::string percentDecode(llvm::StringRef Content) { bool isValidScheme(llvm::StringRef Scheme) { if (Scheme.empty()) return false; - if (!std::isalpha(Scheme[0])) + if (!llvm::isAlpha(Scheme[0])) return false; return std::all_of(Scheme.begin() + 1, Scheme.end(), [](char C) { - return std::isalpha(C) || std::isdigit(C) || C == '+' || C == '.' || - C == '-'; + return llvm::isAlnum(C) || C == '+' || C == '.' || C == '-'; }); } From b544531125a7bafdf724753addf1225b17598432 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 25 Sep 2018 11:54:51 +0000 Subject: [PATCH 238/686] [clangd] Implement VByte PostingList compression This patch implements Variable-length Byte compression of `PostingList`s to sacrifice some performance for lower memory consumption. `PostingList` compression and decompression was extensively tested using fuzzer for multiple hours and runnning significant number of realistic `FuzzyFindRequests`. AddressSanitizer and UndefinedBehaviorSanitizer were used to ensure the correct behaviour. Performance evaluation was conducted with recent LLVM symbol index (292k symbols) and the collection of user-recorded queries (7751 `FuzzyFindRequest` JSON dumps): | Metrics | Before| After | Change (%) | ----- | ----- | ----- | ----- | Memory consumption (posting lists only), MB | 54.4 | 23.5 | -60% | Time to process queries, sec | 7.70 | 9.4 | +25% Reviewers: sammccall, ioeric Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D52300 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342965 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Dex.cpp | 4 +- clangd/index/dex/PostingList.cpp | 188 +++++++++++++++++++++++++++---- clangd/index/dex/PostingList.h | 56 ++++++--- unittests/clangd/DexTests.cpp | 67 +++-------- 4 files changed, 228 insertions(+), 87 deletions(-) diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index d3d7471d7..a29e69595 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -128,8 +128,8 @@ void Dex::buildIndex() { // Convert lists of items to posting lists. for (const auto &TokenToPostingList : TempInvertedIndex) - InvertedIndex.insert({TokenToPostingList.first, - PostingList(move(TokenToPostingList.second))}); + InvertedIndex.insert( + {TokenToPostingList.first, PostingList(TokenToPostingList.second)}); vlog("Built Dex with estimated memory usage {0} bytes.", estimateMemoryUsage()); diff --git a/clangd/index/dex/PostingList.cpp b/clangd/index/dex/PostingList.cpp index 99e534d1a..e41a53d6a 100644 --- a/clangd/index/dex/PostingList.cpp +++ b/clangd/index/dex/PostingList.cpp @@ -9,6 +9,8 @@ #include "PostingList.h" #include "Iterator.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MathExtras.h" namespace clang { namespace clangd { @@ -16,21 +18,27 @@ namespace dex { namespace { -/// Implements Iterator over std::vector. This is the most basic -/// iterator and is simply a wrapper around -/// std::vector::const_iterator. -class PlainIterator : public Iterator { +/// Implements iterator of PostingList chunks. This requires iterating over two +/// levels: the first level iterator iterates over the chunks and decompresses +/// them on-the-fly when the contents of chunk are to be seen. +class ChunkIterator : public Iterator { public: - explicit PlainIterator(llvm::ArrayRef Documents) - : Documents(Documents), Index(std::begin(Documents)) {} + explicit ChunkIterator(llvm::ArrayRef Chunks) + : Chunks(Chunks), CurrentChunk(Chunks.begin()) { + if (!Chunks.empty()) { + DecompressedChunk = CurrentChunk->decompress(); + CurrentID = DecompressedChunk.begin(); + } + } - bool reachedEnd() const override { return Index == std::end(Documents); } + bool reachedEnd() const override { return CurrentChunk == Chunks.end(); } /// Advances cursor to the next item. void advance() override { assert(!reachedEnd() && "Posting List iterator can't advance() at the end."); - ++Index; + ++CurrentID; + normalizeCursor(); } /// Applies binary search to advance cursor to the next item with DocID @@ -38,16 +46,17 @@ class PlainIterator : public Iterator { void advanceTo(DocID ID) override { assert(!reachedEnd() && "Posting List iterator can't advance() at the end."); - // If current ID is beyond requested one, iterator is already in the right - // state. - if (peek() < ID) - Index = std::lower_bound(Index, std::end(Documents), ID); + if (ID <= peek()) + return; + advanceToChunk(ID); + // Try to find ID within current chunk. + CurrentID = std::lower_bound(CurrentID, std::end(DecompressedChunk), ID); + normalizeCursor(); } DocID peek() const override { - assert(!reachedEnd() && - "Posting List iterator can't peek() at the end."); - return *Index; + assert(!reachedEnd() && "Posting List iterator can't peek() at the end."); + return *CurrentID; } float consume() override { @@ -56,27 +65,160 @@ class PlainIterator : public Iterator { return DEFAULT_BOOST_SCORE; } - size_t estimateSize() const override { return Documents.size(); } + size_t estimateSize() const override { + return Chunks.size() * ApproxEntriesPerChunk; + } private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { OS << '['; - if (Index != std::end(Documents)) - OS << *Index; - else - OS << "END"; + if (CurrentChunk != Chunks.begin() || + (CurrentID != DecompressedChunk.begin() && !DecompressedChunk.empty())) + OS << "... "; + OS << (reachedEnd() ? "END" : std::to_string(*CurrentID)); + if (!reachedEnd() && CurrentID < DecompressedChunk.end() - 1) + OS << " ..."; OS << ']'; return OS; } - llvm::ArrayRef Documents; - llvm::ArrayRef::const_iterator Index; + /// If the cursor is at the end of a chunk, place it at the start of the next + /// chunk. + void normalizeCursor() { + // Invariant is already established if examined chunk is not exhausted. + if (CurrentID != std::end(DecompressedChunk)) + return; + // Advance to next chunk if current one is exhausted. + ++CurrentChunk; + if (CurrentChunk == Chunks.end()) // Reached the end of PostingList. + return; + DecompressedChunk = CurrentChunk->decompress(); + CurrentID = DecompressedChunk.begin(); + } + + /// Advances CurrentChunk to the chunk which might contain ID. + void advanceToChunk(DocID ID) { + if ((CurrentChunk != Chunks.end() - 1) && + ((CurrentChunk + 1)->Head <= ID)) { + // Find the next chunk with Head >= ID. + CurrentChunk = std::lower_bound( + CurrentChunk + 1, Chunks.end(), ID, + [](const Chunk &C, const DocID ID) { return C.Head <= ID; }); + --CurrentChunk; + DecompressedChunk = CurrentChunk->decompress(); + CurrentID = DecompressedChunk.begin(); + } + } + + llvm::ArrayRef Chunks; + /// Iterator over chunks. + /// If CurrentChunk is valid, then DecompressedChunk is + /// CurrentChunk->decompress() and CurrentID is a valid (non-end) iterator + /// into it. + decltype(Chunks)::const_iterator CurrentChunk; + llvm::SmallVector DecompressedChunk; + /// Iterator over DecompressedChunk. + decltype(DecompressedChunk)::iterator CurrentID; + + static constexpr size_t ApproxEntriesPerChunk = 15; }; +static constexpr size_t BitsPerEncodingByte = 7; + +/// Writes a variable length DocID into the buffer and updates the buffer size. +/// If it doesn't fit, returns false and doesn't write to the buffer. +bool encodeVByte(DocID Delta, llvm::MutableArrayRef &Payload) { + assert(Delta != 0 && "0 is not a valid PostingList delta."); + // Calculate number of bytes Delta encoding would take by examining the + // meaningful bits. + unsigned Width = 1 + llvm::findLastSet(Delta) / BitsPerEncodingByte; + if (Width > Payload.size()) + return false; + + do { + uint8_t Encoding = Delta & 0x7f; + Delta >>= 7; + Payload.front() = Delta ? Encoding | 0x80 : Encoding; + Payload = Payload.drop_front(); + } while (Delta != 0); + return true; +} + +/// Use Variable-length Byte (VByte) delta encoding to compress sorted list of +/// DocIDs. The compression stores deltas (differences) between subsequent +/// DocIDs and encodes these deltas utilizing the least possible number of +/// bytes. +/// +/// Each encoding byte consists of two parts: the first bit (continuation bit) +/// indicates whether this is the last byte (0 if this byte is the last) of +/// current encoding and seven bytes a piece of DocID (payload). DocID contains +/// 32 bits and therefore it takes up to 5 bytes to encode it (4 full 7-bit +/// payloads and one 4-bit payload), but in practice it is expected that gaps +/// (deltas) between subsequent DocIDs are not large enough to require 5 bytes. +/// In very dense posting lists (with average gaps less than 128) this +/// representation would be 4 times more efficient than raw DocID array. +/// +/// PostingList encoding example: +/// +/// DocIDs 42 47 7000 +/// gaps 5 6958 +/// Encoding (raw number) 00000101 10110110 00101110 +std::vector encodeStream(llvm::ArrayRef Documents) { + assert(!Documents.empty() && "Can't encode empty sequence."); + std::vector Result; + Result.emplace_back(); + DocID Last = Result.back().Head = Documents.front(); + llvm::MutableArrayRef RemainingPayload = Result.back().Payload; + for (DocID Doc : Documents.drop_front()) { + if (!encodeVByte(Doc - Last, RemainingPayload)) { // didn't fit, flush chunk + Result.emplace_back(); + Result.back().Head = Doc; + RemainingPayload = Result.back().Payload; + } + Last = Doc; + } + return std::vector(Result); // no move, shrink-to-fit +} + +/// Reads variable length DocID from the buffer and updates the buffer size. If +/// the stream is terminated, return None. +llvm::Optional readVByte(llvm::ArrayRef &Bytes) { + if (Bytes.front() == 0 || Bytes.empty()) + return llvm::None; + DocID Result = 0; + bool HasNextByte = true; + for (size_t Length = 0; HasNextByte && !Bytes.empty(); ++Length) { + assert(Length <= 5 && "Malformed VByte encoding sequence."); + // Write meaningful bits to the correct place in the document decoding. + Result |= (Bytes.front() & 0x7f) << (BitsPerEncodingByte * Length); + if ((Bytes.front() & 0x80) == 0) + HasNextByte = false; + Bytes = Bytes.drop_front(); + } + return Result; +} + } // namespace +llvm::SmallVector Chunk::decompress() const { + llvm::SmallVector Result{Head}; + llvm::ArrayRef Bytes(Payload); + DocID Delta; + for (DocID Current = Head; !Bytes.empty(); Current += Delta) { + auto MaybeDelta = readVByte(Bytes); + if (!MaybeDelta) + break; + Delta = *MaybeDelta; + Result.push_back(Current + Delta); + } + return llvm::SmallVector{Result}; +} + +PostingList::PostingList(llvm::ArrayRef Documents) + : Chunks(encodeStream(Documents)) {} + std::unique_ptr PostingList::iterator() const { - return llvm::make_unique(Documents); + return llvm::make_unique(Chunks); } } // namespace dex diff --git a/clangd/index/dex/PostingList.h b/clangd/index/dex/PostingList.h index 63762e4f7..be0835940 100644 --- a/clangd/index/dex/PostingList.h +++ b/clangd/index/dex/PostingList.h @@ -6,13 +6,19 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// -// This defines posting list interface: a storage for identifiers of symbols -// which can be characterized by a specific feature (such as fuzzy-find trigram, -// scope, type or any other Search Token). Posting lists can be traversed in -// order using an iterator and are values for inverted index, which maps search -// tokens to corresponding posting lists. -// +/// +/// \file +/// This defines posting list interface: a storage for identifiers of symbols +/// which can be characterized by a specific feature (such as fuzzy-find +/// trigram, scope, type or any other Search Token). Posting lists can be +/// traversed in order using an iterator and are values for inverted index, +/// which maps search tokens to corresponding posting lists. +/// +/// In order to decrease size of Index in-memory representation, Variable Byte +/// Encoding (VByte) is used for PostingLists compression. An overview of VByte +/// algorithm can be found in "Introduction to Information Retrieval" book: +/// https://nlp.stanford.edu/IR-book/html/htmledition/variable-byte-codes-1.html +/// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_POSTINGLIST_H @@ -20,6 +26,7 @@ #include "Iterator.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" #include #include @@ -29,20 +36,43 @@ namespace dex { class Iterator; +/// NOTE: This is an implementation detail. +/// +/// Chunk is a fixed-width piece of PostingList which contains the first DocID +/// in uncompressed format (Head) and delta-encoded Payload. It can be +/// decompressed upon request. +struct Chunk { + /// Keep sizeof(Chunk) == 32. + static constexpr size_t PayloadSize = 32 - sizeof(DocID); + + llvm::SmallVector decompress() const; + + /// The first element of decompressed Chunk. + DocID Head; + /// VByte-encoded deltas. + std::array Payload = std::array(); +}; +static_assert(sizeof(Chunk) == 32, "Chunk should take 32 bytes of memory."); + /// PostingList is the storage of DocIDs which can be inserted to the Query -/// Tree as a leaf by constructing Iterator over the PostingList object. -// FIXME(kbobyrev): Use VByte algorithm to compress underlying data. +/// Tree as a leaf by constructing Iterator over the PostingList object. DocIDs +/// are stored in underlying chunks. Compression saves memory at a small cost +/// in access time, which is still fast enough in practice. class PostingList { public: - explicit PostingList(const std::vector &&Documents) - : Documents(std::move(Documents)) {} + explicit PostingList(llvm::ArrayRef Documents); + /// Constructs DocumentIterator over given posting list. DocumentIterator will + /// go through the chunks and decompress them on-the-fly when necessary. std::unique_ptr iterator() const; - size_t bytes() const { return Documents.size() * sizeof(DocID); } + /// Returns in-memory size. + size_t bytes() const { + return sizeof(Chunk) + Chunks.capacity() * sizeof(Chunk); + } private: - const std::vector Documents; + const std::vector Chunks; }; } // namespace dex diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index b9c1f07b0..d9db3087f 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -69,19 +69,6 @@ TEST(DexIterators, DocumentIterator) { EXPECT_TRUE(DocIterator->reachedEnd()); } -TEST(DexIterators, AndWithEmpty) { - const PostingList L0({}); - const PostingList L1({0, 5, 7, 10, 42, 320, 9000}); - - auto AndEmpty = createAnd(L0.iterator()); - EXPECT_TRUE(AndEmpty->reachedEnd()); - - auto AndWithEmpty = createAnd(L0.iterator(), L1.iterator()); - EXPECT_TRUE(AndWithEmpty->reachedEnd()); - - EXPECT_THAT(consumeIDs(*AndWithEmpty), ElementsAre()); -} - TEST(DexIterators, AndTwoLists) { const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); @@ -120,20 +107,6 @@ TEST(DexIterators, AndThreeLists) { EXPECT_TRUE(And->reachedEnd()); } -TEST(DexIterators, OrWithEmpty) { - const PostingList L0({}); - const PostingList L1({0, 5, 7, 10, 42, 320, 9000}); - - auto OrEmpty = createOr(L0.iterator()); - EXPECT_TRUE(OrEmpty->reachedEnd()); - - auto OrWithEmpty = createOr(L0.iterator(), L1.iterator()); - EXPECT_FALSE(OrWithEmpty->reachedEnd()); - - EXPECT_THAT(consumeIDs(*OrWithEmpty), - ElementsAre(0U, 5U, 7U, 10U, 42U, 320U, 9000U)); -} - TEST(DexIterators, OrTwoLists) { const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); @@ -211,29 +184,27 @@ TEST(DexIterators, QueryTree) { // |And Iterator: 1, 5, 9| |Or Iterator: 0, 1, 3, 5| // +----------+----------+ +----------+------------+ // | | - // +------+-----+ +---------------------+ - // | | | | | - // +-------v-----+ +----+---+ +--v--+ +---v----+ +----v---+ - // |1, 3, 5, 8, 9| |Boost: 2| |Empty| |Boost: 3| |Boost: 4| - // +-------------+ +----+---+ +-----+ +---+----+ +----+---+ - // | | | - // +----v-----+ +-v--+ +---v---+ - // |1, 5, 7, 9| |1, 5| |0, 3, 5| - // +----------+ +----+ +-------+ + // +------+-----+ ------------+ + // | | | | + // +-------v-----+ +----+---+ +---v----+ +----v---+ + // |1, 3, 5, 8, 9| |Boost: 2| |Boost: 3| |Boost: 4| + // +-------------+ +----+---+ +---+----+ +----+---+ + // | | | + // +----v-----+ +-v--+ +---v---+ + // |1, 5, 7, 9| |1, 5| |0, 3, 5| + // +----------+ +----+ +-------+ // const PostingList L0({1, 3, 5, 8, 9}); const PostingList L1({1, 5, 7, 9}); - const PostingList L3({}); - const PostingList L4({1, 5}); - const PostingList L5({0, 3, 5}); + const PostingList L2({1, 5}); + const PostingList L3({0, 3, 5}); // Root of the query tree: [1, 5] auto Root = createAnd( // Lower And Iterator: [1, 5, 9] createAnd(L0.iterator(), createBoost(L1.iterator(), 2U)), // Lower Or Iterator: [0, 1, 5] - createOr(L3.iterator(), createBoost(L4.iterator(), 3U), - createBoost(L5.iterator(), 4U))); + createOr(createBoost(L2.iterator(), 3U), createBoost(L3.iterator(), 4U))); EXPECT_FALSE(Root->reachedEnd()); EXPECT_EQ(Root->peek(), 1U); @@ -260,15 +231,13 @@ TEST(DexIterators, StringRepresentation) { const PostingList L2({1, 5, 7, 9}); const PostingList L3({0, 5}); const PostingList L4({0, 1, 5}); - const PostingList L5({}); - - EXPECT_EQ(llvm::to_string(*(L0.iterator())), "[4]"); - - auto Nested = - createAnd(createAnd(L1.iterator(), L2.iterator()), - createOr(L3.iterator(), L4.iterator(), L5.iterator())); - EXPECT_EQ(llvm::to_string(*Nested), "(& (| [5] [1] [END]) (& [1] [1]))"); + EXPECT_EQ(llvm::to_string(*(L0.iterator())), "[4 ...]"); + auto It = L0.iterator(); + It->advanceTo(19); + EXPECT_EQ(llvm::to_string(*It), "[... 20 ...]"); + It->advanceTo(9000); + EXPECT_EQ(llvm::to_string(*It), "[... END]"); } TEST(DexIterators, Limit) { From c25aa9c05dc56c48654322a00d5c06c53bcdb233 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 25 Sep 2018 13:14:11 +0000 Subject: [PATCH 239/686] [clangd] Fix some buildbots after r342965 Some compilers fail to parse struct default member initializer. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342970 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/PostingList.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/index/dex/PostingList.h b/clangd/index/dex/PostingList.h index be0835940..010fb1b03 100644 --- a/clangd/index/dex/PostingList.h +++ b/clangd/index/dex/PostingList.h @@ -50,7 +50,7 @@ struct Chunk { /// The first element of decompressed Chunk. DocID Head; /// VByte-encoded deltas. - std::array Payload = std::array(); + std::array Payload; }; static_assert(sizeof(Chunk) == 32, "Chunk should take 32 bytes of memory."); From 0eddbe908913429e4f69b4a877f9c0ca28e08382 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 25 Sep 2018 13:58:48 +0000 Subject: [PATCH 240/686] [clangd] NFC: Simplify code, enforce LLVM Coding Standards For consistency, functional-style code pieces are replaced with their simple counterparts to improve readability. Also, file headers are fixed to comply with LLVM Coding Standards. `static` member of anonymous namespace is not marked `static` anymore, because it is redundant. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D52466 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342974 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Dex.cpp | 2 +- clangd/index/dex/Dex.h | 19 +++++++++-------- clangd/index/dex/Iterator.cpp | 38 +++++++++++++++------------------- clangd/index/dex/Iterator.h | 39 ++++++++++++++++++----------------- clangd/index/dex/Token.h | 23 +++++++++++---------- clangd/index/dex/Trigram.h | 25 +++++++++++----------- 6 files changed, 73 insertions(+), 73 deletions(-) diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index a29e69595..29742227b 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -23,7 +23,7 @@ namespace dex { namespace { // Mark symbols which are can be used for code completion. -static const Token RestrictedForCodeCompletion = +const Token RestrictedForCodeCompletion = Token(Token::Kind::Sentinel, "Restricted For Code Completion"); // Returns the tokens which are given symbol's characteristics. Currently, the diff --git a/clangd/index/dex/Dex.h b/clangd/index/dex/Dex.h index 79b771a86..4b2d85658 100644 --- a/clangd/index/dex/Dex.h +++ b/clangd/index/dex/Dex.h @@ -6,15 +6,16 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// -// This defines Dex - a symbol index implementation based on query iterators -// over symbol tokens, such as fuzzy matching trigrams, scopes, types, etc. -// While consuming more memory and having longer build stage due to -// preprocessing, Dex will have substantially lower latency. It will also allow -// efficient symbol searching which is crucial for operations like code -// completion, and can be very important for a number of different code -// transformations which will be eventually supported by Clangd. -// +/// +/// \file +/// This defines Dex - a symbol index implementation based on query iterators +/// over symbol tokens, such as fuzzy matching trigrams, scopes, types, etc. +/// While consuming more memory and having longer build stage due to +/// preprocessing, Dex will have substantially lower latency. It will also allow +/// efficient symbol searching which is crucial for operations like code +/// completion, and can be very important for a number of different code +/// transformations which will be eventually supported by Clangd. +/// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_DEX_DEX_H diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index 06d778dd5..7009d9b20 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -64,11 +64,10 @@ class AndIterator : public Iterator { float consume() override { assert(!reachedEnd() && "AND iterator can't consume() at the end."); - return std::accumulate( - begin(Children), end(Children), DEFAULT_BOOST_SCORE, - [&](float Current, const std::unique_ptr &Child) { - return Current * Child->consume(); - }); + float Boost = DEFAULT_BOOST_SCORE; + for (const auto &Child : Children) + Boost *= Child->consume(); + return Boost; } size_t estimateSize() const override { @@ -140,10 +139,10 @@ class OrIterator : public Iterator { /// Returns true if all children are exhausted. bool reachedEnd() const override { - return std::all_of(begin(Children), end(Children), - [](const std::unique_ptr &Child) { - return Child->reachedEnd(); - }); + for (const auto &Child : Children) + if (!Child->reachedEnd()) + return false; + return true; } /// Moves each child pointing to the smallest DocID to the next item. @@ -181,21 +180,18 @@ class OrIterator : public Iterator { float consume() override { assert(!reachedEnd() && "OR iterator can't consume() at the end."); const DocID ID = peek(); - return std::accumulate( - begin(Children), end(Children), DEFAULT_BOOST_SCORE, - [&](float Boost, const std::unique_ptr &Child) { - return (!Child->reachedEnd() && Child->peek() == ID) - ? std::max(Boost, Child->consume()) - : Boost; - }); + float Boost = DEFAULT_BOOST_SCORE; + for (const auto &Child : Children) + if (!Child->reachedEnd() && Child->peek() == ID) + Boost = std::max(Boost, Child->consume()); + return Boost; } size_t estimateSize() const override { - return std::accumulate( - begin(Children), end(Children), Children.front()->estimateSize(), - [&](size_t Current, const std::unique_ptr &Child) { - return std::max(Current, Child->estimateSize()); - }); + size_t Size = 0; + for (const auto &Child : Children) + Size = std::max(Size, Child->estimateSize()); + return Size; } private: diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 9b002b3c1..5e5460f01 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -6,25 +6,26 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// -// Symbol index queries consist of specific requirements for the requested -// symbol, such as high fuzzy matching score, scope, type etc. The lists of all -// symbols matching some criteria (e.g. belonging to "clang::clangd::" scope) -// are expressed in a form of Search Tokens which are stored in the inverted -// index. Inverted index maps these tokens to the posting lists - sorted (by -// symbol quality) sequences of symbol IDs matching the token, e.g. scope token -// "clangd::clangd::" is mapped to the list of IDs of all symbols which are -// declared in this namespace. Search queries are build from a set of -// requirements which can be combined with each other forming the query trees. -// The leafs of such trees are posting lists, and the nodes are operations on -// these posting lists, e.g. intersection or union. Efficient processing of -// these multi-level queries is handled by Iterators. Iterators advance through -// all leaf posting lists producing the result of search query, which preserves -// the sorted order of IDs. Having the resulting IDs sorted is important, -// because it allows receiving a certain number of the most valuable items (e.g. -// symbols with highest quality which was the sorting key in the first place) -// without processing all items with requested properties (this might not be -// computationally effective if search request is not very restrictive). +/// +/// \file +/// Symbol index queries consist of specific requirements for the requested +/// symbol, such as high fuzzy matching score, scope, type etc. The lists of all +/// symbols matching some criteria (e.g. belonging to "clang::clangd::" scope) +/// are expressed in a form of Search Tokens which are stored in the inverted +/// index. Inverted index maps these tokens to the posting lists - sorted (by +/// symbol quality) sequences of symbol IDs matching the token, e.g. scope token +/// "clangd::clangd::" is mapped to the list of IDs of all symbols which are +/// declared in this namespace. Search queries are build from a set of +/// requirements which can be combined with each other forming the query trees. +/// The leafs of such trees are posting lists, and the nodes are operations on +/// these posting lists, e.g. intersection or union. Efficient processing of +/// these multi-level queries is handled by Iterators. Iterators advance through +/// all leaf posting lists producing the result of search query, which preserves +/// the sorted order of IDs. Having the resulting IDs sorted is important, +/// because it allows receiving a certain number of the most valuable items +/// (e.g. symbols with highest quality which was the sorting key in the first +/// place) without processing all items with requested properties (this might +/// not be computationally effective if search request is not very restrictive). // //===----------------------------------------------------------------------===// diff --git a/clangd/index/dex/Token.h b/clangd/index/dex/Token.h index 8cafcccdd..bae27130a 100644 --- a/clangd/index/dex/Token.h +++ b/clangd/index/dex/Token.h @@ -6,17 +6,18 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// -// Token objects represent a characteristic of a symbol, which can be used to -// perform efficient search. Tokens are keys for inverted index which are mapped -// to the corresponding posting lists. -// -// The symbol std::cout might have the tokens: -// * Scope "std::" -// * Trigram "cou" -// * Trigram "out" -// * Type "std::ostream" -// +/// +/// \file +/// Token objects represent a characteristic of a symbol, which can be used to +/// perform efficient search. Tokens are keys for inverted index which are +/// mapped to the corresponding posting lists. +/// +/// The symbol std::cout might have the tokens: +/// * Scope "std::" +/// * Trigram "cou" +/// * Trigram "out" +/// * Type "std::ostream" +/// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_DEX_TOKEN_H diff --git a/clangd/index/dex/Trigram.h b/clangd/index/dex/Trigram.h index 665b0dae7..a01c1063d 100644 --- a/clangd/index/dex/Trigram.h +++ b/clangd/index/dex/Trigram.h @@ -6,18 +6,19 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// -// Trigrams are attributes of the symbol unqualified name used to effectively -// extract symbols which can be fuzzy-matched given user query from the inverted -// index. To match query with the extracted set of trigrams Q, the set of -// generated trigrams T for identifier (unqualified symbol name) should contain -// all items of Q, i.e. Q ⊆ T. -// -// Trigram sets extracted from unqualified name and from query are different: -// the set of query trigrams only contains consecutive sequences of three -// characters (which is only a subset of all trigrams generated for an -// identifier). -// +/// +/// \file +/// Trigrams are attributes of the symbol unqualified name used to effectively +/// extract symbols which can be fuzzy-matched given user query from the +/// inverted index. To match query with the extracted set of trigrams Q, the set +/// of generated trigrams T for identifier (unqualified symbol name) should +/// contain all items of Q, i.e. Q ⊆ T. +/// +/// Trigram sets extracted from unqualified name and from query are different: +/// the set of query trigrams only contains consecutive sequences of three +/// characters (which is only a subset of all trigrams generated for an +/// identifier). +/// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_DEX_TRIGRAM_H From 6609f826c93062248befc76bc95eeddc84fb8e1b Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 25 Sep 2018 18:06:43 +0000 Subject: [PATCH 241/686] [clangd] Merge binary + YAML serialization behind a (mostly) common interface. Summary: Interface is in one file, implementation in two as they have little in common. A couple of ad-hoc YAML functions left exposed: - symbol -> YAML I expect to keep for tools like dexp - YAML -> symbol is used for the MR-style indexer, I think we can eliminate this (merge-on-the-fly, else use a different serialization) Reviewers: kbobyrev Subscribers: mgorny, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52453 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@342999 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 2 +- clangd/benchmarks/IndexBenchmark.cpp | 2 +- clangd/index/Serialization.cpp | 67 ++++++++++++-- clangd/index/Serialization.h | 37 ++++++-- clangd/index/SymbolYAML.h | 54 ----------- .../{SymbolYAML.cpp => YAMLSerialization.cpp} | 89 +++++++------------ clangd/index/dex/dexp/Dexp.cpp | 4 +- clangd/indexer/IndexerMain.cpp | 33 +++---- clangd/tool/ClangdMain.cpp | 2 +- unittests/clangd/SerializationTests.cpp | 58 ++++++------ unittests/clangd/SymbolCollectorTests.cpp | 1 - 11 files changed, 174 insertions(+), 175 deletions(-) delete mode 100644 clangd/index/SymbolYAML.h rename clangd/index/{SymbolYAML.cpp => YAMLSerialization.cpp} (76%) diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index 08af301ae..a8cd64a10 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -44,7 +44,7 @@ add_clang_library(clangDaemon index/Merge.cpp index/Serialization.cpp index/SymbolCollector.cpp - index/SymbolYAML.cpp + index/YAMLSerialization.cpp index/dex/Dex.cpp index/dex/Iterator.cpp diff --git a/clangd/benchmarks/IndexBenchmark.cpp b/clangd/benchmarks/IndexBenchmark.cpp index dc868f8d2..980b4de6f 100644 --- a/clangd/benchmarks/IndexBenchmark.cpp +++ b/clangd/benchmarks/IndexBenchmark.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "../index/SymbolYAML.h" +#include "../index/Serialization.h" #include "../index/dex/Dex.h" #include "benchmark/benchmark.h" #include "llvm/ADT/SmallVector.h" diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index 03be61f84..116e974f4 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -9,6 +9,8 @@ #include "Serialization.h" #include "Index.h" #include "RIFF.h" +#include "Trace.h" +#include "dex/Dex.h" #include "llvm/Support/Compression.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" @@ -294,8 +296,6 @@ Symbol readSymbol(Reader &Data, ArrayRef Strings) { return Sym; } -} // namespace - // FILE ENCODING // A file is a RIFF chunk with type 'CdIx'. // It contains the sections: @@ -308,7 +308,7 @@ Symbol readSymbol(Reader &Data, ArrayRef Strings) { // data. Later we may want to support some backward compatibility. constexpr static uint32_t Version = 4; -Expected readIndexFile(StringRef Data) { +Expected readRIFF(StringRef Data) { auto RIFF = riff::readFile(Data); if (!RIFF) return RIFF.takeError(); @@ -343,7 +343,7 @@ Expected readIndexFile(StringRef Data) { return std::move(Result); } -raw_ostream &operator<<(raw_ostream &OS, const IndexFileOut &Data) { +void writeRIFF(const IndexFileOut &Data, raw_ostream &OS) { assert(Data.Symbols && "An index file without symbols makes no sense!"); riff::File RIFF; RIFF.Type = riff::fourCC("CdIx"); @@ -377,7 +377,64 @@ raw_ostream &operator<<(raw_ostream &OS, const IndexFileOut &Data) { } RIFF.Chunks.push_back({riff::fourCC("symb"), SymbolSection}); - return OS << RIFF; + OS << RIFF; +} + +} // namespace + +// Defined in YAMLSerialization.cpp. +void writeYAML(const IndexFileOut &, raw_ostream &); +Expected readYAML(StringRef); + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IndexFileOut &O) { + switch (O.Format) { + case IndexFileFormat::RIFF: + writeYAML(O, OS); + break; + case IndexFileFormat::YAML: + writeRIFF(O, OS); + break; + } + return OS; +} + +Expected readIndexFile(StringRef Data) { + if (Data.startswith("RIFF")) { + return readRIFF(Data); + } else if (auto YAMLContents = readYAML(Data)) { + return std::move(*YAMLContents); + } else { + return makeError("Not a RIFF file and failed to parse as YAML: " + + llvm::toString(YAMLContents.takeError())); + } +} + +std::unique_ptr loadIndex(llvm::StringRef SymbolFilename, + llvm::ArrayRef URISchemes, + bool UseDex) { + trace::Span OverallTracer("LoadIndex"); + auto Buffer = MemoryBuffer::getFile(SymbolFilename); + if (!Buffer) { + llvm::errs() << "Can't open " << SymbolFilename << "\n"; + return nullptr; + } + + SymbolSlab Symbols; + RefSlab Refs; + { + trace::Span Tracer("ParseIndex"); + if (auto I = readIndexFile(Buffer->get()->getBuffer())) { + if (I->Symbols) + Symbols = std::move(*I->Symbols); + } else { + llvm::errs() << "Bad Index: " << llvm::toString(I.takeError()) << "\n"; + return nullptr; + } + } + + trace::Span Tracer("BuildIndex"); + return UseDex ? dex::Dex::build(std::move(Symbols), URISchemes) + : MemIndex::build(std::move(Symbols), std::move(Refs)); } } // namespace clangd diff --git a/clangd/index/Serialization.h b/clangd/index/Serialization.h index 7ad867f94..c73ce23b5 100644 --- a/clangd/index/Serialization.h +++ b/clangd/index/Serialization.h @@ -7,14 +7,18 @@ // //===----------------------------------------------------------------------===// // -// This file provides a compact binary serialization of indexed symbols. +// This file provides serialization of indexed symbols and other data. // -// It writes two sections: +// It writes sections: +// - metadata such as version info // - a string table (which is compressed) // - lists of encoded symbols // -// The format has a simple versioning scheme: the version is embedded in the -// data and non-current versions are rejected when reading. +// The format has a simple versioning scheme: the format version number is +// written in the file and non-current versions are rejected when reading. +// +// Human-readable YAML serialization is also supported, and recommended for +// debugging and experiments only. // //===----------------------------------------------------------------------===// @@ -23,25 +27,48 @@ #include "Index.h" #include "llvm/Support/Error.h" +namespace llvm { +namespace yaml { +class Input; +} +} // namespace llvm namespace clang { namespace clangd { +enum class IndexFileFormat { + RIFF, // Versioned binary format, suitable for production use. + YAML, // Human-readable format, suitable for experiments and debugging. +}; + // Specifies the contents of an index file to be written. struct IndexFileOut { const SymbolSlab *Symbols; // TODO: Support serializing symbol occurrences. // TODO: Support serializing Dex posting lists. + IndexFileFormat Format = IndexFileFormat::RIFF; }; // Serializes an index file. (This is a RIFF container chunk). -llvm::raw_ostream &operator<<(llvm::raw_ostream &, const IndexFileOut &); +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IndexFileOut &O); // Holds the contents of an index file that was read. struct IndexFileIn { llvm::Optional Symbols; + IndexFileFormat Format; }; // Parse an index file. The input must be a RIFF container chunk. llvm::Expected readIndexFile(llvm::StringRef); +std::string toYAML(const Symbol &); +// Returned symbol is backed by the YAML input. +// FIXME: this is only needed for IndexerMain, find a better solution. +llvm::Expected symbolFromYAML(llvm::yaml::Input &); + +// Build an in-memory static index from an index file. +// The size should be relatively small, so data can be managed in memory. +std::unique_ptr loadIndex(llvm::StringRef Filename, + llvm::ArrayRef URISchemes, + bool UseDex = true); + } // namespace clangd } // namespace clang diff --git a/clangd/index/SymbolYAML.h b/clangd/index/SymbolYAML.h deleted file mode 100644 index 7da16f128..000000000 --- a/clangd/index/SymbolYAML.h +++ /dev/null @@ -1,54 +0,0 @@ -//===--- SymbolYAML.h --------------------------------------------*- C++-*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// SymbolYAML provides facilities to convert Symbol to YAML, and vice versa. -// The YAML format of Symbol is designed for simplicity and experiment, but -// isn't a suitable/efficient store. -// -// This is for **experimental** only. Don't use it in the production code. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOL_FROM_YAML_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOL_FROM_YAML_H - -#include "Index.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/YAMLTraits.h" -#include "llvm/Support/raw_ostream.h" - -namespace clang { -namespace clangd { - -// Read symbols from a YAML-format string. -SymbolSlab symbolsFromYAML(llvm::StringRef YAMLContent); - -// Read one symbol from a YAML-stream. -// The returned symbol is backed by Input. -Symbol SymbolFromYAML(llvm::yaml::Input &Input); - -// Convert a single symbol to YAML-format string. -// The YAML result is safe to concatenate. -std::string SymbolToYAML(Symbol Sym); - -// Convert symbols to a YAML-format string. -// The YAML result is safe to concatenate if you have multiple symbol slabs. -void SymbolsToYAML(const SymbolSlab &Symbols, llvm::raw_ostream &OS); - -// Build an in-memory static index for global symbols from a symbol file. -// The size of global symbols should be relatively small, so that all symbols -// can be managed in memory. -std::unique_ptr loadIndex(llvm::StringRef SymbolFilename, - llvm::ArrayRef URISchemes, - bool UseDex = true); - -} // namespace clangd -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOL_FROM_YAML_H diff --git a/clangd/index/SymbolYAML.cpp b/clangd/index/YAMLSerialization.cpp similarity index 76% rename from clangd/index/SymbolYAML.cpp rename to clangd/index/YAMLSerialization.cpp index 6254187fd..cf3e2a69e 100644 --- a/clangd/index/SymbolYAML.cpp +++ b/clangd/index/YAMLSerialization.cpp @@ -7,7 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "SymbolYAML.h" #include "Index.h" #include "Serialization.h" #include "Trace.h" @@ -16,10 +15,10 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Errc.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include -LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(clang::clangd::Symbol) LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Symbol::IncludeHeaderWithReferences) namespace llvm { @@ -27,8 +26,8 @@ namespace yaml { using clang::clangd::Symbol; using clang::clangd::SymbolID; -using clang::clangd::SymbolOrigin; using clang::clangd::SymbolLocation; +using clang::clangd::SymbolOrigin; using clang::index::SymbolInfo; using clang::index::SymbolKind; using clang::index::SymbolLanguage; @@ -186,65 +185,45 @@ template <> struct ScalarEnumerationTraits { namespace clang { namespace clangd { -SymbolSlab symbolsFromYAML(llvm::StringRef YAMLContent) { - llvm::yaml::Input Yin(YAMLContent); - std::vector S; - Yin >> S; - - SymbolSlab::Builder Syms; - for (auto &Sym : S) - Syms.insert(Sym); - return std::move(Syms).build(); -} - -Symbol SymbolFromYAML(llvm::yaml::Input &Input) { - Symbol S; - Input >> S; - return S; -} - -void SymbolsToYAML(const SymbolSlab &Symbols, llvm::raw_ostream &OS) { +void writeYAML(const IndexFileOut &O, raw_ostream &OS) { llvm::yaml::Output Yout(OS); - for (Symbol S : Symbols) // copy: Yout<< requires mutability. - Yout << S; + for (Symbol Sym : *O.Symbols) // copy: Yout<< requires mutability. + Yout << Sym; } -std::string SymbolToYAML(Symbol Sym) { - std::string Str; - llvm::raw_string_ostream OS(Str); - llvm::yaml::Output Yout(OS); - Yout << Sym; - return OS.str(); +Expected readYAML(StringRef Data) { + SymbolSlab::Builder Symbols; + llvm::yaml::Input Yin(Data); + do { + Symbol S; + Yin >> S; + if (Yin.error()) + return llvm::errorCodeToError(Yin.error()); + Symbols.insert(S); + } while (Yin.nextDocument()); + + IndexFileIn Result; + Result.Symbols.emplace(std::move(Symbols).build()); + return std::move(Result); } -std::unique_ptr loadIndex(llvm::StringRef SymbolFilename, - llvm::ArrayRef URISchemes, - bool UseDex) { - trace::Span OverallTracer("LoadIndex"); - auto Buffer = llvm::MemoryBuffer::getFile(SymbolFilename); - if (!Buffer) { - llvm::errs() << "Can't open " << SymbolFilename << "\n"; - return nullptr; - } - StringRef Data = Buffer->get()->getBuffer(); - - llvm::Optional Slab; - if (Data.startswith("RIFF")) { // Magic for binary index file. - trace::Span Tracer("ParseRIFF"); - if (auto RIFF = readIndexFile(Data)) - Slab = std::move(RIFF->Symbols); - else - llvm::errs() << "Bad RIFF: " << llvm::toString(RIFF.takeError()) << "\n"; - } else { - trace::Span Tracer("ParseYAML"); - Slab = symbolsFromYAML(Data); +std::string toYAML(const Symbol &S) { + std::string Buf; + { + llvm::raw_string_ostream OS(Buf); + llvm::yaml::Output Yout(OS); + Symbol Sym = S; // copy: Yout<< requires mutability. + OS << Sym; } + return Buf; +} - if (!Slab) - return nullptr; - trace::Span Tracer("BuildIndex"); - return UseDex ? dex::Dex::build(std::move(*Slab), URISchemes) - : MemIndex::build(std::move(*Slab), RefSlab()); +Expected symbolFromYAML(llvm::yaml::Input &Yin) { + Symbol S; + Yin >> S; + if (Yin.error()) + return llvm::errorCodeToError(Yin.error()); + return S; } } // namespace clangd diff --git a/clangd/index/dex/dexp/Dexp.cpp b/clangd/index/dex/dexp/Dexp.cpp index afaace3ac..a61e0d005 100644 --- a/clangd/index/dex/dexp/Dexp.cpp +++ b/clangd/index/dex/dexp/Dexp.cpp @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "../../../index/SymbolYAML.h" +#include "../../Serialization.h" #include "../Dex.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -155,7 +155,7 @@ class Lookup : public Command { bool FoundSymbol = false; Index->lookup(Request, [&](const Symbol &Sym) { FoundSymbol = true; - llvm::outs() << SymbolToYAML(Sym); + llvm::outs() << toYAML(Sym); }); if (!FoundSymbol) llvm::outs() << "not found\n"; diff --git a/clangd/indexer/IndexerMain.cpp b/clangd/indexer/IndexerMain.cpp index 24551bf61..dbd43cd19 100644 --- a/clangd/indexer/IndexerMain.cpp +++ b/clangd/indexer/IndexerMain.cpp @@ -18,7 +18,6 @@ #include "index/Merge.h" #include "index/Serialization.h" #include "index/SymbolCollector.h" -#include "index/SymbolYAML.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Index/IndexDataConsumer.h" @@ -60,12 +59,13 @@ static llvm::cl::opt MergeOnTheFly( "MapReduce."), llvm::cl::init(true), llvm::cl::Hidden); -enum IndexFormat { YAML, Binary }; -static llvm::cl::opt Format( - "format", llvm::cl::desc("Format of the index to be written"), - llvm::cl::values(clEnumValN(YAML, "yaml", "human-readable YAML format"), - clEnumValN(Binary, "binary", "binary RIFF format")), - llvm::cl::init(YAML)); +static llvm::cl::opt + Format("format", llvm::cl::desc("Format of the index to be written"), + llvm::cl::values(clEnumValN(IndexFileFormat::YAML, "yaml", + "human-readable YAML format"), + clEnumValN(IndexFileFormat::RIFF, "binary", + "binary RIFF format")), + llvm::cl::init(IndexFileFormat::YAML)); /// Responsible for aggregating symbols from each processed file and producing /// the final results. All methods in this class must be thread-safe, @@ -162,8 +162,7 @@ class ToolExecutorConsumer : public SymbolsConsumer { void consumeSymbols(SymbolSlab Symbols) override { for (const auto &Sym : Symbols) - Executor.getExecutionContext()->reportResult(Sym.ID.str(), - SymbolToYAML(Sym)); + Executor.getExecutionContext()->reportResult(Sym.ID.str(), toYAML(Sym)); } SymbolSlab mergeResults() override { @@ -171,7 +170,7 @@ class ToolExecutorConsumer : public SymbolsConsumer { Executor.getToolResults()->forEachResult( [&](llvm::StringRef Key, llvm::StringRef Value) { llvm::yaml::Input Yin(Value); - auto Sym = clang::clangd::SymbolFromYAML(Yin); + auto Sym = cantFail(clang::clangd::symbolFromYAML(Yin)); auto ID = cantFail(clang::clangd::SymbolID::fromStr(Key)); if (const auto *Existing = UniqueSymbols.find(ID)) UniqueSymbols.insert(mergeSymbol(*Existing, Sym)); @@ -270,15 +269,9 @@ int main(int argc, const char **argv) { // Reduce phase: combine symbols with the same IDs. auto UniqueSymbols = Consumer->mergeResults(); // Output phase: emit result symbols. - switch (clang::clangd::Format) { - case clang::clangd::IndexFormat::YAML: - SymbolsToYAML(UniqueSymbols, llvm::outs()); - break; - case clang::clangd::IndexFormat::Binary: { - clang::clangd::IndexFileOut Out; - Out.Symbols = &UniqueSymbols; - llvm::outs() << Out; - } - } + clang::clangd::IndexFileOut Out; + Out.Symbols = &UniqueSymbols; + Out.Format = clang::clangd::Format; + llvm::outs() << Out; return 0; } diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 64a931265..4c6a4c352 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -12,7 +12,7 @@ #include "Path.h" #include "RIFF.h" #include "Trace.h" -#include "index/SymbolYAML.h" +#include "index/Serialization.h" #include "clang/Basic/Version.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" diff --git a/unittests/clangd/SerializationTests.cpp b/unittests/clangd/SerializationTests.cpp index 0999c87f2..7d4ac3bd5 100644 --- a/unittests/clangd/SerializationTests.cpp +++ b/unittests/clangd/SerializationTests.cpp @@ -9,18 +9,18 @@ #include "index/Index.h" #include "index/Serialization.h" -#include "index/SymbolYAML.h" #include "llvm/Support/ScopedPrinter.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +using testing::AllOf; using testing::UnorderedElementsAre; using testing::UnorderedElementsAreArray; namespace clang { namespace clangd { namespace { -const char *YAML1 = R"( +const char *YAML = R"( --- ID: 057557CEBF6E6B2DD437FBF60CC58F352D1DF856 Name: 'Foo1' @@ -46,9 +46,6 @@ ReturnType: 'int' - Header: 'include2' References: 3 ... -)"; - -const char *YAML2 = R"( --- ID: 057557CEBF6E6B2DD437FBF60CC58F352D1DF858 Name: 'Foo2' @@ -70,15 +67,29 @@ CompletionSnippetSuffix: '-snippet' ... )"; +MATCHER_P(ID, I, "") { return arg.ID == cantFail(SymbolID::fromStr(I)); } MATCHER_P(QName, Name, "") { return (arg.Scope + arg.Name).str() == Name; } MATCHER_P2(IncludeHeaderWithRef, IncludeHeader, References, "") { return (arg.IncludeHeader == IncludeHeader) && (arg.References == References); } TEST(SerializationTest, YAMLConversions) { - auto Symbols1 = symbolsFromYAML(YAML1); - ASSERT_EQ(Symbols1.size(), 1u); - const auto &Sym1 = *Symbols1.begin(); + auto In = readIndexFile(YAML); + EXPECT_TRUE(bool(In)) << In.takeError(); + + auto ParsedYAML = readIndexFile(YAML); + ASSERT_TRUE(bool(ParsedYAML)) << ParsedYAML.takeError(); + ASSERT_TRUE(bool(ParsedYAML->Symbols)); + EXPECT_THAT( + *ParsedYAML->Symbols, + UnorderedElementsAre(ID("057557CEBF6E6B2DD437FBF60CC58F352D1DF856"), + ID("057557CEBF6E6B2DD437FBF60CC58F352D1DF858"))); + + auto Sym1 = *ParsedYAML->Symbols->find( + cantFail(SymbolID::fromStr("057557CEBF6E6B2DD437FBF60CC58F352D1DF856"))); + auto Sym2 = *ParsedYAML->Symbols->find( + cantFail(SymbolID::fromStr("057557CEBF6E6B2DD437FBF60CC58F352D1DF858"))); + EXPECT_THAT(Sym1, QName("clang::Foo1")); EXPECT_EQ(Sym1.Signature, ""); EXPECT_EQ(Sym1.Documentation, "Foo doc"); @@ -91,51 +102,38 @@ TEST(SerializationTest, YAMLConversions) { UnorderedElementsAre(IncludeHeaderWithRef("include1", 7u), IncludeHeaderWithRef("include2", 3u))); - auto Symbols2 = symbolsFromYAML(YAML2); - ASSERT_EQ(Symbols2.size(), 1u); - const auto &Sym2 = *Symbols2.begin(); EXPECT_THAT(Sym2, QName("clang::Foo2")); EXPECT_EQ(Sym2.Signature, "-sig"); EXPECT_EQ(Sym2.ReturnType, ""); EXPECT_EQ(Sym2.CanonicalDeclaration.FileURI, "file:///path/bar.h"); EXPECT_FALSE(Sym2.Flags & Symbol::IndexedForCodeCompletion); EXPECT_TRUE(Sym2.Flags & Symbol::Deprecated); - - std::string ConcatenatedYAML; - { - llvm::raw_string_ostream OS(ConcatenatedYAML); - SymbolsToYAML(Symbols1, OS); - SymbolsToYAML(Symbols2, OS); - } - auto ConcatenatedSymbols = symbolsFromYAML(ConcatenatedYAML); - EXPECT_THAT(ConcatenatedSymbols, - UnorderedElementsAre(QName("clang::Foo1"), QName("clang::Foo2"))); } std::vector YAMLFromSymbols(const SymbolSlab &Slab) { std::vector Result; for (const auto &Sym : Slab) - Result.push_back(SymbolToYAML(Sym)); + Result.push_back(toYAML(Sym)); return Result; } TEST(SerializationTest, BinaryConversions) { - // We reuse the test symbols from YAML. - auto Slab = symbolsFromYAML(std::string(YAML1) + YAML2); - ASSERT_EQ(Slab.size(), 2u); + auto In = readIndexFile(YAML); + EXPECT_TRUE(bool(In)) << In.takeError(); // Write to binary format, and parse again. IndexFileOut Out; - Out.Symbols = &Slab; + Out.Symbols = In->Symbols.getPointer(); + Out.Format = IndexFileFormat::RIFF; std::string Serialized = llvm::to_string(Out); - auto In = readIndexFile(Serialized); - ASSERT_TRUE(bool(In)) << In.takeError(); + auto In2 = readIndexFile(Serialized); + ASSERT_TRUE(bool(In2)) << In.takeError(); ASSERT_TRUE(In->Symbols); // Assert the YAML serializations match, for nice comparisons and diffs. - EXPECT_THAT(YAMLFromSymbols(*In->Symbols), - UnorderedElementsAreArray(YAMLFromSymbols(Slab))); + EXPECT_THAT(YAMLFromSymbols(*In2->Symbols), + UnorderedElementsAreArray(YAMLFromSymbols(*In->Symbols))); } } // namespace diff --git a/unittests/clangd/SymbolCollectorTests.cpp b/unittests/clangd/SymbolCollectorTests.cpp index 4a9eed77c..5b3c58e4c 100644 --- a/unittests/clangd/SymbolCollectorTests.cpp +++ b/unittests/clangd/SymbolCollectorTests.cpp @@ -11,7 +11,6 @@ #include "TestFS.h" #include "TestTU.h" #include "index/SymbolCollector.h" -#include "index/SymbolYAML.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/VirtualFileSystem.h" From 3e33a5a44acb989053e1411433ba0368adda21fb Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Tue, 25 Sep 2018 18:12:28 +0000 Subject: [PATCH 242/686] [clang-tidy] Add modernize-concat-nested-namespaces check Summary: Finds instances of namespaces concatenated using explicit syntax, such as `namespace a { namespace b { [...] }}` and offers fix to glue it to `namespace a::b { [...] }`. Properly handles `inline` and unnamed namespaces. ~~Also, detects empty blocks in nested namespaces and offers to remove them.~~ Test with common use cases included. I ran the check against entire llvm repository. Except for expected `nested namespace definitions only available with -std=c++17 or -std=gnu++17` warnings I noticed no issues when the check was performed. Example: ``` namespace a { namespace b { void test(); }} ``` can become ``` namespace a::b { void test(); } ``` Patch by wgml! Reviewers: alexfh, aaron.ballman, hokein Reviewed By: aaron.ballman Subscribers: JonasToth, Eugene.Zelenko, lebedev.ri, mgorny, xazax.hun, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D52136 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343000 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/modernize/CMakeLists.txt | 1 + .../modernize/ConcatNestedNamespacesCheck.cpp | 113 ++++++++++++ .../modernize/ConcatNestedNamespacesCheck.h | 41 +++++ clang-tidy/modernize/ModernizeTidyModule.cpp | 3 + docs/ReleaseNotes.rst | 11 +- docs/clang-tidy/checks/list.rst | 1 + .../modernize-concat-nested-namespaces.rst | 49 ++++++ .../modernize-concat-nested-namespaces.cpp | 161 ++++++++++++++++++ 8 files changed, 378 insertions(+), 2 deletions(-) create mode 100644 clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp create mode 100644 clang-tidy/modernize/ConcatNestedNamespacesCheck.h create mode 100644 docs/clang-tidy/checks/modernize-concat-nested-namespaces.rst create mode 100644 test/clang-tidy/modernize-concat-nested-namespaces.cpp diff --git a/clang-tidy/modernize/CMakeLists.txt b/clang-tidy/modernize/CMakeLists.txt index 907eb6c30..06ea18d67 100644 --- a/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tidy/modernize/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyModernizeModule AvoidBindCheck.cpp + ConcatNestedNamespacesCheck.cpp DeprecatedHeadersCheck.cpp LoopConvertCheck.cpp LoopConvertUtils.cpp diff --git a/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp b/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp new file mode 100644 index 000000000..bef85f7b3 --- /dev/null +++ b/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp @@ -0,0 +1,113 @@ +//===--- ConcatNestedNamespacesCheck.cpp - clang-tidy----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ConcatNestedNamespacesCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include +#include + +namespace clang { +namespace tidy { +namespace modernize { + +static bool locationsInSameFile(const SourceManager &Sources, + SourceLocation Loc1, SourceLocation Loc2) { + return Loc1.isFileID() && Loc2.isFileID() && + Sources.getFileID(Loc1) == Sources.getFileID(Loc2); +} + +static bool anonymousOrInlineNamespace(const NamespaceDecl &ND) { + return ND.isAnonymousNamespace() || ND.isInlineNamespace(); +} + +static bool singleNamedNamespaceChild(const NamespaceDecl &ND) { + NamespaceDecl::decl_range Decls = ND.decls(); + if (std::distance(Decls.begin(), Decls.end()) != 1) + return false; + + const auto *ChildNamespace = dyn_cast(*Decls.begin()); + return ChildNamespace && !anonymousOrInlineNamespace(*ChildNamespace); +} + +static bool alreadyConcatenated(std::size_t NumCandidates, + const SourceRange &ReplacementRange, + const SourceManager &Sources, + const LangOptions &LangOpts) { + CharSourceRange TextRange = + Lexer::getAsCharRange(ReplacementRange, Sources, LangOpts); + StringRef CurrentNamespacesText = + Lexer::getSourceText(TextRange, Sources, LangOpts); + return CurrentNamespacesText.count(':') == (NumCandidates - 1) * 2; +} + +ConcatNestedNamespacesCheck::NamespaceString +ConcatNestedNamespacesCheck::concatNamespaces() { + NamespaceString Result("namespace "); + Result.append(Namespaces.front()->getName()); + + std::for_each(std::next(Namespaces.begin()), Namespaces.end(), + [&Result](const NamespaceDecl *ND) { + Result.append("::"); + Result.append(ND->getName()); + }); + + return Result; +} + +void ConcatNestedNamespacesCheck::registerMatchers( + ast_matchers::MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus17) + return; + + Finder->addMatcher(ast_matchers::namespaceDecl().bind("namespace"), this); +} + +void ConcatNestedNamespacesCheck::reportDiagnostic( + const SourceRange &FrontReplacement, const SourceRange &BackReplacement) { + diag(Namespaces.front()->getBeginLoc(), + "nested namespaces can be concatenated", DiagnosticIDs::Warning) + << FixItHint::CreateReplacement(FrontReplacement, concatNamespaces()) + << FixItHint::CreateReplacement(BackReplacement, "}"); +} + +void ConcatNestedNamespacesCheck::check( + const ast_matchers::MatchFinder::MatchResult &Result) { + const NamespaceDecl &ND = *Result.Nodes.getNodeAs("namespace"); + const SourceManager &Sources = *Result.SourceManager; + + if (!locationsInSameFile(Sources, ND.getBeginLoc(), ND.getRBraceLoc())) + return; + + if (!Sources.isInMainFile(ND.getBeginLoc())) + return; + + if (anonymousOrInlineNamespace(ND)) + return; + + Namespaces.push_back(&ND); + + if (singleNamedNamespaceChild(ND)) + return; + + SourceRange FrontReplacement(Namespaces.front()->getBeginLoc(), + Namespaces.back()->getLocation()); + SourceRange BackReplacement(Namespaces.back()->getRBraceLoc(), + Namespaces.front()->getRBraceLoc()); + + if (!alreadyConcatenated(Namespaces.size(), FrontReplacement, Sources, + getLangOpts())) + reportDiagnostic(FrontReplacement, BackReplacement); + + Namespaces.clear(); +} + +} // namespace modernize +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/modernize/ConcatNestedNamespacesCheck.h b/clang-tidy/modernize/ConcatNestedNamespacesCheck.h new file mode 100644 index 000000000..547690d2e --- /dev/null +++ b/clang-tidy/modernize/ConcatNestedNamespacesCheck.h @@ -0,0 +1,41 @@ +//===--- ConcatNestedNamespacesCheck.h - clang-tidy--------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CONCATNESTEDNAMESPACESCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CONCATNESTEDNAMESPACESCHECK_H + +#include "../ClangTidy.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang { +namespace tidy { +namespace modernize { + +class ConcatNestedNamespacesCheck : public ClangTidyCheck { +public: + ConcatNestedNamespacesCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + using NamespaceContextVec = llvm::SmallVector; + using NamespaceString = llvm::SmallString<40>; + + void reportDiagnostic(const SourceRange &FrontReplacement, + const SourceRange &BackReplacement); + NamespaceString concatNamespaces(); + NamespaceContextVec Namespaces; +}; +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CONCATNESTEDNAMESPACESCHECK_H diff --git a/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tidy/modernize/ModernizeTidyModule.cpp index 63b7d0226..fa0a49071 100644 --- a/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "AvoidBindCheck.h" +#include "ConcatNestedNamespacesCheck.h" #include "DeprecatedHeadersCheck.h" #include "LoopConvertCheck.h" #include "MakeSharedCheck.h" @@ -46,6 +47,8 @@ class ModernizeModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck("modernize-avoid-bind"); + CheckFactories.registerCheck( + "modernize-concat-nested-namespaces"); CheckFactories.registerCheck( "modernize-deprecated-headers"); CheckFactories.registerCheck("modernize-loop-convert"); diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index b18ee76ea..386c74e7a 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -72,7 +72,7 @@ Improvements to clang-tidy - New :doc:`abseil-no-internal-dependencies ` check. - + Gives a warning if code using Abseil depends on internal details. - New :doc:`abseil-no-namespace @@ -90,9 +90,16 @@ Improvements to clang-tidy - New :doc:`abseil-str-cat-append ` check. - Flags uses of ``absl::StrCat()`` to append to a ``std::string``. Suggests + Flags uses of ``absl::StrCat()`` to append to a ``std::string``. Suggests ``absl::StrAppend()`` should be used instead. +- New :doc:`modernize-concat-nested-namespaces + ` check. + + Checks for uses of nested namespaces in the form of + ``namespace a { namespace b { ... }}`` and offers change to + syntax introduced in C++17 standard: ``namespace a::b { ... }``. + - New :doc:`readability-magic-numbers ` check. diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index 600969fbd..4180385fe 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -172,6 +172,7 @@ Clang-Tidy Checks misc-unused-parameters misc-unused-using-decls modernize-avoid-bind + modernize-concat-nested-namespaces modernize-deprecated-headers modernize-loop-convert modernize-make-shared diff --git a/docs/clang-tidy/checks/modernize-concat-nested-namespaces.rst b/docs/clang-tidy/checks/modernize-concat-nested-namespaces.rst new file mode 100644 index 000000000..a1fdc7f04 --- /dev/null +++ b/docs/clang-tidy/checks/modernize-concat-nested-namespaces.rst @@ -0,0 +1,49 @@ +.. title:: clang-tidy - modernize-concat-nested-namespaces + +modernize-concat-nested-namespaces +================================== + +Checks for use of nested namespaces such as ``namespace a { namespace b { ... } }`` +and suggests changing to the more concise syntax introduced in C++17: ``namespace a::b { ... }``. +Inline namespaces are not modified. + +For example: + +.. code-block:: c++ + + namespace n1 { + namespace n2 { + void t(); + } + } + + namespace n3 { + namespace n4 { + namespace n5 { + void t(); + } + } + namespace n6 { + namespace n7 { + void t(); + } + } + } + +Will be modified to: + +.. code-block:: c++ + + namespace n1::n2 { + void t(); + } + + namespace n3 { + namespace n4::n5 { + void t(); + } + namespace n6::n7 { + void t(); + } + } + diff --git a/test/clang-tidy/modernize-concat-nested-namespaces.cpp b/test/clang-tidy/modernize-concat-nested-namespaces.cpp new file mode 100644 index 000000000..22a92f601 --- /dev/null +++ b/test/clang-tidy/modernize-concat-nested-namespaces.cpp @@ -0,0 +1,161 @@ +// RUN: %check_clang_tidy %s modernize-concat-nested-namespaces %t -- -- -std=c++17 + +namespace n1 {} + +namespace n2 { +namespace n3 { +void t(); +} +namespace n4 { +void t(); +} +} // namespace n2 + +namespace n5 { +inline namespace n6 { +void t(); +} +} // namespace n5 + +namespace n7 { +void t(); + +namespace n8 { +void t(); +} +} // namespace n7 + +namespace n9 { +namespace n10 { +// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] +// CHECK-FIXES: namespace n9::n10 +void t(); +} // namespace n10 +} // namespace n9 +// CHECK-FIXES: } + +namespace n11 { +namespace n12 { +// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] +// CHECK-FIXES: namespace n11::n12 +namespace n13 { +void t(); +} +namespace n14 { +void t(); +} +} // namespace n12 +} // namespace n11 +// CHECK-FIXES: } + +namespace n15 { +namespace n16 { +void t(); +} + +inline namespace n17 { +void t(); +} + +namespace n18 { +namespace n19 { +namespace n20 { +// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] +// CHECK-FIXES: namespace n18::n19::n20 +void t(); +} // namespace n20 +} // namespace n19 +} // namespace n18 +// CHECK-FIXES: } + +namespace n21 { +void t(); +} +} // namespace n15 + +namespace n22 { +namespace { +void t(); +} +} // namespace n22 + +namespace n23 { +namespace { +namespace n24 { +namespace n25 { +// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] +// CHECK-FIXES: namespace n24::n25 +void t(); +} // namespace n25 +} // namespace n24 +// CHECK-FIXES: } +} // namespace +} // namespace n23 + +namespace n26::n27 { +namespace n28 { +namespace n29::n30 { +// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] +// CHECK-FIXES: namespace n26::n27::n28::n29::n30 +void t() {} +} // namespace n29::n30 +} // namespace n28 +} // namespace n26::n27 +// CHECK-FIXES: } + +namespace n31 { +namespace n32 {} +// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] +} // namespace n31 +// CHECK-FIXES-EMPTY + +namespace n33 { +namespace n34 { +namespace n35 {} +// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] +} // namespace n34 +// CHECK-FIXES-EMPTY +namespace n36 { +void t(); +} +} // namespace n33 + +namespace n37::n38 { +void t(); +} + +#define IEXIST +namespace n39 { +namespace n40 { +// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] +// CHECK-FIXES: namespace n39::n40 +#ifdef IEXIST +void t() {} +#endif +} // namespace n40 +} // namespace n39 +// CHECK-FIXES: } + +namespace n41 { +namespace n42 { +// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: nested namespaces can be concatenated [modernize-concat-nested-namespaces] +// CHECK-FIXES: namespace n41::n42 +#ifdef IDONTEXIST +void t() {} +#endif +} // namespace n42 +} // namespace n41 +// CHECK-FIXES: } + +int main() { + n26::n27::n28::n29::n30::t(); +#ifdef IEXIST + n39::n40::t(); +#endif + +#ifdef IDONTEXIST + n41::n42::t(); +#endif + + return 0; +} From bc7e0bfd03e1e6cf4fae72ea62f20b3672e15f17 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Tue, 25 Sep 2018 18:15:52 +0000 Subject: [PATCH 243/686] [clang-tidy] use CHECK-NOTES in tests for bugprone-macro-repeated-side-effects Reviewers: alexfh, aaron.ballman, hokein Reviewed By: alexfh Subscribers: xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D52230 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343001 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../bugprone-macro-repeated-side-effects.c | 57 ++++++++++++------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/test/clang-tidy/bugprone-macro-repeated-side-effects.c b/test/clang-tidy/bugprone-macro-repeated-side-effects.c index bcea47a4c..995d872dc 100644 --- a/test/clang-tidy/bugprone-macro-repeated-side-effects.c +++ b/test/clang-tidy/bugprone-macro-repeated-side-effects.c @@ -3,21 +3,29 @@ #define badA(x,y) ((x)+((x)+(y))+(y)) void bad(int ret, int a, int b) { ret = badA(a++, b); - // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x' are repeated in macro expansion [bugprone-macro-repeated-side-effects] + // CHECK-NOTES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x' are repeated in macro expansion [bugprone-macro-repeated-side-effects] + // CHECK-NOTES: :[[@LINE-4]]:9: note: macro 'badA' defined here ret = badA(++a, b); - // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x' + // CHECK-NOTES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x' + // CHECK-NOTES: :[[@LINE-7]]:9: note: macro 'badA' defined here ret = badA(a--, b); - // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x' + // CHECK-NOTES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x' + // CHECK-NOTES: :[[@LINE-10]]:9: note: macro 'badA' defined here ret = badA(--a, b); - // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x' + // CHECK-NOTES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x' + // CHECK-NOTES: :[[@LINE-13]]:9: note: macro 'badA' defined here ret = badA(a, b++); - // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y' + // CHECK-NOTES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y' + // CHECK-NOTES: :[[@LINE-16]]:9: note: macro 'badA' defined here ret = badA(a, ++b); - // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y' + // CHECK-NOTES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y' + // CHECK-NOTES: :[[@LINE-19]]:9: note: macro 'badA' defined here ret = badA(a, b--); - // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y' + // CHECK-NOTES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y' + // CHECK-NOTES: :[[@LINE-22]]:9: note: macro 'badA' defined here ret = badA(a, --b); - // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y' + // CHECK-NOTES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y' + // CHECK-NOTES: :[[@LINE-25]]:9: note: macro 'badA' defined here } @@ -25,15 +33,20 @@ void bad(int ret, int a, int b) { #define LIMIT(X,A,B) ((X) < (A) ? (A) : ((X) > (B) ? (B) : (X))) // two ?: void question(int x) { MIN(x++, 12); - // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: side effects in the 1st macro argument 'A' + // CHECK-NOTES: :[[@LINE-1]]:7: warning: side effects in the 1st macro argument 'A' + // CHECK-NOTES: :[[@LINE-5]]:9: note: macro 'MIN' defined here MIN(34, x++); - // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: side effects in the 2nd macro argument 'B' + // CHECK-NOTES: :[[@LINE-1]]:11: warning: side effects in the 2nd macro argument 'B' + // CHECK-NOTES: :[[@LINE-8]]:9: note: macro 'MIN' defined here LIMIT(x++, 0, 100); - // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: side effects in the 1st macro argument 'X' + // CHECK-NOTES: :[[@LINE-1]]:9: warning: side effects in the 1st macro argument 'X' + // CHECK-NOTES: :[[@LINE-10]]:9: note: macro 'LIMIT' defined here LIMIT(20, x++, 100); - // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: side effects in the 2nd macro argument 'A' + // CHECK-NOTES: :[[@LINE-1]]:13: warning: side effects in the 2nd macro argument 'A' + // CHECK-NOTES: :[[@LINE-13]]:9: note: macro 'LIMIT' defined here LIMIT(20, 0, x++); - // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: side effects in the 3rd macro argument 'B' + // CHECK-NOTES: :[[@LINE-1]]:16: warning: side effects in the 3rd macro argument 'B' + // CHECK-NOTES: :[[@LINE-16]]:9: note: macro 'LIMIT' defined here } // False positive: Repeated side effects is intentional. @@ -41,7 +54,8 @@ void question(int x) { #define UNROLL(A) {A A} void fp1(int i) { UNROLL({ i++; }); - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: side effects in the 1st macro argument 'A' + // CHECK-NOTES: :[[@LINE-1]]:10: warning: side effects in the 1st macro argument 'A' + // CHECK-NOTES: :[[@LINE-4]]:9: note: macro 'UNROLL' defined here } // Do not produce a false positive on a strchr() macro. Explanation; Currently the '?' @@ -66,11 +80,14 @@ void pass(char* pstr, char ch) { (v) + (v) + (x) + (x) + (y) + (y) + (z) + (z)) void large(int a) { largeA(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a++, 0, 0, 0, 0, 0, 0); - // CHECK-MESSAGES: :[[@LINE-1]]:64: warning: side effects in the 19th macro argument 's' + // CHECK-NOTES: :[[@LINE-1]]:64: warning: side effects in the 19th macro argument 's' + // CHECK-NOTES: :[[@LINE-8]]:9: note: macro 'largeA' defined here largeA(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a++, 0, 0, 0, 0, 0); - // CHECK-MESSAGES: :[[@LINE-1]]:67: warning: side effects in the 20th macro argument 't' + // CHECK-NOTES: :[[@LINE-1]]:67: warning: side effects in the 20th macro argument 't' + // CHECK-NOTES: :[[@LINE-11]]:9: note: macro 'largeA' defined here largeA(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a++, 0, 0, 0, 0); - // CHECK-MESSAGES: :[[@LINE-1]]:70: warning: side effects in the 21st macro argument 'u' + // CHECK-NOTES: :[[@LINE-1]]:70: warning: side effects in the 21st macro argument 'u' + // CHECK-NOTES: :[[@LINE-14]]:9: note: macro 'largeA' defined here } // Passing macro argument as argument to __builtin_constant_p and macros. @@ -81,13 +98,15 @@ void large(int a) { #define macrogood(x) (builtingood1(x) + (x)) void builtins(int ret, int a) { ret += builtinbad(a++); - // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: side effects in the 1st macro argument 'x' + // CHECK-NOTES: :[[@LINE-1]]:21: warning: side effects in the 1st macro argument 'x' + // CHECK-NOTES: :[[@LINE-8]]:9: note: macro 'builtinbad' defined here ret += builtingood1(a++); ret += builtingood2(a++); ret += macrobad(a++); - // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: side effects in the 1st macro argument 'x' + // CHECK-NOTES: :[[@LINE-1]]:19: warning: side effects in the 1st macro argument 'x' + // CHECK-NOTES: :[[@LINE-12]]:9: note: macro 'macrobad' defined here ret += macrogood(a++); } From 1fe1993c0d5908c4d6dc9d52a48ec289f90d23be Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 25 Sep 2018 19:53:33 +0000 Subject: [PATCH 244/686] [clangd] Fix reversed RIFF/YAML serialization git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343017 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Serialization.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index 116e974f4..4f5413a7e 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -389,10 +389,10 @@ Expected readYAML(StringRef); llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IndexFileOut &O) { switch (O.Format) { case IndexFileFormat::RIFF: - writeYAML(O, OS); + writeRIFF(O, OS); break; case IndexFileFormat::YAML: - writeRIFF(O, OS); + writeYAML(O, OS); break; } return OS; From de725ec8a2b9f9f6ac44808f6648930935fd680b Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 25 Sep 2018 20:02:36 +0000 Subject: [PATCH 245/686] [clangd] Extract mapper logic from clangd-indexer into a library. Summary: Soon we can drop support for MR-via-YAML. I need to modify some out-of-tree versions to use the library, first. Reviewers: kadircet Subscribers: mgorny, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D52465 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343019 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 1 + clangd/index/IndexAction.cpp | 73 ++++++++++++++++++++++++++++++++++ clangd/index/IndexAction.h | 32 +++++++++++++++ clangd/index/Serialization.h | 21 +++++----- clangd/index/SymbolCollector.h | 3 ++ clangd/indexer/IndexerMain.cpp | 65 +++--------------------------- 6 files changed, 126 insertions(+), 69 deletions(-) create mode 100644 clangd/index/IndexAction.cpp create mode 100644 clangd/index/IndexAction.h diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index a8cd64a10..0894a927e 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -40,6 +40,7 @@ add_clang_library(clangDaemon index/CanonicalIncludes.cpp index/FileIndex.cpp index/Index.cpp + index/IndexAction.cpp index/MemIndex.cpp index/Merge.cpp index/Serialization.cpp diff --git a/clangd/index/IndexAction.cpp b/clangd/index/IndexAction.cpp new file mode 100644 index 000000000..b9dcb6e19 --- /dev/null +++ b/clangd/index/IndexAction.cpp @@ -0,0 +1,73 @@ +#include "IndexAction.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/IndexingAction.h" +#include "clang/Tooling/Tooling.h" +namespace clang { +namespace clangd { +namespace { + +// Wraps the index action and reports index data after each translation unit. +class IndexAction : public WrapperFrontendAction { +public: + IndexAction(std::shared_ptr C, + std::unique_ptr Includes, + const index::IndexingOptions &Opts, + std::function &SymbolsCallback) + : WrapperFrontendAction(index::createIndexingAction(C, Opts, nullptr)), + SymbolsCallback(SymbolsCallback), Collector(C), + Includes(std::move(Includes)), + PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {} + + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + CI.getPreprocessor().addCommentHandler(PragmaHandler.get()); + return WrapperFrontendAction::CreateASTConsumer(CI, InFile); + } + + bool BeginInvocation(CompilerInstance &CI) override { + // We want all comments, not just the doxygen ones. + CI.getLangOpts().CommentOpts.ParseAllComments = true; + return WrapperFrontendAction::BeginInvocation(CI); + } + + void EndSourceFileAction() override { + WrapperFrontendAction::EndSourceFileAction(); + + const auto &CI = getCompilerInstance(); + if (CI.hasDiagnostics() && + CI.getDiagnostics().hasUncompilableErrorOccurred()) { + llvm::errs() << "Skipping TU due to uncompilable errors\n"; + return; + } + SymbolsCallback(Collector->takeSymbols()); + } + +private: + std::function SymbolsCallback; + std::shared_ptr Collector; + std::unique_ptr Includes; + std::unique_ptr PragmaHandler; +}; + +} // namespace + +std::unique_ptr +createStaticIndexingAction(SymbolCollector::Options Opts, + std::function SymbolsCallback) { + index::IndexingOptions IndexOpts; + IndexOpts.SystemSymbolFilter = + index::IndexingOptions::SystemSymbolFilterKind::All; + Opts.CollectIncludePath = true; + Opts.CountReferences = true; + Opts.Origin = SymbolOrigin::Static; + auto Includes = llvm::make_unique(); + addSystemHeadersMapping(Includes.get()); + Opts.Includes = Includes.get(); + return llvm::make_unique( + std::make_shared(std::move(Opts)), std::move(Includes), + IndexOpts, SymbolsCallback); +}; + +} // namespace clangd +} // namespace clang diff --git a/clangd/index/IndexAction.h b/clangd/index/IndexAction.h new file mode 100644 index 000000000..b51bfd252 --- /dev/null +++ b/clangd/index/IndexAction.h @@ -0,0 +1,32 @@ +//===--- IndexAction.h - Run the indexer as a frontend action ----*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_INDEX_ACTION_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_INDEX_ACTION_H +#include "SymbolCollector.h" +#include "clang/Frontend/FrontendActions.h" + +namespace clang { +namespace clangd { + +// Creates an action that indexes translation units and delivers the results +// for SymbolsCallback (each slab corresponds to one TU). +// +// Only a subset of SymbolCollector::Options are respected: +// - include paths are always collected, and canonicalized appropriately +// - references are always counted +// - the symbol origin is always Static +std::unique_ptr +createStaticIndexingAction(SymbolCollector::Options Opts, + std::function SymbolsCallback); + +} // namespace clangd +} // namespace clang + +#endif diff --git a/clangd/index/Serialization.h b/clangd/index/Serialization.h index c73ce23b5..4240bdb72 100644 --- a/clangd/index/Serialization.h +++ b/clangd/index/Serialization.h @@ -40,23 +40,26 @@ enum class IndexFileFormat { YAML, // Human-readable format, suitable for experiments and debugging. }; +// Holds the contents of an index file that was read. +struct IndexFileIn { + llvm::Optional Symbols; +}; +// Parse an index file. The input must be a RIFF container chunk. +llvm::Expected readIndexFile(llvm::StringRef); + // Specifies the contents of an index file to be written. struct IndexFileOut { const SymbolSlab *Symbols; // TODO: Support serializing symbol occurrences. // TODO: Support serializing Dex posting lists. IndexFileFormat Format = IndexFileFormat::RIFF; -}; -// Serializes an index file. (This is a RIFF container chunk). -llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IndexFileOut &O); -// Holds the contents of an index file that was read. -struct IndexFileIn { - llvm::Optional Symbols; - IndexFileFormat Format; + IndexFileOut() = default; + IndexFileOut(const IndexFileIn &I) + : Symbols(I.Symbols ? I.Symbols.getPointer() : nullptr) {} }; -// Parse an index file. The input must be a RIFF container chunk. -llvm::Expected readIndexFile(llvm::StringRef); +// Serializes an index file. +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IndexFileOut &O); std::string toYAML(const Symbol &); // Returned symbol is backed by the YAML input. diff --git a/clangd/index/SymbolCollector.h b/clangd/index/SymbolCollector.h index db0e3074e..1994183c2 100644 --- a/clangd/index/SymbolCollector.h +++ b/clangd/index/SymbolCollector.h @@ -6,6 +6,8 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOL_COLLECTOR_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_SYMBOL_COLLECTOR_H #include "CanonicalIncludes.h" #include "Index.h" @@ -122,3 +124,4 @@ class SymbolCollector : public index::IndexDataConsumer { } // namespace clangd } // namespace clang +#endif diff --git a/clangd/indexer/IndexerMain.cpp b/clangd/indexer/IndexerMain.cpp index dbd43cd19..913669382 100644 --- a/clangd/indexer/IndexerMain.cpp +++ b/clangd/indexer/IndexerMain.cpp @@ -15,6 +15,7 @@ #include "RIFF.h" #include "index/CanonicalIncludes.h" #include "index/Index.h" +#include "index/IndexAction.h" #include "index/Merge.h" #include "index/Serialization.h" #include "index/SymbolCollector.h" @@ -86,68 +87,12 @@ class SymbolIndexActionFactory : public tooling::FrontendActionFactory { SymbolIndexActionFactory(SymbolsConsumer &Consumer) : Consumer(Consumer) {} clang::FrontendAction *create() override { - // Wraps the index action and reports collected symbols to the execution - // context at the end of each translation unit. - class WrappedIndexAction : public WrapperFrontendAction { - public: - WrappedIndexAction(std::shared_ptr C, - std::unique_ptr Includes, - const index::IndexingOptions &Opts, - SymbolsConsumer &Consumer) - : WrapperFrontendAction( - index::createIndexingAction(C, Opts, nullptr)), - Consumer(Consumer), Collector(C), Includes(std::move(Includes)), - PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {} - - std::unique_ptr - CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { - CI.getPreprocessor().addCommentHandler(PragmaHandler.get()); - return WrapperFrontendAction::CreateASTConsumer(CI, InFile); - } - - bool BeginInvocation(CompilerInstance &CI) override { - // We want all comments, not just the doxygen ones. - CI.getLangOpts().CommentOpts.ParseAllComments = true; - return WrapperFrontendAction::BeginInvocation(CI); - } - - void EndSourceFileAction() override { - WrapperFrontendAction::EndSourceFileAction(); - - const auto &CI = getCompilerInstance(); - if (CI.hasDiagnostics() && - CI.getDiagnostics().hasUncompilableErrorOccurred()) { - llvm::errs() - << "Found uncompilable errors in the translation unit. Igoring " - "collected symbols...\n"; - return; - } - - Consumer.consumeSymbols(Collector->takeSymbols()); - } - - private: - SymbolsConsumer &Consumer; - std::shared_ptr Collector; - std::unique_ptr Includes; - std::unique_ptr PragmaHandler; - }; - - index::IndexingOptions IndexOpts; - IndexOpts.SystemSymbolFilter = - index::IndexingOptions::SystemSymbolFilterKind::All; - IndexOpts.IndexFunctionLocals = false; auto CollectorOpts = SymbolCollector::Options(); CollectorOpts.FallbackDir = AssumedHeaderDir; - CollectorOpts.CollectIncludePath = true; - CollectorOpts.CountReferences = true; - CollectorOpts.Origin = SymbolOrigin::Static; - auto Includes = llvm::make_unique(); - addSystemHeadersMapping(Includes.get()); - CollectorOpts.Includes = Includes.get(); - return new WrappedIndexAction( - std::make_shared(std::move(CollectorOpts)), - std::move(Includes), IndexOpts, Consumer); + return createStaticIndexingAction( + CollectorOpts, + [&](SymbolSlab S) { Consumer.consumeSymbols(std::move(S)); }) + .release(); } SymbolsConsumer &Consumer; From 284e71380ae0656884c6dd1473a02970b5c77a7c Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Tue, 25 Sep 2018 22:32:11 +0000 Subject: [PATCH 246/686] [clangd] Remove unused using-declaration testing::AllOf git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343039 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/SerializationTests.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/unittests/clangd/SerializationTests.cpp b/unittests/clangd/SerializationTests.cpp index 7d4ac3bd5..751883b27 100644 --- a/unittests/clangd/SerializationTests.cpp +++ b/unittests/clangd/SerializationTests.cpp @@ -13,7 +13,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -using testing::AllOf; using testing::UnorderedElementsAre; using testing::UnorderedElementsAreArray; namespace clang { From 2659ea55929dcb946174478ea43de6d1ff46799f Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Wed, 26 Sep 2018 05:45:31 +0000 Subject: [PATCH 247/686] [clangd] Handle template args for disabled function arg snippets Reviewers: kadircet, ioeric, sammccall Reviewed By: kadircet Subscribers: MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D52422 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343066 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 42 ++++++++++++++++++--- unittests/clangd/CodeCompleteTests.cpp | 51 +++++++++++++++++++++----- 2 files changed, 78 insertions(+), 15 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index ca083964e..2328fc601 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -482,13 +482,43 @@ struct CodeCompletionBuilder { auto *Snippet = onlyValue<&BundledEntry::SnippetSuffix>(); if (!Snippet) // All bundles are function calls. + // FIXME(ibiryukov): sometimes add template arguments to a snippet, e.g. + // we need to complete 'forward<$1>($0)'. return "($0)"; - if (!Snippet->empty() && !EnableFunctionArgSnippets && - ((Completion.Kind == CompletionItemKind::Function) || - (Completion.Kind == CompletionItemKind::Method)) && - (Snippet->front() == '(') && (Snippet->back() == ')')) - // Check whether function has any parameters or not. - return Snippet->size() > 2 ? "($0)" : "()"; + if (EnableFunctionArgSnippets) + return *Snippet; + + // Replace argument snippets with a simplified pattern. + if (Snippet->empty()) + return ""; + if (Completion.Kind == CompletionItemKind::Function || + Completion.Kind == CompletionItemKind::Method) { + // Functions snippets can be of 2 types: + // - containing only function arguments, e.g. + // foo(${1:int p1}, ${2:int p2}); + // We transform this pattern to '($0)' or '()'. + // - template arguments and function arguments, e.g. + // foo<${1:class}>(${2:int p1}). + // We transform this pattern to '<$1>()$0' or '<$0>()'. + + bool EmptyArgs = llvm::StringRef(*Snippet).endswith("()"); + if (Snippet->front() == '<') + return EmptyArgs ? "<$1>()$0" : "<$1>($0)"; + if (Snippet->front() == '(') + return EmptyArgs ? "()" : "($0)"; + return *Snippet; // Not an arg snippet? + } + if (Completion.Kind == CompletionItemKind::Reference || + Completion.Kind == CompletionItemKind::Class) { + if (Snippet->front() != '<') + return *Snippet; // Not an arg snippet? + + // Classes and template using aliases can only have template arguments, + // e.g. Foo<${1:class}>. + if (llvm::StringRef(*Snippet).endswith("<>")) + return "<>"; // can happen with defaulted template arguments. + return "<$0>"; + } return *Snippet; } diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index c1e8404c7..c7e387a34 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -1741,32 +1741,65 @@ TEST(CompletionTest, CompletionFunctionArgsDisabled) { CodeCompleteOptions Opts; Opts.EnableSnippets = true; Opts.EnableFunctionArgSnippets = false; - const std::string Header = - R"cpp( + + { + auto Results = completions( + R"cpp( void xfoo(); void xfoo(int x, int y); - void xbar(); - void f() { - )cpp"; - { - auto Results = completions(Header + "\nxfo^", {}, Opts); + void f() { xfo^ })cpp", + {}, Opts); EXPECT_THAT( Results.Completions, UnorderedElementsAre(AllOf(Named("xfoo"), SnippetSuffix("()")), AllOf(Named("xfoo"), SnippetSuffix("($0)")))); } { - auto Results = completions(Header + "\nxba^", {}, Opts); + auto Results = completions( + R"cpp( + void xbar(); + void f() { xba^ })cpp", + {}, Opts); EXPECT_THAT(Results.Completions, UnorderedElementsAre(AllOf( Named("xbar"), SnippetSuffix("()")))); } { Opts.BundleOverloads = true; - auto Results = completions(Header + "\nxfo^", {}, Opts); + auto Results = completions( + R"cpp( + void xfoo(); + void xfoo(int x, int y); + void f() { xfo^ })cpp", + {}, Opts); EXPECT_THAT( Results.Completions, UnorderedElementsAre(AllOf(Named("xfoo"), SnippetSuffix("($0)")))); } + { + auto Results = completions( + R"cpp( + template + void xfoo(int a, U b); + void f() { xfo^ })cpp", + {}, Opts); + EXPECT_THAT( + Results.Completions, + UnorderedElementsAre(AllOf(Named("xfoo"), SnippetSuffix("<$1>($0)")))); + } + { + auto Results = completions( + R"cpp( + template + class foo_class{}; + template + using foo_alias = T**; + void f() { foo_^ })cpp", + {}, Opts); + EXPECT_THAT( + Results.Completions, + UnorderedElementsAre(AllOf(Named("foo_class"), SnippetSuffix("<$0>")), + AllOf(Named("foo_alias"), SnippetSuffix("<$0>")))); + } } TEST(CompletionTest, SuggestOverrides) { From dad0145922a626f69020f1ae34f76e9fd8e12861 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Wed, 26 Sep 2018 05:48:29 +0000 Subject: [PATCH 248/686] [clangd] Fix crash if pending computations were active on exit Summary: Make sure JSONRPCDispatcher outlives the worker threads, they access its fields to remove the stored cancellations when Context dies. Reviewers: sammccall, ioeric Reviewed By: ioeric Subscribers: MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52420 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343067 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 126 +++++++++++++++++++------------------ clangd/ClangdLSPServer.h | 5 +- 2 files changed, 69 insertions(+), 62 deletions(-) diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index c68d422d3..a4ebf575d 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -77,9 +77,9 @@ void ClangdLSPServer::onInitialize(InitializeParams &Params) { applyConfiguration(*Params.initializationOptions); if (Params.rootUri && *Params.rootUri) - Server.setRootPath(Params.rootUri->file()); + Server->setRootPath(Params.rootUri->file()); else if (Params.rootPath && !Params.rootPath->empty()) - Server.setRootPath(*Params.rootPath); + Server->setRootPath(*Params.rootPath); CCOpts.EnableSnippets = Params.capabilities.textDocument.completion.completionItem.snippetSupport; @@ -147,7 +147,7 @@ void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams &Params) { std::string &Contents = Params.textDocument.text; DraftMgr.addDraft(File, Contents); - Server.addDocument(File, Contents, WantDiagnostics::Yes); + Server->addDocument(File, Contents, WantDiagnostics::Yes); } void ClangdLSPServer::onDocumentDidChange(DidChangeTextDocumentParams &Params) { @@ -164,17 +164,17 @@ void ClangdLSPServer::onDocumentDidChange(DidChangeTextDocumentParams &Params) { // the client. It is better to remove the draft and let further operations // fail rather than giving wrong results. DraftMgr.removeDraft(File); - Server.removeDocument(File); + Server->removeDocument(File); CDB.invalidate(File); elog("Failed to update {0}: {1}", File, Contents.takeError()); return; } - Server.addDocument(File, *Contents, WantDiags); + Server->addDocument(File, *Contents, WantDiags); } void ClangdLSPServer::onFileEvent(DidChangeWatchedFilesParams &Params) { - Server.onFileEvent(Params); + Server->onFileEvent(Params); } void ClangdLSPServer::onCommand(ExecuteCommandParams &Params) { @@ -210,7 +210,7 @@ void ClangdLSPServer::onCommand(ExecuteCommandParams &Params) { } void ClangdLSPServer::onWorkspaceSymbol(WorkspaceSymbolParams &Params) { - Server.workspaceSymbols( + Server->workspaceSymbols( Params.query, CCOpts.Limit, [this](llvm::Expected> Items) { if (!Items) @@ -230,7 +230,7 @@ void ClangdLSPServer::onRename(RenameParams &Params) { return replyError(ErrorCode::InvalidParams, "onRename called for non-added file"); - Server.rename( + Server->rename( File, Params.position, Params.newName, [File, Code, Params](llvm::Expected> Replacements) { @@ -252,7 +252,7 @@ void ClangdLSPServer::onRename(RenameParams &Params) { void ClangdLSPServer::onDocumentDidClose(DidCloseTextDocumentParams &Params) { PathRef File = Params.textDocument.uri.file(); DraftMgr.removeDraft(File); - Server.removeDocument(File); + Server->removeDocument(File); CDB.invalidate(File); } @@ -264,7 +264,7 @@ void ClangdLSPServer::onDocumentOnTypeFormatting( return replyError(ErrorCode::InvalidParams, "onDocumentOnTypeFormatting called for non-added file"); - auto ReplacementsOrError = Server.formatOnType(*Code, File, Params.position); + auto ReplacementsOrError = Server->formatOnType(*Code, File, Params.position); if (ReplacementsOrError) reply(json::Array(replacementsToEdits(*Code, ReplacementsOrError.get()))); else @@ -280,7 +280,7 @@ void ClangdLSPServer::onDocumentRangeFormatting( return replyError(ErrorCode::InvalidParams, "onDocumentRangeFormatting called for non-added file"); - auto ReplacementsOrError = Server.formatRange(*Code, File, Params.range); + auto ReplacementsOrError = Server->formatRange(*Code, File, Params.range); if (ReplacementsOrError) reply(json::Array(replacementsToEdits(*Code, ReplacementsOrError.get()))); else @@ -295,7 +295,7 @@ void ClangdLSPServer::onDocumentFormatting(DocumentFormattingParams &Params) { return replyError(ErrorCode::InvalidParams, "onDocumentFormatting called for non-added file"); - auto ReplacementsOrError = Server.formatFile(*Code, File); + auto ReplacementsOrError = Server->formatFile(*Code, File); if (ReplacementsOrError) reply(json::Array(replacementsToEdits(*Code, ReplacementsOrError.get()))); else @@ -304,7 +304,7 @@ void ClangdLSPServer::onDocumentFormatting(DocumentFormattingParams &Params) { } void ClangdLSPServer::onDocumentSymbol(DocumentSymbolParams &Params) { - Server.documentSymbols( + Server->documentSymbols( Params.textDocument.uri.file(), [this](llvm::Expected> Items) { if (!Items) @@ -341,47 +341,47 @@ void ClangdLSPServer::onCodeAction(CodeActionParams &Params) { } void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) { - Server.codeComplete(Params.textDocument.uri.file(), Params.position, CCOpts, - [this](llvm::Expected List) { - if (!List) - return replyError(List.takeError()); - CompletionList LSPList; - LSPList.isIncomplete = List->HasMore; - for (const auto &R : List->Completions) - LSPList.items.push_back(R.render(CCOpts)); - return reply(std::move(LSPList)); - }); + Server->codeComplete(Params.textDocument.uri.file(), Params.position, CCOpts, + [this](llvm::Expected List) { + if (!List) + return replyError(List.takeError()); + CompletionList LSPList; + LSPList.isIncomplete = List->HasMore; + for (const auto &R : List->Completions) + LSPList.items.push_back(R.render(CCOpts)); + return reply(std::move(LSPList)); + }); } void ClangdLSPServer::onSignatureHelp(TextDocumentPositionParams &Params) { - Server.signatureHelp(Params.textDocument.uri.file(), Params.position, - [](llvm::Expected SignatureHelp) { - if (!SignatureHelp) - return replyError( - ErrorCode::InvalidParams, - llvm::toString(SignatureHelp.takeError())); - reply(*SignatureHelp); - }); + Server->signatureHelp(Params.textDocument.uri.file(), Params.position, + [](llvm::Expected SignatureHelp) { + if (!SignatureHelp) + return replyError( + ErrorCode::InvalidParams, + llvm::toString(SignatureHelp.takeError())); + reply(*SignatureHelp); + }); } void ClangdLSPServer::onGoToDefinition(TextDocumentPositionParams &Params) { - Server.findDefinitions(Params.textDocument.uri.file(), Params.position, - [](llvm::Expected> Items) { - if (!Items) - return replyError( - ErrorCode::InvalidParams, - llvm::toString(Items.takeError())); - reply(json::Array(*Items)); - }); + Server->findDefinitions(Params.textDocument.uri.file(), Params.position, + [](llvm::Expected> Items) { + if (!Items) + return replyError( + ErrorCode::InvalidParams, + llvm::toString(Items.takeError())); + reply(json::Array(*Items)); + }); } void ClangdLSPServer::onSwitchSourceHeader(TextDocumentIdentifier &Params) { - llvm::Optional Result = Server.switchSourceHeader(Params.uri.file()); + llvm::Optional Result = Server->switchSourceHeader(Params.uri.file()); reply(Result ? URI::createFile(*Result).toString() : ""); } void ClangdLSPServer::onDocumentHighlight(TextDocumentPositionParams &Params) { - Server.findDocumentHighlights( + Server->findDocumentHighlights( Params.textDocument.uri.file(), Params.position, [](llvm::Expected> Highlights) { if (!Highlights) @@ -392,16 +392,16 @@ void ClangdLSPServer::onDocumentHighlight(TextDocumentPositionParams &Params) { } void ClangdLSPServer::onHover(TextDocumentPositionParams &Params) { - Server.findHover(Params.textDocument.uri.file(), Params.position, - [](llvm::Expected> H) { - if (!H) { - replyError(ErrorCode::InternalError, - llvm::toString(H.takeError())); - return; - } + Server->findHover(Params.textDocument.uri.file(), Params.position, + [](llvm::Expected> H) { + if (!H) { + replyError(ErrorCode::InternalError, + llvm::toString(H.takeError())); + return; + } - reply(*H); - }); + reply(*H); + }); } void ClangdLSPServer::applyConfiguration( @@ -440,14 +440,14 @@ void ClangdLSPServer::onChangeConfiguration( } void ClangdLSPServer::onReference(ReferenceParams &Params) { - Server.findReferences(Params.textDocument.uri.file(), Params.position, - [](llvm::Expected> Locations) { - if (!Locations) - return replyError( - ErrorCode::InternalError, - llvm::toString(Locations.takeError())); - reply(llvm::json::Array(*Locations)); - }); + Server->findReferences(Params.textDocument.uri.file(), Params.position, + [](llvm::Expected> Locations) { + if (!Locations) + return replyError( + ErrorCode::InternalError, + llvm::toString(Locations.takeError())); + reply(llvm::json::Array(*Locations)); + }); } ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, @@ -459,10 +459,12 @@ ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, : CompilationDB::makeDirectoryBased( std::move(CompileCommandsDir))), CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()), - Server(CDB.getCDB(), FSProvider, /*DiagConsumer=*/*this, Opts) {} + Server(new ClangdServer(CDB.getCDB(), FSProvider, /*DiagConsumer=*/*this, + Opts)) {} bool ClangdLSPServer::run(std::FILE *In, JSONStreamStyle InputStyle) { assert(!IsDone && "Run was called before"); + assert(Server); // Set up JSONRPCDispatcher. JSONRPCDispatcher Dispatcher([](const json::Value &Params) { @@ -476,6 +478,8 @@ bool ClangdLSPServer::run(std::FILE *In, JSONStreamStyle InputStyle) { // Make sure IsDone is set to true after this method exits to ensure assertion // at the start of the method fires if it's ever executed again. IsDone = true; + // Destroy ClangdServer to ensure all worker threads finish. + Server.reset(); return ShutdownRequestReceived; } @@ -551,8 +555,8 @@ void ClangdLSPServer::onDiagnosticsReady(PathRef File, void ClangdLSPServer::reparseOpenedFiles() { for (const Path &FilePath : DraftMgr.getActiveFiles()) - Server.addDocument(FilePath, *DraftMgr.getDraft(FilePath), - WantDiagnostics::Auto); + Server->addDocument(FilePath, *DraftMgr.getDraft(FilePath), + WantDiagnostics::Auto); } ClangdLSPServer::CompilationDB ClangdLSPServer::CompilationDB::makeInMemory() { diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h index 62260e222..f39fdb61c 100644 --- a/clangd/ClangdLSPServer.h +++ b/clangd/ClangdLSPServer.h @@ -19,6 +19,7 @@ #include "ProtocolHandlers.h" #include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/Optional.h" +#include namespace clang { namespace clangd { @@ -170,7 +171,9 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { // Server must be the last member of the class to allow its destructor to exit // the worker thread that may otherwise run an async callback on partially // destructed instance of ClangdLSPServer. - ClangdServer Server; + // Set in construtor and destroyed when run() finishes. To ensure all worker + // threads exit before run() returns. + std::unique_ptr Server; }; } // namespace clangd } // namespace clang From 3d39bcd0bcb3661f0fa005a1dc73b127bc33d94d Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Wed, 26 Sep 2018 09:02:45 +0000 Subject: [PATCH 249/686] Removed extra semicolon to fix Wpedantic. (NFCI). git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343083 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/IndexAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/index/IndexAction.cpp b/clangd/index/IndexAction.cpp index b9dcb6e19..9bc7e06ae 100644 --- a/clangd/index/IndexAction.cpp +++ b/clangd/index/IndexAction.cpp @@ -67,7 +67,7 @@ createStaticIndexingAction(SymbolCollector::Options Opts, return llvm::make_unique( std::make_shared(std::move(Opts)), std::move(Includes), IndexOpts, SymbolsCallback); -}; +} } // namespace clangd } // namespace clang From e023098ce95fd6c4a1fa82b8e0ef24e5770655d3 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Wed, 26 Sep 2018 14:59:49 +0000 Subject: [PATCH 250/686] [docs] Update PostingList string representation format Because `PostingList` objects are compressed, it is now impossible to see elements other than the current one and the documentation doesn't match implementation anymore. Reviewed By: ioeric Differential Revision: https://reviews.llvm.org/D52545 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343116 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 5e5460f01..3066a8c78 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -94,9 +94,8 @@ class Iterator { /// /// Where Type is the iterator type representation: "&" for And, "|" for Or, /// ChildN is N-th iterator child. Raw iterators over PostingList are - /// represented as "[ID1, ID2, ..., {IDN}, ... END]" where IDN is N-th - /// PostingList entry and the element which is pointed to by the PostingList - /// iterator is enclosed in {} braces. + /// represented as "[... CurID ...]" where CurID is the current PostingList + /// entry being inspected. friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Iterator &Iterator) { return Iterator.dump(OS); From 4a134ab29343ab4cec8e489a28812ab83fa597fe Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Wed, 26 Sep 2018 15:06:23 +0000 Subject: [PATCH 251/686] [clangd] Fix bugs with incorrect memory estimate report * With the current implementation, `sizeof(std::vector)` is added twice to the `Dex` memory estimate which is incorrect * `Dex` logs memory usage estimation before `BackingDataSize` is set and hence the log report excludes size of the external `SymbolSlab` which is coupled with `Dex` instance Reviewed By: ioeric Differential Revision: https://reviews.llvm.org/D52503 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343117 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Serialization.cpp | 10 ++++++++-- clangd/index/dex/Dex.cpp | 7 ++----- clangd/index/dex/PostingList.h | 6 ++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index 4f5413a7e..5e16137f8 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -6,8 +6,10 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// + #include "Serialization.h" #include "Index.h" +#include "Logger.h" #include "RIFF.h" #include "Trace.h" #include "dex/Dex.h" @@ -433,8 +435,12 @@ std::unique_ptr loadIndex(llvm::StringRef SymbolFilename, } trace::Span Tracer("BuildIndex"); - return UseDex ? dex::Dex::build(std::move(Symbols), URISchemes) - : MemIndex::build(std::move(Symbols), std::move(Refs)); + auto Index = UseDex ? dex::Dex::build(std::move(Symbols), URISchemes) + : MemIndex::build(std::move(Symbols), std::move(Refs)); + vlog("Loaded {0} from {1} with estimated memory usage {2}", + UseDex ? "Dex" : "MemIndex", SymbolFilename, + Index->estimateMemoryUsage()); + return Index; } } // namespace clangd diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 29742227b..4086d1f00 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -130,9 +130,6 @@ void Dex::buildIndex() { for (const auto &TokenToPostingList : TempInvertedIndex) InvertedIndex.insert( {TokenToPostingList.first, PostingList(TokenToPostingList.second)}); - - vlog("Built Dex with estimated memory usage {0} bytes.", - estimateMemoryUsage()); } /// Constructs iterators over tokens extracted from the query and exhausts it @@ -248,8 +245,8 @@ size_t Dex::estimateMemoryUsage() const { Bytes += SymbolQuality.size() * sizeof(float); Bytes += LookupTable.getMemorySize(); Bytes += InvertedIndex.getMemorySize(); - for (const auto &P : InvertedIndex) - Bytes += P.second.bytes(); + for (const auto &TokenToPostingList : InvertedIndex) + Bytes += TokenToPostingList.second.bytes(); return Bytes + BackingDataSize; } diff --git a/clangd/index/dex/PostingList.h b/clangd/index/dex/PostingList.h index 010fb1b03..5b3c9b79f 100644 --- a/clangd/index/dex/PostingList.h +++ b/clangd/index/dex/PostingList.h @@ -66,10 +66,8 @@ class PostingList { /// go through the chunks and decompress them on-the-fly when necessary. std::unique_ptr iterator() const; - /// Returns in-memory size. - size_t bytes() const { - return sizeof(Chunk) + Chunks.capacity() * sizeof(Chunk); - } + /// Returns in-memory size of external storage. + size_t bytes() const { return Chunks.capacity() * sizeof(Chunk); } private: const std::vector Chunks; From 9d8988a1b7ef863a01f88bdb7262119fa640eb81 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Thu, 27 Sep 2018 04:19:29 +0000 Subject: [PATCH 252/686] llvm::sort(C.begin(), C.end()) -> llvm::sort(C) The convenience wrapper in STLExtras is available since rL342102. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343166 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/readability/MagicNumbersCheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tidy/readability/MagicNumbersCheck.cpp b/clang-tidy/readability/MagicNumbersCheck.cpp index 08021f325..0a0872199 100644 --- a/clang-tidy/readability/MagicNumbersCheck.cpp +++ b/clang-tidy/readability/MagicNumbersCheck.cpp @@ -66,7 +66,7 @@ MagicNumbersCheck::MagicNumbersCheck(StringRef Name, ClangTidyContext *Context) IgnoredIntegerValues.resize(IgnoredIntegerValuesInput.size()); llvm::transform(IgnoredIntegerValuesInput, IgnoredIntegerValues.begin(), [](const std::string &Value) { return std::stoll(Value); }); - llvm::sort(IgnoredIntegerValues.begin(), IgnoredIntegerValues.end()); + llvm::sort(IgnoredIntegerValues); if (!IgnoreAllFloatingPointValues) { // Process the set of ignored floating point values. From 3776b2420e7297423a0058fb57f923cda4c9028b Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Thu, 27 Sep 2018 04:23:24 +0000 Subject: [PATCH 253/686] [clang-tidy] Add dependency to clangAnalysis after rC343160 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343168 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/mpi/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/clang-tidy/mpi/CMakeLists.txt b/clang-tidy/mpi/CMakeLists.txt index 36584342c..5be7b3655 100644 --- a/clang-tidy/mpi/CMakeLists.txt +++ b/clang-tidy/mpi/CMakeLists.txt @@ -6,6 +6,7 @@ add_clang_library(clangTidyMPIModule TypeMismatchCheck.cpp LINK_LIBS + clangAnalysis clangAST clangASTMatchers clangBasic From 6bc4932654d8718bcf03f7a1f563b3dc52abdcdf Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Thu, 27 Sep 2018 12:12:42 +0000 Subject: [PATCH 254/686] Improve diagnostics range reporting. Summary: If we have some range information coming from clang diagnostic, promote that one even if it doesn't contain diagnostic location inside. Reviewers: sammccall, ioeric Reviewed By: ioeric Subscribers: ilya-biryukov, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D52544 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343197 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/Diagnostics.cpp | 11 +++++++++++ unittests/clangd/ClangdUnitTests.cpp | 12 +++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/clangd/Diagnostics.cpp b/clangd/Diagnostics.cpp index 893854d1d..a2f86eb94 100644 --- a/clangd/Diagnostics.cpp +++ b/clangd/Diagnostics.cpp @@ -52,17 +52,28 @@ Range diagnosticRange(const clang::Diagnostic &D, const LangOptions &L) { auto &M = D.getSourceManager(); auto Loc = M.getFileLoc(D.getLocation()); // Accept the first range that contains the location. + llvm::Optional FallbackRange; for (const auto &CR : D.getRanges()) { auto R = Lexer::makeFileCharRange(CR, M, L); if (locationInRange(Loc, R, M)) return halfOpenToRange(M, R); + // If there are no ranges that contain the location report the first range. + if (!FallbackRange) + FallbackRange = halfOpenToRange(M, R); } // The range may be given as a fixit hint instead. for (const auto &F : D.getFixItHints()) { auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L); if (locationInRange(Loc, R, M)) return halfOpenToRange(M, R); + // If there's a fixit that performs insertion, it has zero-width. Therefore + // it can't contain the location of the diag, but it might be possible that + // this should be reported as range. For example missing semicolon. + if (!FallbackRange && R.getBegin() == R.getEnd()) + FallbackRange = halfOpenToRange(M, R); } + if (FallbackRange) + return *FallbackRange; // If no suitable range is found, just use the token at the location. auto R = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Loc), M, L); if (!R.isValid()) // Fall back to location only, let the editor deal with it. diff --git a/unittests/clangd/ClangdUnitTests.cpp b/unittests/clangd/ClangdUnitTests.cpp index 5e662cb87..48d936632 100644 --- a/unittests/clangd/ClangdUnitTests.cpp +++ b/unittests/clangd/ClangdUnitTests.cpp @@ -79,8 +79,9 @@ TEST(DiagnosticsTest, DiagnosticRanges) { int main() { $typo[[go\ o]](); - foo()$semicolon[[]] + foo()$semicolon[[]]//with comments $unk[[unknown]](); + double bar = $type[["foo"]]; } )cpp"); EXPECT_THAT( @@ -93,11 +94,16 @@ o]](); Fix(Test.range("typo"), "foo", "change 'go\\ o' to 'foo'")), // This is a pretty normal range. WithNote(Diag(Test.range("decl"), "'foo' declared here"))), - // This range is zero-width, and at the end of a line. + // This range is zero-width and insertion. Therefore make sure we are + // not expanding it into other tokens. Since we are not going to + // replace those. AllOf(Diag(Test.range("semicolon"), "expected ';' after expression"), WithFix(Fix(Test.range("semicolon"), ";", "insert ';'"))), // This range isn't provided by clang, we expand to the token. - Diag(Test.range("unk"), "use of undeclared identifier 'unknown'"))); + Diag(Test.range("unk"), "use of undeclared identifier 'unknown'"), + Diag(Test.range("type"), + "cannot initialize a variable of type 'double' with an lvalue " + "of type 'const char [4]'"))); } TEST(DiagnosticsTest, FlagsMatter) { From 4b1b3c36c5862d180df751d6d10657d6b084a900 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Thu, 27 Sep 2018 12:17:59 +0000 Subject: [PATCH 255/686] [clang-tidy] use CHECK-NOTES in tests for bugprone-forward-declaration-namespace Reviewers: aaron.ballman, alexfh, hokein Subscribers: xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D52185 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343198 91177308-0d34-0410-b5e6-96231b3b80d8 --- ...bugprone-forward-declaration-namespace.cpp | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/test/clang-tidy/bugprone-forward-declaration-namespace.cpp b/test/clang-tidy/bugprone-forward-declaration-namespace.cpp index d93814868..e0b225877 100644 --- a/test/clang-tidy/bugprone-forward-declaration-namespace.cpp +++ b/test/clang-tidy/bugprone-forward-declaration-namespace.cpp @@ -3,19 +3,19 @@ namespace { // This is a declaration in a wrong namespace. class T_A; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_A' is never referenced, but a declaration with the same name found in another namespace 'na' [bugprone-forward-declaration-namespace] -// CHECK-MESSAGES: note: a declaration of 'T_A' is found here -// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_A', but a definition with the same name 'T_A' found in another namespace '(global)' [bugprone-forward-declaration-namespace] -// CHECK-MESSAGES: note: a definition of 'T_A' is found here +// CHECK-NOTES: :[[@LINE-1]]:7: warning: declaration 'T_A' is never referenced, but a declaration with the same name found in another namespace 'na' [bugprone-forward-declaration-namespace] +// CHECK-NOTES: note: a declaration of 'T_A' is found here +// CHECK-NOTES: :[[@LINE-3]]:7: warning: no definition found for 'T_A', but a definition with the same name 'T_A' found in another namespace '(global)' [bugprone-forward-declaration-namespace] +// CHECK-NOTES: note: a definition of 'T_A' is found here } namespace na { // This is a declaration in a wrong namespace. class T_A; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_A' is never referenced, but a declaration with the same name found in another namespace '(anonymous)' -// CHECK-MESSAGES: note: a declaration of 'T_A' is found here -// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_A', but a definition with the same name 'T_A' found in another namespace '(global)' -// CHECK-MESSAGES: note: a definition of 'T_A' is found here +// CHECK-NOTES: :[[@LINE-1]]:7: warning: declaration 'T_A' is never referenced, but a declaration with the same name found in another namespace '(anonymous)' +// CHECK-NOTES: note: a declaration of 'T_A' is found here +// CHECK-NOTES: :[[@LINE-3]]:7: warning: no definition found for 'T_A', but a definition with the same name 'T_A' found in another namespace '(global)' +// CHECK-NOTES: note: a definition of 'T_A' is found here } class T_A; @@ -25,8 +25,8 @@ class T_A { }; class NESTED; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: no definition found for 'NESTED', but a definition with the same name 'NESTED' found in another namespace '(anonymous namespace)::nq::(anonymous)' -// CHECK-MESSAGES: note: a definition of 'NESTED' is found here +// CHECK-NOTES: :[[@LINE-1]]:7: warning: no definition found for 'NESTED', but a definition with the same name 'NESTED' found in another namespace '(anonymous namespace)::nq::(anonymous)' +// CHECK-NOTES: note: a definition of 'NESTED' is found here namespace { namespace nq { @@ -38,10 +38,10 @@ class NESTED {}; namespace na { class T_B; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_B' is never referenced, but a declaration with the same name found in another namespace 'nb' -// CHECK-MESSAGES: note: a declaration of 'T_B' is found here -// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_B', but a definition with the same name 'T_B' found in another namespace 'nb' -// CHECK-MESSAGES: note: a definition of 'T_B' is found here +// CHECK-NOTES: :[[@LINE-1]]:7: warning: declaration 'T_B' is never referenced, but a declaration with the same name found in another namespace 'nb' +// CHECK-NOTES: note: a declaration of 'T_B' is found here +// CHECK-NOTES: :[[@LINE-3]]:7: warning: no definition found for 'T_B', but a definition with the same name 'T_B' found in another namespace 'nb' +// CHECK-NOTES: note: a definition of 'T_B' is found here } namespace nb { @@ -56,10 +56,10 @@ class T_B { namespace na { class T_B; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_B' is never referenced, but a declaration with the same name found in another namespace 'nb' -// CHECK-MESSAGES: note: a declaration of 'T_B' is found here -// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_B', but a definition with the same name 'T_B' found in another namespace 'nb' -// CHECK-MESSAGES: note: a definition of 'T_B' is found here +// CHECK-NOTES: :[[@LINE-1]]:7: warning: declaration 'T_B' is never referenced, but a declaration with the same name found in another namespace 'nb' +// CHECK-NOTES: note: a declaration of 'T_B' is found here +// CHECK-NOTES: :[[@LINE-3]]:7: warning: no definition found for 'T_B', but a definition with the same name 'T_B' found in another namespace 'nb' +// CHECK-NOTES: note: a definition of 'T_B' is found here } // A simple forward declaration. Although it is never used, but no declaration @@ -89,8 +89,8 @@ void f(OUTSIDER_1 *); namespace nb { class OUTSIDER_1; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'OUTSIDER_1' is never referenced, but a declaration with the same name found in another namespace 'na' -// CHECK-MESSAGES: note: a declaration of 'OUTSIDER_1' is found here +// CHECK-NOTES: :[[@LINE-1]]:7: warning: declaration 'OUTSIDER_1' is never referenced, but a declaration with the same name found in another namespace 'na' +// CHECK-NOTES: note: a declaration of 'OUTSIDER_1' is found here } @@ -139,8 +139,8 @@ extern template class T_TEMP_1; namespace nd { class D; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'D' is never referenced, but a declaration with the same name found in another namespace 'nd::ne' -// CHECK-MESSAGES: note: a declaration of 'D' is found here +// CHECK-NOTES: :[[@LINE-1]]:7: warning: declaration 'D' is never referenced, but a declaration with the same name found in another namespace 'nd::ne' +// CHECK-NOTES: note: a declaration of 'D' is found here } namespace nd { From 4518b975811e6b048e0d4b171bb9258364cd7936 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Thu, 27 Sep 2018 12:22:48 +0000 Subject: [PATCH 256/686] [clang-tidy] use CHECK-NOTES in tests for bugprone-use-after-move Reviewers: alexfh, aaron.ballman, hokein Subscribers: xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D52228 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343199 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clang-tidy/bugprone-use-after-move.cpp | 220 ++++++++++---------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/test/clang-tidy/bugprone-use-after-move.cpp b/test/clang-tidy/bugprone-use-after-move.cpp index e4596c8ea..ae1f021e0 100644 --- a/test/clang-tidy/bugprone-use-after-move.cpp +++ b/test/clang-tidy/bugprone-use-after-move.cpp @@ -125,8 +125,8 @@ void simple() { a.foo(); A other_a = std::move(a); a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:15: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:15: note: move occurred here } // A warning should only be emitted for one use-after-move. @@ -135,8 +135,8 @@ void onlyFlagOneUseAfterMove() { a.foo(); A other_a = std::move(a); a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:15: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:15: note: move occurred here a.foo(); } @@ -146,8 +146,8 @@ void moveAfterMove() { A a; std::move(a); std::move(a); - // CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:15: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } // This is also true if the move itself turns into the use on the second loop // iteration. @@ -155,9 +155,9 @@ void moveAfterMove() { A a; for (int i = 0; i < 10; ++i) { std::move(a); - // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:7: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:17: note: the use happens in a later loop + // CHECK-NOTES: [[@LINE-1]]:17: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:7: note: move occurred here + // CHECK-NOTES: [[@LINE-3]]:17: note: the use happens in a later loop } } } @@ -166,8 +166,8 @@ void moveAfterMove() { void parameters(A a) { std::move(a); a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here } void standardSmartPtr() { @@ -180,22 +180,22 @@ void standardSmartPtr() { ptr.get(); static_cast(ptr); *ptr; - // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved - // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved + // CHECK-NOTES: [[@LINE-5]]:5: note: move occurred here } { std::unique_ptr ptr; std::move(ptr); ptr->foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } { std::unique_ptr ptr; std::move(ptr); ptr[0]; - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } { std::shared_ptr ptr; @@ -203,15 +203,15 @@ void standardSmartPtr() { ptr.get(); static_cast(ptr); *ptr; - // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved - // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved + // CHECK-NOTES: [[@LINE-5]]:5: note: move occurred here } { std::shared_ptr ptr; std::move(ptr); ptr->foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } { // std::weak_ptr<> cannot be dereferenced directly, so we only check that @@ -252,8 +252,8 @@ void standardSmartPtr() { } ptr; std::move(ptr); ptr.get(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } } @@ -263,8 +263,8 @@ class Container { A a; std::move(a); a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } }; @@ -273,8 +273,8 @@ void moveInDeclaration() { A a; A another_a(std::move(a)); a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } // We see the std::move if it's inside an initializer list. Initializer lists @@ -290,8 +290,8 @@ void moveInInitList() { A a; S s{std::move(a)}; a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:7: note: move occurred here } void lambdas() { @@ -301,8 +301,8 @@ void lambdas() { auto lambda = [a] { std::move(a); a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:7: note: move occurred here }; } // This is just as true if the variable was declared inside the lambda. @@ -311,8 +311,8 @@ void lambdas() { A a; std::move(a); a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:7: note: move occurred here }; } // But don't warn if the move happened inside the lambda but the use happened @@ -331,31 +331,31 @@ void lambdas() { A a; std::move(a); auto lambda = [a]() { a.foo(); }; - // CHECK-MESSAGES: [[@LINE-1]]:20: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:20: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } // ...even if the capture was implicit. { A a; std::move(a); auto lambda = [=]() { a.foo(); }; - // CHECK-MESSAGES: [[@LINE-1]]:20: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:20: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } // Same tests but for capture by reference. { A a; std::move(a); auto lambda = [&a]() { a.foo(); }; - // CHECK-MESSAGES: [[@LINE-1]]:21: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:21: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } { A a; std::move(a); auto lambda = [&]() { a.foo(); }; - // CHECK-MESSAGES: [[@LINE-1]]:20: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:20: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } // But don't warn if the move happened after the capture. { @@ -390,8 +390,8 @@ void movedTypeIsNotDependentType() { A a; std::move(a); a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here } // And if the moved type is a dependent type, the use-after-move is detected if @@ -401,8 +401,8 @@ void movedTypeIsDependentType() { T t; std::move(t); t.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 't' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:3: warning: 't' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here } template void movedTypeIsDependentType(); @@ -417,8 +417,8 @@ void implicitConversionOperator() { Convertible convertible; takeA(std::move(convertible)); convertible; - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'convertible' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:9: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:3: warning: 'convertible' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:9: note: move occurred here } // Using decltype on an expression is not a use. @@ -480,9 +480,9 @@ void useAndMoveInLoop() { A a; for (int i = 0; i < 10; ++i) { a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE+2]]:7: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:7: note: the use happens in a later loop + // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE+2]]:7: note: move occurred here + // CHECK-NOTES: [[@LINE-3]]:7: note: the use happens in a later loop std::move(a); } } @@ -559,8 +559,8 @@ void differentBranches(int i) { std::move(a); case 2: a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-4]]:7: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-4]]:7: note: move occurred here break; } } @@ -575,8 +575,8 @@ void mutuallyExclusiveBranchesFalsePositive(bool b) { } if (!b) { a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-5]]:5: note: move occurred here } } @@ -658,8 +658,8 @@ void assignments(int i) { a = A(); std::move(a); a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } // Report a use-after-move if we can't be sure that the variable was assigned // to. @@ -671,8 +671,8 @@ void assignments(int i) { } if (i > 5) { a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-7]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-7]]:5: note: move occurred here } } } @@ -708,14 +708,14 @@ void passByConstPointerIsUse() { const A a; std::move(a); passByConstPointer(&a); - // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:25: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } const A a; std::move(a); passByConstReference(a); - // CHECK-MESSAGES: [[@LINE-1]]:24: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:24: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here } // Clearing a standard container using clear() is treated as a @@ -820,8 +820,8 @@ void standardContainerClearIsReinit() { } container; std::move(container); container.clear(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container' used after it was - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'container' used after it was + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } // An intervening clear() on a different container does not reinitialize. { @@ -829,8 +829,8 @@ void standardContainerClearIsReinit() { std::move(container1); container2.clear(); container1.empty(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container1' used after it was - // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'container1' used after it was + // CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here } } @@ -875,8 +875,8 @@ void standardContainerAssignIsReinit() { } container; std::move(container); container.assign(0, 0); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container' used after it was - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'container' used after it was + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } // An intervening assign() on a different container does not reinitialize. { @@ -884,8 +884,8 @@ void standardContainerAssignIsReinit() { std::move(container1); container2.assign(0, 0); container1.empty(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container1' used after it was - // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'container1' used after it was + // CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here } } @@ -912,8 +912,8 @@ void reinitAnnotation() { AnnotatedContainer obj; std::move(obj); obj.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'obj' used after it was - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'obj' used after it was + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } { AnnotatedContainer obj; @@ -928,8 +928,8 @@ void reinitAnnotation() { std::move(obj1); obj2.clear(); obj1.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'obj1' used after it was - // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'obj1' used after it was + // CHECK-NOTES: [[@LINE-4]]:5: note: move occurred here } } @@ -962,27 +962,27 @@ void sequencingOfMoveAndUse() { { A a; passByValue(a.getInt(), std::move(a)); - // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:29: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:17: note: the use and move are unsequenced + // CHECK-NOTES: [[@LINE-1]]:17: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:29: note: move occurred here + // CHECK-NOTES: [[@LINE-3]]:17: note: the use and move are unsequenced } { A a; passByValue(std::move(a), a.getInt()); - // CHECK-MESSAGES: [[@LINE-1]]:31: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:17: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:31: note: the use and move are unsequenced + // CHECK-NOTES: [[@LINE-1]]:31: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:17: note: move occurred here + // CHECK-NOTES: [[@LINE-3]]:31: note: the use and move are unsequenced } // An even more convoluted example. { A a; g(g(a, std::move(a)), g(a, std::move(a))); - // CHECK-MESSAGES: [[@LINE-1]]:9: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:27: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:9: note: the use and move are unsequenced - // CHECK-MESSAGES: [[@LINE-4]]:29: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-5]]:7: note: move occurred here - // CHECK-MESSAGES: [[@LINE-6]]:29: note: the use and move are unsequenced + // CHECK-NOTES: [[@LINE-1]]:9: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:27: note: move occurred here + // CHECK-NOTES: [[@LINE-3]]:9: note: the use and move are unsequenced + // CHECK-NOTES: [[@LINE-4]]:29: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-5]]:7: note: move occurred here + // CHECK-NOTES: [[@LINE-6]]:29: note: the use and move are unsequenced } // This case is fine because the actual move only happens inside the call to // operator=(). a.getInt(), by necessity, is evaluated before that call. @@ -997,17 +997,17 @@ void sequencingOfMoveAndUse() { A a; int v[3]; v[a.getInt()] = intFromA(std::move(a)); - // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:21: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:7: note: the use and move are unsequenced + // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:21: note: move occurred here + // CHECK-NOTES: [[@LINE-3]]:7: note: the use and move are unsequenced } { A a; int v[3]; v[intFromA(std::move(a))] = intFromInt(a.i); - // CHECK-MESSAGES: [[@LINE-1]]:44: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:7: note: move occurred here - // CHECK-MESSAGES: [[@LINE-3]]:44: note: the use and move are unsequenced + // CHECK-NOTES: [[@LINE-1]]:44: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:7: note: move occurred here + // CHECK-NOTES: [[@LINE-3]]:44: note: the use and move are unsequenced } } @@ -1023,15 +1023,15 @@ void sequencingOfMoveAndReinit() { A a; passByValue(std::move(a), (a = A())); a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:17: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:17: note: move occurred here } { A a; passByValue((a = A()), std::move(a)); a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:28: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:28: note: move occurred here } // Common usage pattern: Move the object to a function that mutates it in some // way, then reassign the result to the object. This pattern is fine. @@ -1053,15 +1053,15 @@ void sequencingOfReinitAndUse() { A a; std::move(a); passByValue(a.getInt(), (a = A())); - // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:17: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } { A a; std::move(a); passByValue((a = A()), a.getInt()); - // CHECK-MESSAGES: [[@LINE-1]]:28: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:28: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here } } @@ -1077,8 +1077,8 @@ void commaOperatorSequences() { A a; (a = A()), A(std::move(a)); a.foo(); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-3]]:16: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:16: note: move occurred here } } @@ -1099,8 +1099,8 @@ void initializerListSequences() { }; A a; S2 s2{std::move(a), a.getInt()}; - // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:11: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:25: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here } } @@ -1114,8 +1114,8 @@ void declarationSequences() { { A a; A a1 = std::move(a), a2 = a; - // CHECK-MESSAGES: [[@LINE-1]]:31: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:12: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:31: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:12: note: move occurred here } } @@ -1138,8 +1138,8 @@ void logicalOperatorsSequence() { { A a; if (A(std::move(a)).getInt() > 0 && a.getInt() > 0) { - // CHECK-MESSAGES: [[@LINE-1]]:41: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:9: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:41: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:9: note: move occurred here A().foo(); } } @@ -1152,8 +1152,8 @@ void logicalOperatorsSequence() { { A a; if (A(std::move(a)).getInt() > 0 || a.getInt() > 0) { - // CHECK-MESSAGES: [[@LINE-1]]:41: warning: 'a' used after it was moved - // CHECK-MESSAGES: [[@LINE-2]]:9: note: move occurred here + // CHECK-NOTES: [[@LINE-1]]:41: warning: 'a' used after it was moved + // CHECK-NOTES: [[@LINE-2]]:9: note: move occurred here A().foo(); } } From c31a1ffce9ec6febcc46d23628290ac1f094ca98 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Thu, 27 Sep 2018 12:30:44 +0000 Subject: [PATCH 257/686] [clang-tidy] use CHECK-NOTES in tests for bugprone suspicious-enum-usage Reviewers: alexfh, aaron.ballman, hokein Subscribers: xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D52229 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343201 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../bugprone-suspicious-enum-usage-strict.cpp | 20 +++++++++---------- .../bugprone-suspicious-enum-usage.cpp | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/clang-tidy/bugprone-suspicious-enum-usage-strict.cpp b/test/clang-tidy/bugprone-suspicious-enum-usage-strict.cpp index d9ed8cede..cf0ec598d 100644 --- a/test/clang-tidy/bugprone-suspicious-enum-usage-strict.cpp +++ b/test/clang-tidy/bugprone-suspicious-enum-usage-strict.cpp @@ -10,23 +10,23 @@ enum A { G = 63 }; -// CHECK-MESSAGES: :[[@LINE+2]]:1: warning: enum type seems like a bitmask (contains mostly power-of-2 literals) but a literal is not power-of-2 -// CHECK-MESSAGES: :76:7: note: used here as a bitmask +// CHECK-NOTES: :[[@LINE+2]]:1: warning: enum type seems like a bitmask (contains mostly power-of-2 literals) but a literal is not power-of-2 +// CHECK-NOTES: :76:7: note: used here as a bitmask enum X { X = 8, Y = 16, Z = 4, ZZ = 3 - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: enum type seems like a bitmask (contains mostly power-of-2 literals), but this literal is not a power-of-2 [bugprone-suspicious-enum-usage] -// CHECK-MESSAGES: :70:13: note: used here as a bitmask + // CHECK-NOTES: :[[@LINE-1]]:3: warning: enum type seems like a bitmask (contains mostly power-of-2 literals), but this literal is not a power-of-2 [bugprone-suspicious-enum-usage] +// CHECK-NOTES: :70:13: note: used here as a bitmask }; -// CHECK-MESSAGES: :[[@LINE+2]]:1: warning: enum type seems like a bitmask (contains mostly power-of-2 literals) but some literals are not power-of-2 -// CHECK-MESSAGES: :73:8: note: used here as a bitmask +// CHECK-NOTES: :[[@LINE+2]]:1: warning: enum type seems like a bitmask (contains mostly power-of-2 literals) but some literals are not power-of-2 +// CHECK-NOTES: :73:8: note: used here as a bitmask enum PP { P = 2, Q = 3, - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: enum type seems like a bitmask (contains mostly power-of-2 literals), but this literal is not a power-of-2 - // CHECK-MESSAGES: :65:11: note: used here as a bitmask + // CHECK-NOTES: :[[@LINE-1]]:3: warning: enum type seems like a bitmask (contains mostly power-of-2 literals), but this literal is not a power-of-2 + // CHECK-NOTES: :65:11: note: used here as a bitmask R = 4, S = 8, T = 16, @@ -58,10 +58,10 @@ Days bestDay() { int trigger() { if (bestDay() | A) return 1; - // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: enum values are from different enum types + // CHECK-NOTES: :[[@LINE-2]]:17: warning: enum values are from different enum types if (I | Y) return 1; - // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: enum values are from different enum types + // CHECK-NOTES: :[[@LINE-2]]:9: warning: enum values are from different enum types if (P + Q == R) return 1; else if ((S | R) == T) diff --git a/test/clang-tidy/bugprone-suspicious-enum-usage.cpp b/test/clang-tidy/bugprone-suspicious-enum-usage.cpp index e1a54d459..314e43424 100644 --- a/test/clang-tidy/bugprone-suspicious-enum-usage.cpp +++ b/test/clang-tidy/bugprone-suspicious-enum-usage.cpp @@ -54,10 +54,10 @@ int trigger() { int emptytest = EmptyVal | B; if (bestDay() | A) return 1; - // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: enum values are from different enum types + // CHECK-NOTES: :[[@LINE-2]]:17: warning: enum values are from different enum types if (I | Y) return 1; - // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: enum values are from different enum types + // CHECK-NOTES: :[[@LINE-2]]:9: warning: enum values are from different enum types } int dont_trigger() { From 46cc53f04bafc1c789977e43f6b4bd1e8d2905fa Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Thu, 27 Sep 2018 14:21:07 +0000 Subject: [PATCH 258/686] Tell whether file/folder for include completions. Reviewers: sammccall Reviewed By: sammccall Subscribers: ilya-biryukov, ioeric, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D52547 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343221 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 5 +++++ clangd/Protocol.h | 7 +++++++ unittests/clangd/CodeCompleteTests.cpp | 21 +++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 2328fc601..e43389ba8 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -349,6 +349,11 @@ struct CodeCompletionBuilder { } Completion.Kind = toCompletionItemKind( C.SemaResult->Kind, C.SemaResult->Declaration, ContextKind); + // Sema could provide more info on whether the completion was a file or + // folder. + if (Completion.Kind == CompletionItemKind::File && + Completion.Name.back() == '/') + Completion.Kind = CompletionItemKind::Folder; for (const auto &FixIt : C.SemaResult->FixIts) { Completion.FixIts.push_back( toTextEdit(FixIt, ASTCtx.getSourceManager(), ASTCtx.getLangOpts())); diff --git a/clangd/Protocol.h b/clangd/Protocol.h index f09a63c95..5e77e8b6a 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -704,6 +704,13 @@ enum class CompletionItemKind { Color = 16, File = 17, Reference = 18, + Folder = 19, + EnumMember = 20, + Constant = 21, + Struct = 22, + Event = 23, + Operator = 24, + TypeParameter = 25, }; /// Defines whether the insert text in a completion item should be interpreted diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index c7e387a34..fc707baf2 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -2073,6 +2073,27 @@ TEST(SignatureHelpTest, ConstructorInitializeFields) { } } +TEST(CompletionTest, IncludedCompletionKinds) { + MockFSProvider FS; + MockCompilationDatabase CDB; + std::string Subdir = testPath("sub"); + std::string SearchDirArg = (llvm::Twine("-I") + Subdir).str(); + CDB.ExtraClangFlags = {SearchDirArg.c_str()}; + std::string BarHeader = testPath("sub/bar.h"); + FS.Files[BarHeader] = ""; + IgnoreDiagnostics DiagConsumer; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + auto Results = completions(Server, + R"cpp( + #include "^" + )cpp" + ); + EXPECT_THAT(Results.Completions, + AllOf(Has("sub/", CompletionItemKind::Folder), + Has("bar.h\"", CompletionItemKind::File))); +} + + } // namespace } // namespace clangd } // namespace clang From ce04ed3fa312d77e798765e02a2723ed58b70b21 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Thu, 27 Sep 2018 14:27:02 +0000 Subject: [PATCH 259/686] [clangd] Make IncludeInserter less slow. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343223 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/Headers.cpp | 12 ++++++------ clangd/Headers.h | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clangd/Headers.cpp b/clangd/Headers.cpp index 137b29aa1..f42f204c1 100644 --- a/clangd/Headers.cpp +++ b/clangd/Headers.cpp @@ -126,6 +126,12 @@ IncludeStructure::includeDepth(llvm::StringRef Root) const { return Result; } +void IncludeInserter::addExisting(const Inclusion &Inc) { + IncludedHeaders.insert(Inc.Written); + if (!Inc.Resolved.empty()) + IncludedHeaders.insert(Inc.Resolved); +} + /// FIXME(ioeric): we might not want to insert an absolute include path if the /// path is not shortened. bool IncludeInserter::shouldInsertInclude( @@ -133,12 +139,6 @@ bool IncludeInserter::shouldInsertInclude( assert(DeclaringHeader.valid() && InsertedHeader.valid()); if (FileName == DeclaringHeader.File || FileName == InsertedHeader.File) return false; - llvm::StringSet<> IncludedHeaders; - for (const auto &Inc : Inclusions) { - IncludedHeaders.insert(Inc.Written); - if (!Inc.Resolved.empty()) - IncludedHeaders.insert(Inc.Resolved); - } auto Included = [&](llvm::StringRef Header) { return IncludedHeaders.find(Header) != IncludedHeaders.end(); }; diff --git a/clangd/Headers.h b/clangd/Headers.h index 2abf27c60..c140a65a8 100644 --- a/clangd/Headers.h +++ b/clangd/Headers.h @@ -97,7 +97,7 @@ class IncludeInserter { HeaderSearchInfo(HeaderSearchInfo), Inserter(FileName, Code, Style.IncludeStyle) {} - void addExisting(Inclusion Inc) { Inclusions.push_back(std::move(Inc)); } + void addExisting(const Inclusion &Inc); /// Checks whether to add an #include of the header into \p File. /// An #include will not be added if: @@ -134,8 +134,8 @@ class IncludeInserter { StringRef Code; StringRef BuildDir; HeaderSearch &HeaderSearchInfo; - std::vector Inclusions; - tooling::HeaderIncludes Inserter; // Computers insertion replacement. + llvm::StringSet<> IncludedHeaders; // Both written and resolved. + tooling::HeaderIncludes Inserter; // Computers insertion replacement. }; } // namespace clangd From f3e8ad08560c9d1a8b73044f120c2436e8238a20 Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Thu, 27 Sep 2018 17:13:07 +0000 Subject: [PATCH 260/686] Introduce completionItemKind capability support. Reviewers: sammccall Reviewed By: sammccall Subscribers: ilya-biryukov, ioeric, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D52616 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343237 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 26 ++++++++++-- clangd/ClangdLSPServer.h | 2 + clangd/Protocol.cpp | 51 ++++++++++++++++++++++ clangd/Protocol.h | 87 ++++++++++++++++++++++++-------------- 4 files changed, 131 insertions(+), 35 deletions(-) diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index a4ebf575d..c6ce3ef9b 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -70,6 +70,14 @@ SymbolKindBitset defaultSymbolKinds() { return Defaults; } +CompletionItemKindBitset defaultCompletionItemKinds() { + CompletionItemKindBitset Defaults; + for (size_t I = CompletionItemKindMin; + I <= static_cast(CompletionItemKind::Reference); ++I) + Defaults.set(I); + return Defaults; +} + } // namespace void ClangdLSPServer::onInitialize(InitializeParams &Params) { @@ -89,13 +97,20 @@ void ClangdLSPServer::onInitialize(InitializeParams &Params) { Params.capabilities.textDocument.publishDiagnostics.categorySupport; if (Params.capabilities.workspace && Params.capabilities.workspace->symbol && - Params.capabilities.workspace->symbol->symbolKind) { + Params.capabilities.workspace->symbol->symbolKind && + Params.capabilities.workspace->symbol->symbolKind->valueSet) { for (SymbolKind Kind : *Params.capabilities.workspace->symbol->symbolKind->valueSet) { SupportedSymbolKinds.set(static_cast(Kind)); } } + if (Params.capabilities.textDocument.completion.completionItemKind && + Params.capabilities.textDocument.completion.completionItemKind->valueSet) + for (CompletionItemKind Kind : *Params.capabilities.textDocument.completion + .completionItemKind->valueSet) + SupportedCompletionItemKinds.set(static_cast(Kind)); + reply(json::Object{ {{"capabilities", json::Object{ @@ -347,8 +362,12 @@ void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) { return replyError(List.takeError()); CompletionList LSPList; LSPList.isIncomplete = List->HasMore; - for (const auto &R : List->Completions) - LSPList.items.push_back(R.render(CCOpts)); + for (const auto &R : List->Completions) { + CompletionItem C = R.render(CCOpts); + C.kind = adjustKindToCapability( + C.kind, SupportedCompletionItemKinds); + LSPList.items.push_back(std::move(C)); + } return reply(std::move(LSPList)); }); } @@ -459,6 +478,7 @@ ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, : CompilationDB::makeDirectoryBased( std::move(CompileCommandsDir))), CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()), + SupportedCompletionItemKinds(defaultCompletionItemKinds()), Server(new ClangdServer(CDB.getCDB(), FSProvider, /*DiagConsumer=*/*this, Opts)) {} diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h index f39fdb61c..f77b24b9b 100644 --- a/clangd/ClangdLSPServer.h +++ b/clangd/ClangdLSPServer.h @@ -164,6 +164,8 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { ClangdDiagnosticOptions DiagOpts; /// The supported kinds of the client. SymbolKindBitset SupportedSymbolKinds; + /// The supported completion item kinds of the client. + CompletionItemKindBitset SupportedCompletionItemKinds; // Store of the current versions of the open documents. DraftStore DraftMgr; diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp index e6ffe66a1..7a137eb6b 100644 --- a/clangd/Protocol.cpp +++ b/clangd/Protocol.cpp @@ -496,6 +496,57 @@ json::Value toJSON(const Hover &H) { return std::move(Result); } +bool fromJSON(const json::Value &E, CompletionItemKind &Out) { + if (auto T = E.getAsInteger()) { + if (*T < static_cast(CompletionItemKind::Text) || + *T > static_cast(CompletionItemKind::TypeParameter)) + return false; + Out = static_cast(*T); + return true; + } + return false; +} + +CompletionItemKind +adjustKindToCapability(CompletionItemKind Kind, + CompletionItemKindBitset &supportedCompletionItemKinds) { + auto KindVal = static_cast(Kind); + if (KindVal >= CompletionItemKindMin && + KindVal <= supportedCompletionItemKinds.size() && + supportedCompletionItemKinds[KindVal]) + return Kind; + + switch (Kind) { + // Provide some fall backs for common kinds that are close enough. + case CompletionItemKind::Folder: + return CompletionItemKind::File; + case CompletionItemKind::EnumMember: + return CompletionItemKind::Enum; + case CompletionItemKind::Struct: + return CompletionItemKind::Class; + default: + return CompletionItemKind::Text; + } +} + +bool fromJSON(const json::Value &E, std::vector &Out) { + if (auto *A = E.getAsArray()) { + Out.clear(); + for (size_t I = 0; I < A->size(); ++I) { + CompletionItemKind KindOut; + if (fromJSON((*A)[I], KindOut)) + Out.push_back(KindOut); + } + return true; + } + return false; +} + +bool fromJSON(const json::Value &Params, CompletionItemKindCapabilities &R) { + json::ObjectMapper O(Params); + return O && O.map("valueSet", R.valueSet); +} + json::Value toJSON(const CompletionItem &CI) { assert(!CI.label.empty() && "completion item label is required"); json::Object Result{{"label", CI.label}}; diff --git a/clangd/Protocol.h b/clangd/Protocol.h index 5e77e8b6a..22da2e3af 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -233,13 +233,65 @@ struct CompletionItemClientCapabilities { }; bool fromJSON(const llvm::json::Value &, CompletionItemClientCapabilities &); +/// The kind of a completion entry. +enum class CompletionItemKind { + Missing = 0, + Text = 1, + Method = 2, + Function = 3, + Constructor = 4, + Field = 5, + Variable = 6, + Class = 7, + Interface = 8, + Module = 9, + Property = 10, + Unit = 11, + Value = 12, + Enum = 13, + Keyword = 14, + Snippet = 15, + Color = 16, + File = 17, + Reference = 18, + Folder = 19, + EnumMember = 20, + Constant = 21, + Struct = 22, + Event = 23, + Operator = 24, + TypeParameter = 25, +}; +bool fromJSON(const llvm::json::Value &, CompletionItemKind &); + +struct CompletionItemKindCapabilities { + /// The CompletionItemKinds that the client supports. If not set, the client + /// only supports <= CompletionItemKind::Reference and will not fall back to a + /// valid default value. + llvm::Optional> valueSet; +}; +// Discards unknown CompletionItemKinds. +bool fromJSON(const llvm::json::Value &, std::vector &); +bool fromJSON(const llvm::json::Value &, CompletionItemKindCapabilities &); + +constexpr auto CompletionItemKindMin = + static_cast(CompletionItemKind::Text); +constexpr auto CompletionItemKindMax = + static_cast(CompletionItemKind::TypeParameter); +using CompletionItemKindBitset = std::bitset; +CompletionItemKind +adjustKindToCapability(CompletionItemKind Kind, + CompletionItemKindBitset &supportedCompletionItemKinds); + struct CompletionClientCapabilities { /// Whether completion supports dynamic registration. bool dynamicRegistration = false; /// The client supports the following `CompletionItem` specific capabilities. CompletionItemClientCapabilities completionItem; - // NOTE: not used by clangd at the moment. - // llvm::Optional completionItemKind; + /// The CompletionItemKinds that the client supports. If not set, the client + /// only supports <= CompletionItemKind::Reference and will not fall back to a + /// valid default value. + llvm::Optional completionItemKind; /// The client supports to send additional context information for a /// `textDocument/completion` request. @@ -305,6 +357,7 @@ struct SymbolKindCapabilities { /// value. llvm::Optional> valueSet; }; +// Discards unknown SymbolKinds. bool fromJSON(const llvm::json::Value &, std::vector &); bool fromJSON(const llvm::json::Value &, SymbolKindCapabilities &); SymbolKind adjustKindToCapability(SymbolKind Kind, @@ -683,36 +736,6 @@ struct Hover { }; llvm::json::Value toJSON(const Hover &H); -/// The kind of a completion entry. -enum class CompletionItemKind { - Missing = 0, - Text = 1, - Method = 2, - Function = 3, - Constructor = 4, - Field = 5, - Variable = 6, - Class = 7, - Interface = 8, - Module = 9, - Property = 10, - Unit = 11, - Value = 12, - Enum = 13, - Keyword = 14, - Snippet = 15, - Color = 16, - File = 17, - Reference = 18, - Folder = 19, - EnumMember = 20, - Constant = 21, - Struct = 22, - Event = 23, - Operator = 24, - TypeParameter = 25, -}; - /// Defines whether the insert text in a completion item should be interpreted /// as plain text or a snippet. enum class InsertTextFormat { From 1647dd7a02e2f8791398f73f444cdf8bcf42122b Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Thu, 27 Sep 2018 18:23:23 +0000 Subject: [PATCH 261/686] [clangd] Add more tracing to index queries. NFC Reviewers: sammccall Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52611 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343247 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/MemIndex.cpp | 8 +++++++- clangd/index/Merge.cpp | 17 ++++++++++++++++- clangd/index/dex/Dex.cpp | 5 +++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index 546902206..d39b86f23 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -11,6 +11,7 @@ #include "FuzzyMatch.h" #include "Logger.h" #include "Quality.h" +#include "Trace.h" namespace clang { namespace clangd { @@ -28,6 +29,7 @@ bool MemIndex::fuzzyFind( llvm::function_ref Callback) const { assert(!StringRef(Req.Query).contains("::") && "There must be no :: in query."); + trace::Span Tracer("MemIndex fuzzyFind"); TopN> Top( Req.Limit ? *Req.Limit : std::numeric_limits::max()); @@ -47,13 +49,16 @@ bool MemIndex::fuzzyFind( if (Top.push({*Score * quality(*Sym), Sym})) More = true; // An element with smallest score was discarded. } - for (const auto &Item : std::move(Top).items()) + auto Results = std::move(Top).items(); + SPAN_ATTACH(Tracer, "results", static_cast(Results.size())); + for (const auto &Item : Results) Callback(*Item.second); return More; } void MemIndex::lookup(const LookupRequest &Req, llvm::function_ref Callback) const { + trace::Span Tracer("MemIndex lookup"); for (const auto &ID : Req.IDs) { auto I = Index.find(ID); if (I != Index.end()) @@ -63,6 +68,7 @@ void MemIndex::lookup(const LookupRequest &Req, void MemIndex::refs(const RefsRequest &Req, llvm::function_ref Callback) const { + trace::Span Tracer("MemIndex refs"); for (const auto &ReqID : Req.IDs) { auto SymRefs = Refs.find(ReqID); if (SymRefs == Refs.end()) diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index 89323c14b..9cca9da22 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -9,6 +9,7 @@ #include "Merge.h" #include "Logger.h" +#include "Trace.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/raw_ostream.h" @@ -38,19 +39,31 @@ class MergedIndex : public SymbolIndex { // a) if it's not in the dynamic slab, yield it directly // b) if it's in the dynamic slab, merge it and yield the result // 3) now yield all the dynamic symbols we haven't processed. + trace::Span Tracer("MergedIndex fuzzyFind"); bool More = false; // We'll be incomplete if either source was. SymbolSlab::Builder DynB; - More |= Dynamic->fuzzyFind(Req, [&](const Symbol &S) { DynB.insert(S); }); + unsigned DynamicCount = 0; + unsigned StaticCount = 0; + unsigned MergedCount = 0; + More |= Dynamic->fuzzyFind(Req, [&](const Symbol &S) { + ++DynamicCount; + DynB.insert(S); + }); SymbolSlab Dyn = std::move(DynB).build(); DenseSet SeenDynamicSymbols; More |= Static->fuzzyFind(Req, [&](const Symbol &S) { auto DynS = Dyn.find(S.ID); + ++StaticCount; if (DynS == Dyn.end()) return Callback(S); + ++MergedCount; SeenDynamicSymbols.insert(S.ID); Callback(mergeSymbol(*DynS, S)); }); + SPAN_ATTACH(Tracer, "dynamic", DynamicCount); + SPAN_ATTACH(Tracer, "static", StaticCount); + SPAN_ATTACH(Tracer, "merged", MergedCount); for (const Symbol &S : Dyn) if (!SeenDynamicSymbols.count(S.ID)) Callback(S); @@ -60,6 +73,7 @@ class MergedIndex : public SymbolIndex { void lookup(const LookupRequest &Req, llvm::function_ref Callback) const override { + trace::Span Tracer("MergedIndex lookup"); SymbolSlab::Builder B; Dynamic->lookup(Req, [&](const Symbol &S) { B.insert(S); }); @@ -80,6 +94,7 @@ class MergedIndex : public SymbolIndex { void refs(const RefsRequest &Req, llvm::function_ref Callback) const override { + trace::Span Tracer("MergedIndex refs"); // We don't want duplicated refs from the static/dynamic indexes, // and we can't reliably duplicate them because offsets may differ slightly. // We consider the dynamic index authoritative and report all its refs, diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 4086d1f00..880be3d71 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -12,6 +12,7 @@ #include "FuzzyMatch.h" #include "Logger.h" #include "Quality.h" +#include "Trace.h" #include "llvm/ADT/StringSet.h" #include #include @@ -139,6 +140,8 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref Callback) const { assert(!StringRef(Req.Query).contains("::") && "There must be no :: in query."); + // FIXME: attach the query tree to the trace span. + trace::Span Tracer("Dex fuzzyFind"); FuzzyMatcher Filter(Req.Query); bool More = false; @@ -228,6 +231,7 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, void Dex::lookup(const LookupRequest &Req, llvm::function_ref Callback) const { + trace::Span Tracer("Dex lookup"); for (const auto &ID : Req.IDs) { auto I = LookupTable.find(ID); if (I != LookupTable.end()) @@ -237,6 +241,7 @@ void Dex::lookup(const LookupRequest &Req, void Dex::refs(const RefsRequest &Req, llvm::function_ref Callback) const { + trace::Span Tracer("Dex refs"); log("refs is not implemented."); } From 730058dc9d0a4ee61a60a32b881b56c8d9acb005 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Thu, 27 Sep 2018 18:46:00 +0000 Subject: [PATCH 262/686] [clangd] Initial supoprt for cross-namespace global code completion. Summary: When no scope qualifier is specified, allow completing index symbols from any scope and insert proper automatically. This is still experimental and hidden behind a flag. Things missing: - Scope proximity based scoring. - FuzzyFind supports weighted scopes. Reviewers: sammccall Reviewed By: sammccall Subscribers: kbobyrev, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52364 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343248 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 51 +++++++++++++++++++------- clangd/CodeComplete.h | 7 ++++ clangd/index/Index.h | 6 +++ clangd/index/MemIndex.cpp | 3 +- clangd/index/dex/Dex.cpp | 6 +++ clangd/tool/ClangdMain.cpp | 10 +++++ unittests/clangd/CodeCompleteTests.cpp | 51 ++++++++++++++++++++++++++ unittests/clangd/DexTests.cpp | 11 ++++++ 8 files changed, 130 insertions(+), 15 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index e43389ba8..cd237268d 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -44,6 +44,7 @@ #include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Sema/Sema.h" #include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Error.h" @@ -330,6 +331,7 @@ struct ScoredBundleGreater { struct CodeCompletionBuilder { CodeCompletionBuilder(ASTContext &ASTCtx, const CompletionCandidate &C, CodeCompletionString *SemaCCS, + llvm::ArrayRef QueryScopes, const IncludeInserter &Includes, StringRef FileName, CodeCompletionContext::Kind ContextKind, const CodeCompleteOptions &Opts) @@ -374,6 +376,18 @@ struct CodeCompletionBuilder { Completion.Kind = toCompletionItemKind(C.IndexResult->SymInfo.Kind); if (Completion.Name.empty()) Completion.Name = C.IndexResult->Name; + // If the completion was visible to Sema, no qualifier is needed. This + // avoids unneeded qualifiers in cases like with `using ns::X`. + if (Completion.RequiredQualifier.empty() && !C.SemaResult) { + StringRef ShortestQualifier = C.IndexResult->Scope; + for (StringRef Scope : QueryScopes) { + StringRef Qualifier = C.IndexResult->Scope; + if (Qualifier.consume_front(Scope) && + Qualifier.size() < ShortestQualifier.size()) + ShortestQualifier = Qualifier; + } + Completion.RequiredQualifier = ShortestQualifier; + } Completion.Deprecated |= (C.IndexResult->Flags & Symbol::Deprecated); } @@ -604,9 +618,11 @@ struct SpecifiedScope { } }; -// Get all scopes that will be queried in indexes. -std::vector getQueryScopes(CodeCompletionContext &CCContext, - const SourceManager &SM) { +// Get all scopes that will be queried in indexes and whether symbols from +// any scope is allowed. +std::pair, bool> +getQueryScopes(CodeCompletionContext &CCContext, const SourceManager &SM, + const CodeCompleteOptions &Opts) { auto GetAllAccessibleScopes = [](CodeCompletionContext &CCContext) { SpecifiedScope Info; for (auto *Context : CCContext.getVisitedContexts()) { @@ -627,13 +643,15 @@ std::vector getQueryScopes(CodeCompletionContext &CCContext, // FIXME: Capture scopes and use for scoring, for example, // "using namespace std; namespace foo {v^}" => // foo::value > std::vector > boost::variant - return GetAllAccessibleScopes(CCContext).scopesForIndexQuery(); + auto Scopes = GetAllAccessibleScopes(CCContext).scopesForIndexQuery(); + // Allow AllScopes completion only for there is no explicit scope qualifier. + return {Scopes, Opts.AllScopes}; } // Qualified completion ("std::vec^"), we have two cases depending on whether // the qualifier can be resolved by Sema. if ((*SS)->isValid()) { // Resolved qualifier. - return GetAllAccessibleScopes(CCContext).scopesForIndexQuery(); + return {GetAllAccessibleScopes(CCContext).scopesForIndexQuery(), false}; } // Unresolved qualifier. @@ -651,7 +669,7 @@ std::vector getQueryScopes(CodeCompletionContext &CCContext, if (!Info.UnresolvedQualifier->empty()) *Info.UnresolvedQualifier += "::"; - return Info.scopesForIndexQuery(); + return {Info.scopesForIndexQuery(), false}; } // Should we perform index-based completion in a context of the specified kind? @@ -1262,8 +1280,10 @@ class CodeCompleteFlow { CompletionRecorder *Recorder = nullptr; int NSema = 0, NIndex = 0, NBoth = 0; // Counters for logging. bool Incomplete = false; // Would more be available with a higher limit? - llvm::Optional Filter; // Initialized once Sema runs. - std::vector QueryScopes; // Initialized once Sema runs. + llvm::Optional Filter; // Initialized once Sema runs. + std::vector QueryScopes; // Initialized once Sema runs. + // Whether to query symbols from any scope. Initialized once Sema runs. + bool AllScopes = false; // Include-insertion and proximity scoring rely on the include structure. // This is available after Sema has run. llvm::Optional Inserter; // Available during runWithSema. @@ -1339,9 +1359,9 @@ class CodeCompleteFlow { Inserter.reset(); // Make sure this doesn't out-live Clang. SPAN_ATTACH(Tracer, "sema_completion_kind", getCompletionKindString(Recorder->CCContext.getKind())); - log("Code complete: sema context {0}, query scopes [{1}]", + log("Code complete: sema context {0}, query scopes [{1}] (AnyScope={2})", getCompletionKindString(Recorder->CCContext.getKind()), - llvm::join(QueryScopes.begin(), QueryScopes.end(), ",")); + llvm::join(QueryScopes.begin(), QueryScopes.end(), ","), AllScopes); }); Recorder = RecorderOwner.get(); @@ -1387,8 +1407,8 @@ class CodeCompleteFlow { } Filter = FuzzyMatcher( Recorder->CCSema->getPreprocessor().getCodeCompletionFilter()); - QueryScopes = getQueryScopes(Recorder->CCContext, - Recorder->CCSema->getSourceManager()); + std::tie(QueryScopes, AllScopes) = getQueryScopes( + Recorder->CCContext, Recorder->CCSema->getSourceManager(), Opts); // Sema provides the needed context to query the index. // FIXME: in addition to querying for extra/overlapping symbols, we should // explicitly request symbols corresponding to Sema results. @@ -1428,6 +1448,7 @@ class CodeCompleteFlow { Req.Query = Filter->pattern(); Req.RestrictForCodeCompletion = true; Req.Scopes = QueryScopes; + Req.AnyScope = AllScopes; // FIXME: we should send multiple weighted paths here. Req.ProximityPaths.push_back(FileName); vlog("Code complete: fuzzyFind({0:2})", toJSON(Req)); @@ -1538,6 +1559,8 @@ class CodeCompleteFlow { Relevance.Context = Recorder->CCContext.getKind(); Relevance.Query = SymbolRelevanceSignals::CodeComplete; Relevance.FileProximityMatch = FileProximity.getPointer(); + // FIXME: incorparate scope proximity into relevance score. + auto &First = Bundle.front(); if (auto FuzzyScore = fuzzyScore(First)) Relevance.NameMatch = *FuzzyScore; @@ -1587,8 +1610,8 @@ class CodeCompleteFlow { : nullptr; if (!Builder) Builder.emplace(Recorder->CCSema->getASTContext(), Item, SemaCCS, - *Inserter, FileName, Recorder->CCContext.getKind(), - Opts); + QueryScopes, *Inserter, FileName, + Recorder->CCContext.getKind(), Opts); else Builder->add(Item, SemaCCS); } diff --git a/clangd/CodeComplete.h b/clangd/CodeComplete.h index 6467ad354..92fa2279a 100644 --- a/clangd/CodeComplete.h +++ b/clangd/CodeComplete.h @@ -101,6 +101,13 @@ struct CodeCompleteOptions { /// Whether to generate snippets for function arguments on code-completion. /// Needs snippets to be enabled as well. bool EnableFunctionArgSnippets = true; + + /// Whether to include index symbols that are not defined in the scopes + /// visible from the code completion point. This applies in contexts without + /// explicit scope qualifiers. + /// + /// Such completions can insert scope qualifiers. + bool AllScopes = false; }; // Semi-structured representation of a code-complete suggestion for our C++ API. diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 746638229..06de5d445 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -428,7 +428,13 @@ struct FuzzyFindRequest { /// namespace xyz::abc. /// /// The global scope is "", a top level scope is "foo::", etc. + /// FIXME: drop the special case for empty list, which is the same as + /// `AnyScope = true`. + /// FIXME: support scope proximity. std::vector Scopes; + /// If set to true, allow symbols from any scope. Scopes explicitly listed + /// above will be ranked higher. + bool AnyScope = false; /// \brief The number of top candidates to return. The index may choose to /// return more than this, e.g. if it doesn't know which candidates are best. llvm::Optional Limit; diff --git a/clangd/index/MemIndex.cpp b/clangd/index/MemIndex.cpp index d39b86f23..279969dfc 100644 --- a/clangd/index/MemIndex.cpp +++ b/clangd/index/MemIndex.cpp @@ -39,7 +39,8 @@ bool MemIndex::fuzzyFind( const Symbol *Sym = Pair.second; // Exact match against all possible scopes. - if (!Req.Scopes.empty() && !llvm::is_contained(Req.Scopes, Sym->Scope)) + if (!Req.AnyScope && !Req.Scopes.empty() && + !llvm::is_contained(Req.Scopes, Sym->Scope)) continue; if (Req.RestrictForCodeCompletion && !(Sym->Flags & Symbol::IndexedForCodeCompletion)) diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 880be3d71..b201fd87d 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -13,6 +13,8 @@ #include "Logger.h" #include "Quality.h" #include "Trace.h" +#include "index/Index.h" +#include "index/dex/Iterator.h" #include "llvm/ADT/StringSet.h" #include #include @@ -166,6 +168,10 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, if (It != InvertedIndex.end()) ScopeIterators.push_back(It->second.iterator()); } + if (Req.AnyScope) + ScopeIterators.push_back(createBoost(createTrue(Symbols.size()), + ScopeIterators.empty() ? 1.0 : 0.2)); + // Add OR iterator for scopes if there are any Scope Iterators. if (!ScopeIterators.empty()) TopLevelChildren.push_back(createOr(move(ScopeIterators))); diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 4c6a4c352..aebff5189 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -136,6 +136,15 @@ static llvm::cl::opt EnableIndex( "enabled separatedly."), llvm::cl::init(true), llvm::cl::Hidden); +static llvm::cl::opt AllScopesCompletion( + "all-scopes-completion", + llvm::cl::desc( + "If set to true, code completion will include index symbols that are " + "not defined in the scopes (e.g. " + "namespaces) visible from the code completion point. Such completions " + "can insert scope qualifiers."), + llvm::cl::init(false), llvm::cl::Hidden); + static llvm::cl::opt ShowOrigins("debug-origin", llvm::cl::desc("Show origins of completion items"), @@ -304,6 +313,7 @@ int main(int argc, char *argv[]) { } CCOpts.SpeculativeIndexRequest = Opts.StaticIndex; CCOpts.EnableFunctionArgSnippets = EnableFunctionArgSnippets; + CCOpts.AllScopes = AllScopesCompletion; // Initialize and run ClangdLSPServer. ClangdLSPServer LSPServer( diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index fc707baf2..68b13da17 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -2093,6 +2093,57 @@ TEST(CompletionTest, IncludedCompletionKinds) { Has("bar.h\"", CompletionItemKind::File))); } +TEST(CompletionTest, NoAllScopesCompletionWhenQualified) { + clangd::CodeCompleteOptions Opts = {}; + Opts.AllScopes = true; + + auto Results = completions( + R"cpp( + void f() { na::Clangd^ } + )cpp", + {cls("na::ClangdA"), cls("nx::ClangdX"), cls("Clangd3")}, Opts); + EXPECT_THAT(Results.Completions, + UnorderedElementsAre( + AllOf(Qualifier(""), Scope("na::"), Named("ClangdA")))); +} + +TEST(CompletionTest, AllScopesCompletion) { + clangd::CodeCompleteOptions Opts = {}; + Opts.AllScopes = true; + + auto Results = completions( + R"cpp( + namespace na { + void f() { Clangd^ } + } + )cpp", + {cls("nx::Clangd1"), cls("ny::Clangd2"), cls("Clangd3"), + cls("na::nb::Clangd4")}, + Opts); + EXPECT_THAT( + Results.Completions, + UnorderedElementsAre(AllOf(Qualifier("nx::"), Named("Clangd1")), + AllOf(Qualifier("ny::"), Named("Clangd2")), + AllOf(Qualifier(""), Scope(""), Named("Clangd3")), + AllOf(Qualifier("nb::"), Named("Clangd4")))); +} + +TEST(CompletionTest, NoQualifierIfShadowed) { + clangd::CodeCompleteOptions Opts = {}; + Opts.AllScopes = true; + + auto Results = completions(R"cpp( + namespace nx { class Clangd1 {}; } + using nx::Clangd1; + void f() { Clangd^ } + )cpp", + {cls("nx::Clangd1"), cls("nx::Clangd2")}, Opts); + // Although Clangd1 is from another namespace, Sema tells us it's in-scope and + // needs no qualifier. + EXPECT_THAT(Results.Completions, + UnorderedElementsAre(AllOf(Qualifier(""), Named("Clangd1")), + AllOf(Qualifier("nx::"), Named("Clangd2")))); +} } // namespace } // namespace clangd diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index d9db3087f..231343cd7 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -523,6 +523,17 @@ TEST(DexTest, NoMatchNestedScopes) { EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1")); } +TEST(DexTest, WildcardScope) { + auto I = + Dex::build(generateSymbols({"a::y1", "a::b::y2", "c::y3"}), URISchemes); + FuzzyFindRequest Req; + Req.Query = "y"; + Req.Scopes = {"a::"}; + Req.AnyScope = true; + EXPECT_THAT(match(*I, Req), + UnorderedElementsAre("a::y1", "a::b::y2", "c::y3")); +} + TEST(DexTest, IgnoreCases) { auto I = Dex::build(generateSymbols({"ns::ABC", "ns::abc"}), URISchemes); FuzzyFindRequest Req; From 55db8de10f5ecd55e94cd21bd0bfd86a43ccf4eb Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Fri, 28 Sep 2018 09:32:47 +0000 Subject: [PATCH 263/686] [docs] Fix links in Clangd documentation Add missing `_` after each `external link `_, as required by the reStructuredText specification. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343306 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/clangd.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/clangd.rst b/docs/clangd.rst index cd0599557..c6388f353 100644 --- a/docs/clangd.rst +++ b/docs/clangd.rst @@ -133,15 +133,15 @@ VSCode Integration ------------------ :program:`VSCode` provides `vscode-clangd -` +`_ which is published in Visual Studio Marketplace and can be installed direcetly from :program:`VSCode`. Emacs Integration ----------------- -:program:`Emacs` provides `lsp-mode ` and -`Eglot ` plugins for LSP integration. +:program:`Emacs` provides `lsp-mode `_ and +`Eglot `_ plugins for LSP integration. Getting Involved ================== From 3e4193d671157ed4543e9c172ad4d8b6375c392a Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Sat, 29 Sep 2018 02:17:12 +0000 Subject: [PATCH 264/686] [cxx2a] Fix warning triggered by r343285 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343369 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/BitcodeWriter.h | 1 - 1 file changed, 1 deletion(-) diff --git a/clang-doc/BitcodeWriter.h b/clang-doc/BitcodeWriter.h index 2ff46c612..12a31ea56 100644 --- a/clang-doc/BitcodeWriter.h +++ b/clang-doc/BitcodeWriter.h @@ -169,7 +169,6 @@ class ClangDocBitcodeWriter { Stream.EnterSubblock(ID, BitCodeConstants::SubblockIDSize); } - StreamSubBlockGuard() = default; StreamSubBlockGuard(const StreamSubBlockGuard &) = delete; StreamSubBlockGuard &operator=(const StreamSubBlockGuard &) = delete; From 1f9ec52e0d16cd6b3d72f7c16dd8c81d4d726c4c Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Sun, 30 Sep 2018 17:22:58 +0000 Subject: [PATCH 265/686] Allow clang-tidy to be built without a dependency on the clang static analyzer. Patch by Stephen Kelly. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343415 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 2 -- clang-tidy/CMakeLists.txt | 13 +++++++-- clang-tidy/ClangTidy.cpp | 10 +++++++ clang-tidy/plugin/CMakeLists.txt | 7 ++++- clang-tidy/plugin/ClangTidyPlugin.cpp | 2 ++ clang-tidy/tool/CMakeLists.txt | 7 ++++- clang-tidy/tool/ClangTidyMain.cpp | 2 ++ docs/clang-tidy/index.rst | 4 +++ test/CMakeLists.txt | 16 ++++------ test/clang-tidy/enable-alpha-checks.cpp | 2 ++ test/clang-tidy/mpi-buffer-deref.cpp | 1 + test/clang-tidy/mpi-type-mismatch.cpp | 1 + test/clang-tidy/nolint.cpp | 1 + test/clang-tidy/read_file_config.cpp | 1 + test/clang-tidy/static-analyzer-config.cpp | 1 + test/clang-tidy/static-analyzer.cpp | 1 + test/clang-tidy/temporaries.cpp | 1 + test/lit.cfg | 34 ++++++++++------------ unittests/CMakeLists.txt | 4 +-- 19 files changed, 72 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c434682cf..c3137adef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,8 @@ add_subdirectory(clang-apply-replacements) add_subdirectory(clang-reorder-fields) add_subdirectory(modularize) -if(CLANG_ENABLE_STATIC_ANALYZER) add_subdirectory(clang-tidy) add_subdirectory(clang-tidy-vs) -endif() add_subdirectory(change-namespace) add_subdirectory(clang-doc) diff --git a/clang-tidy/CMakeLists.txt b/clang-tidy/CMakeLists.txt index a87246942..0037268a1 100644 --- a/clang-tidy/CMakeLists.txt +++ b/clang-tidy/CMakeLists.txt @@ -21,12 +21,17 @@ add_clang_library(clangTidy clangLex clangRewrite clangSema - clangStaticAnalyzerCore - clangStaticAnalyzerFrontend clangTooling clangToolingCore ) +if(CLANG_ENABLE_STATIC_ANALYZER) + target_link_libraries(clangTidy PRIVATE + clangStaticAnalyzerCore + clangStaticAnalyzerFrontend + ) +endif() + add_subdirectory(android) add_subdirectory(abseil) add_subdirectory(boost) @@ -39,7 +44,9 @@ add_subdirectory(hicpp) add_subdirectory(llvm) add_subdirectory(misc) add_subdirectory(modernize) -add_subdirectory(mpi) +if(CLANG_ENABLE_STATIC_ANALYZER) + add_subdirectory(mpi) +endif() add_subdirectory(objc) add_subdirectory(performance) add_subdirectory(plugin) diff --git a/clang-tidy/ClangTidy.cpp b/clang-tidy/ClangTidy.cpp index d9eb1a511..d78e5abad 100644 --- a/clang-tidy/ClangTidy.cpp +++ b/clang-tidy/ClangTidy.cpp @@ -34,8 +34,10 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Rewrite/Frontend/FixItRewriter.h" #include "clang/Rewrite/Frontend/FrontendActions.h" +#if CLANG_ENABLE_STATIC_ANALYZER #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#endif // CLANG_ENABLE_STATIC_ANALYZER #include "clang/Tooling/DiagnosticsYaml.h" #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/ReplacementsYaml.h" @@ -56,6 +58,7 @@ namespace clang { namespace tidy { namespace { +#if CLANG_ENABLE_STATIC_ANALYZER static const char *AnalyzerCheckNamePrefix = "clang-analyzer-"; class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer { @@ -87,6 +90,7 @@ class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer { private: ClangTidyContext &Context; }; +#endif // CLANG_ENABLE_STATIC_ANALYZER class ErrorReporter { public: @@ -296,6 +300,7 @@ ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory( } } +#if CLANG_ENABLE_STATIC_ANALYZER static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts, AnalyzerOptionsRef AnalyzerOptions) { StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix); @@ -339,6 +344,7 @@ static CheckersList getCheckersControlList(ClangTidyContext &Context, } return List; } +#endif // CLANG_ENABLE_STATIC_ANALYZER std::unique_ptr ClangTidyASTConsumerFactory::CreateASTConsumer( @@ -380,6 +386,7 @@ ClangTidyASTConsumerFactory::CreateASTConsumer( if (!Checks.empty()) Consumers.push_back(Finder->newASTConsumer()); +#if CLANG_ENABLE_STATIC_ANALYZER AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts(); AnalyzerOptions->CheckersControlList = getCheckersControlList(Context, Context.canEnableAnalyzerAlphaCheckers()); @@ -395,6 +402,7 @@ ClangTidyASTConsumerFactory::CreateASTConsumer( new AnalyzerDiagnosticConsumer(Context)); Consumers.push_back(std::move(AnalysisConsumer)); } +#endif // CLANG_ENABLE_STATIC_ANALYZER return llvm::make_unique( std::move(Consumers), std::move(Profiling), std::move(Finder), std::move(Checks)); @@ -407,9 +415,11 @@ std::vector ClangTidyASTConsumerFactory::getCheckNames() { CheckNames.push_back(CheckFactory.first); } +#if CLANG_ENABLE_STATIC_ANALYZER for (const auto &AnalyzerCheck : getCheckersControlList( Context, Context.canEnableAnalyzerAlphaCheckers())) CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first); +#endif // CLANG_ENABLE_STATIC_ANALYZER std::sort(CheckNames.begin(), CheckNames.end()); return CheckNames; diff --git a/clang-tidy/plugin/CMakeLists.txt b/clang-tidy/plugin/CMakeLists.txt index 3540b2be7..7a12d7fd5 100644 --- a/clang-tidy/plugin/CMakeLists.txt +++ b/clang-tidy/plugin/CMakeLists.txt @@ -20,7 +20,6 @@ add_clang_library(clangTidyPlugin clangTidyLLVMModule clangTidyMiscModule clangTidyModernizeModule - clangTidyMPIModule clangTidyObjCModule clangTidyPerformanceModule clangTidyPortabilityModule @@ -28,3 +27,9 @@ add_clang_library(clangTidyPlugin clangTidyZirconModule clangTooling ) + +if(CLANG_ENABLE_STATIC_ANALYZER) + target_link_libraries(clangTidyPlugin PRIVATE + clangTidyMPIModule + ) +endif() diff --git a/clang-tidy/plugin/ClangTidyPlugin.cpp b/clang-tidy/plugin/ClangTidyPlugin.cpp index 345561205..f998d6a5a 100644 --- a/clang-tidy/plugin/ClangTidyPlugin.cpp +++ b/clang-tidy/plugin/ClangTidyPlugin.cpp @@ -133,10 +133,12 @@ extern volatile int ModernizeModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED ModernizeModuleAnchorDestination = ModernizeModuleAnchorSource; +#if CLANG_ENABLE_STATIC_ANALYZER // This anchor is used to force the linker to link the MPIModule. extern volatile int MPIModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED MPIModuleAnchorDestination = MPIModuleAnchorSource; +#endif // This anchor is used to force the linker to link the ObjCModule. extern volatile int ObjCModuleAnchorSource; diff --git a/clang-tidy/tool/CMakeLists.txt b/clang-tidy/tool/CMakeLists.txt index a3ec4ae75..f58cfea55 100644 --- a/clang-tidy/tool/CMakeLists.txt +++ b/clang-tidy/tool/CMakeLists.txt @@ -29,7 +29,6 @@ target_link_libraries(clang-tidy clangTidyLLVMModule clangTidyMiscModule clangTidyModernizeModule - clangTidyMPIModule clangTidyObjCModule clangTidyPerformanceModule clangTidyPortabilityModule @@ -39,6 +38,12 @@ target_link_libraries(clang-tidy clangToolingCore ) +if(CLANG_ENABLE_STATIC_ANALYZER) + target_link_libraries(clang-tidy PRIVATE + clangTidyMPIModule + ) +endif() + install(PROGRAMS clang-tidy-diff.py DESTINATION share/clang COMPONENT clang-tidy) diff --git a/clang-tidy/tool/ClangTidyMain.cpp b/clang-tidy/tool/ClangTidyMain.cpp index b458b2931..fa864e824 100644 --- a/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tidy/tool/ClangTidyMain.cpp @@ -534,10 +534,12 @@ extern volatile int ModernizeModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED ModernizeModuleAnchorDestination = ModernizeModuleAnchorSource; +#if CLANG_ENABLE_STATIC_ANALYZER // This anchor is used to force the linker to link the MPIModule. extern volatile int MPIModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED MPIModuleAnchorDestination = MPIModuleAnchorSource; +#endif // This anchor is used to force the linker to link the PerformanceModule. extern volatile int PerformanceModuleAnchorSource; diff --git a/docs/clang-tidy/index.rst b/docs/clang-tidy/index.rst index 3b7443c2d..ab165d8c5 100644 --- a/docs/clang-tidy/index.rst +++ b/docs/clang-tidy/index.rst @@ -337,6 +337,10 @@ There are a few tools particularly useful when developing clang-tidy checks: * `clang-check`_ with the ``-ast-dump`` (and optionally ``-ast-dump-filter``) provides a convenient way to dump AST of a C++ program. +If CMake is configured with ``CLANG_ENABLE_STATIC_ANALYZER``, +:program:`clang-tidy` will not be built with support for the +``clang-analyzer-*`` checks or the ``mpi-*`` checks. + .. _AST Matchers: http://clang.llvm.org/docs/LibASTMatchers.html .. _PPCallbacks: http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cc03ff74f..375e71f5d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -60,18 +60,14 @@ set(CLANG_TOOLS_TEST_DEPS # Unit tests ExtraToolsUnitTests - ) -if(CLANG_ENABLE_STATIC_ANALYZER) - list(APPEND CLANG_TOOLS_TEST_DEPS - # For the clang-tidy libclang integration test. - c-index-test - # clang-tidy tests require it. - clang-headers + # For the clang-tidy libclang integration test. + c-index-test + # clang-tidy tests require it. + clang-headers - clang-tidy - ) -endif() + clang-tidy + ) set(llvm_utils FileCheck count not diff --git a/test/clang-tidy/enable-alpha-checks.cpp b/test/clang-tidy/enable-alpha-checks.cpp index b1e513034..74bdfdb5d 100644 --- a/test/clang-tidy/enable-alpha-checks.cpp +++ b/test/clang-tidy/enable-alpha-checks.cpp @@ -1,3 +1,5 @@ +// REQUIRES: static-analyzer + // Check if '-allow-enabling-analyzer-alpha-checkers' is visible for users. // RUN: clang-tidy -help | not grep 'allow-enabling-analyzer-alpha-checkers' diff --git a/test/clang-tidy/mpi-buffer-deref.cpp b/test/clang-tidy/mpi-buffer-deref.cpp index 67304d230..47d58a5a6 100644 --- a/test/clang-tidy/mpi-buffer-deref.cpp +++ b/test/clang-tidy/mpi-buffer-deref.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: %check_clang_tidy %s mpi-buffer-deref %t -- -- -I %S/Inputs/mpi-type-mismatch #include "mpimock.h" diff --git a/test/clang-tidy/mpi-type-mismatch.cpp b/test/clang-tidy/mpi-type-mismatch.cpp index 5e230b89b..bf978dcc1 100644 --- a/test/clang-tidy/mpi-type-mismatch.cpp +++ b/test/clang-tidy/mpi-type-mismatch.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: %check_clang_tidy %s mpi-type-mismatch %t -- -- -I %S/Inputs/mpi-type-mismatch #include "mpimock.h" diff --git a/test/clang-tidy/nolint.cpp b/test/clang-tidy/nolint.cpp index 893e81929..24c37228d 100644 --- a/test/clang-tidy/nolint.cpp +++ b/test/clang-tidy/nolint.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: %check_clang_tidy %s google-explicit-constructor,clang-diagnostic-unused-variable,clang-analyzer-core.UndefinedBinaryOperatorResult %t -- -extra-arg=-Wunused-variable -- -I%S/Inputs/nolint #include "trigger_warning.h" diff --git a/test/clang-tidy/read_file_config.cpp b/test/clang-tidy/read_file_config.cpp index 3635c214f..3e39b4d63 100644 --- a/test/clang-tidy/read_file_config.cpp +++ b/test/clang-tidy/read_file_config.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: mkdir -p %T/read-file-config/ // RUN: cp %s %T/read-file-config/test.cpp // RUN: echo 'Checks: "-*,modernize-use-nullptr"' > %T/read-file-config/.clang-tidy diff --git a/test/clang-tidy/static-analyzer-config.cpp b/test/clang-tidy/static-analyzer-config.cpp index cee0a4c41..9ca87cf8d 100644 --- a/test/clang-tidy/static-analyzer-config.cpp +++ b/test/clang-tidy/static-analyzer-config.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: clang-tidy %s -checks='-*,clang-analyzer-unix.Malloc' -config='{CheckOptions: [{ key: "clang-analyzer-unix.Malloc:Optimistic", value: true}]}' -- | FileCheck %s typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); diff --git a/test/clang-tidy/static-analyzer.cpp b/test/clang-tidy/static-analyzer.cpp index b0015aa47..af9693ad0 100644 --- a/test/clang-tidy/static-analyzer.cpp +++ b/test/clang-tidy/static-analyzer.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: clang-tidy %s -checks='-*,clang-analyzer-*' -- | FileCheck %s extern void *malloc(unsigned long); extern void free(void *); diff --git a/test/clang-tidy/temporaries.cpp b/test/clang-tidy/temporaries.cpp index 3df4c60d4..0aa60ed3f 100644 --- a/test/clang-tidy/temporaries.cpp +++ b/test/clang-tidy/temporaries.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: clang-tidy -checks='-*,clang-analyzer-core.NullDereference' %s -- | FileCheck %s struct NoReturnDtor { diff --git a/test/lit.cfg b/test/lit.cfg index 8e755c879..8f8ebaf98 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -119,24 +119,22 @@ if platform.system() not in ['Windows']: if config.clang_staticanalyzer: config.available_features.add('static-analyzer') - check_clang_tidy = os.path.join( - config.test_source_root, "clang-tidy", "check_clang_tidy.py") - config.substitutions.append( - ('%check_clang_tidy', - '%s %s' % (config.python_executable, check_clang_tidy)) ) - clang_tidy_diff = os.path.join( - config.test_source_root, "..", "clang-tidy", "tool", "clang-tidy-diff.py") - config.substitutions.append( - ('%clang_tidy_diff', - '%s %s' % (config.python_executable, clang_tidy_diff)) ) - run_clang_tidy = os.path.join( - config.test_source_root, "..", "clang-tidy", "tool", "run-clang-tidy.py") - config.substitutions.append( - ('%run_clang_tidy', - '%s %s' % (config.python_executable, run_clang_tidy)) ) -else: - # exclude the clang-tidy test directory - config.excludes.append('clang-tidy') + +check_clang_tidy = os.path.join( + config.test_source_root, "clang-tidy", "check_clang_tidy.py") +config.substitutions.append( + ('%check_clang_tidy', + '%s %s' % (config.python_executable, check_clang_tidy)) ) +clang_tidy_diff = os.path.join( + config.test_source_root, "..", "clang-tidy", "tool", "clang-tidy-diff.py") +config.substitutions.append( + ('%clang_tidy_diff', + '%s %s' % (config.python_executable, clang_tidy_diff)) ) +run_clang_tidy = os.path.join( + config.test_source_root, "..", "clang-tidy", "tool", "run-clang-tidy.py") +config.substitutions.append( + ('%run_clang_tidy', + '%s %s' % (config.python_executable, run_clang_tidy)) ) clangd_benchmarks_dir = os.path.join(os.path.dirname(config.clang_tools_dir), "tools", "clang", "tools", "extra", diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 82f54d5cd..d123700ba 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -18,8 +18,6 @@ add_subdirectory(change-namespace) add_subdirectory(clang-apply-replacements) add_subdirectory(clang-move) add_subdirectory(clang-query) -if(CLANG_ENABLE_STATIC_ANALYZER) - add_subdirectory(clang-tidy) -endif() +add_subdirectory(clang-tidy) add_subdirectory(clangd) add_subdirectory(include-fixer) From a8a63d51ebedd10c34329398171000d30a9518da Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Sun, 30 Sep 2018 17:39:39 +0000 Subject: [PATCH 266/686] Reverting r343415 as it breaks at least one of the bots. http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-ubuntu-fast/builds/37336 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343418 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 2 ++ clang-tidy/CMakeLists.txt | 13 ++------- clang-tidy/ClangTidy.cpp | 10 ------- clang-tidy/plugin/CMakeLists.txt | 7 +---- clang-tidy/plugin/ClangTidyPlugin.cpp | 2 -- clang-tidy/tool/CMakeLists.txt | 7 +---- clang-tidy/tool/ClangTidyMain.cpp | 2 -- docs/clang-tidy/index.rst | 4 --- test/CMakeLists.txt | 16 ++++++---- test/clang-tidy/enable-alpha-checks.cpp | 2 -- test/clang-tidy/mpi-buffer-deref.cpp | 1 - test/clang-tidy/mpi-type-mismatch.cpp | 1 - test/clang-tidy/nolint.cpp | 1 - test/clang-tidy/read_file_config.cpp | 1 - test/clang-tidy/static-analyzer-config.cpp | 1 - test/clang-tidy/static-analyzer.cpp | 1 - test/clang-tidy/temporaries.cpp | 1 - test/lit.cfg | 34 ++++++++++++---------- unittests/CMakeLists.txt | 4 ++- 19 files changed, 38 insertions(+), 72 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c3137adef..c434682cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,10 @@ add_subdirectory(clang-apply-replacements) add_subdirectory(clang-reorder-fields) add_subdirectory(modularize) +if(CLANG_ENABLE_STATIC_ANALYZER) add_subdirectory(clang-tidy) add_subdirectory(clang-tidy-vs) +endif() add_subdirectory(change-namespace) add_subdirectory(clang-doc) diff --git a/clang-tidy/CMakeLists.txt b/clang-tidy/CMakeLists.txt index 0037268a1..a87246942 100644 --- a/clang-tidy/CMakeLists.txt +++ b/clang-tidy/CMakeLists.txt @@ -21,17 +21,12 @@ add_clang_library(clangTidy clangLex clangRewrite clangSema + clangStaticAnalyzerCore + clangStaticAnalyzerFrontend clangTooling clangToolingCore ) -if(CLANG_ENABLE_STATIC_ANALYZER) - target_link_libraries(clangTidy PRIVATE - clangStaticAnalyzerCore - clangStaticAnalyzerFrontend - ) -endif() - add_subdirectory(android) add_subdirectory(abseil) add_subdirectory(boost) @@ -44,9 +39,7 @@ add_subdirectory(hicpp) add_subdirectory(llvm) add_subdirectory(misc) add_subdirectory(modernize) -if(CLANG_ENABLE_STATIC_ANALYZER) - add_subdirectory(mpi) -endif() +add_subdirectory(mpi) add_subdirectory(objc) add_subdirectory(performance) add_subdirectory(plugin) diff --git a/clang-tidy/ClangTidy.cpp b/clang-tidy/ClangTidy.cpp index d78e5abad..d9eb1a511 100644 --- a/clang-tidy/ClangTidy.cpp +++ b/clang-tidy/ClangTidy.cpp @@ -34,10 +34,8 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Rewrite/Frontend/FixItRewriter.h" #include "clang/Rewrite/Frontend/FrontendActions.h" -#if CLANG_ENABLE_STATIC_ANALYZER #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" -#endif // CLANG_ENABLE_STATIC_ANALYZER #include "clang/Tooling/DiagnosticsYaml.h" #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/ReplacementsYaml.h" @@ -58,7 +56,6 @@ namespace clang { namespace tidy { namespace { -#if CLANG_ENABLE_STATIC_ANALYZER static const char *AnalyzerCheckNamePrefix = "clang-analyzer-"; class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer { @@ -90,7 +87,6 @@ class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer { private: ClangTidyContext &Context; }; -#endif // CLANG_ENABLE_STATIC_ANALYZER class ErrorReporter { public: @@ -300,7 +296,6 @@ ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory( } } -#if CLANG_ENABLE_STATIC_ANALYZER static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts, AnalyzerOptionsRef AnalyzerOptions) { StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix); @@ -344,7 +339,6 @@ static CheckersList getCheckersControlList(ClangTidyContext &Context, } return List; } -#endif // CLANG_ENABLE_STATIC_ANALYZER std::unique_ptr ClangTidyASTConsumerFactory::CreateASTConsumer( @@ -386,7 +380,6 @@ ClangTidyASTConsumerFactory::CreateASTConsumer( if (!Checks.empty()) Consumers.push_back(Finder->newASTConsumer()); -#if CLANG_ENABLE_STATIC_ANALYZER AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts(); AnalyzerOptions->CheckersControlList = getCheckersControlList(Context, Context.canEnableAnalyzerAlphaCheckers()); @@ -402,7 +395,6 @@ ClangTidyASTConsumerFactory::CreateASTConsumer( new AnalyzerDiagnosticConsumer(Context)); Consumers.push_back(std::move(AnalysisConsumer)); } -#endif // CLANG_ENABLE_STATIC_ANALYZER return llvm::make_unique( std::move(Consumers), std::move(Profiling), std::move(Finder), std::move(Checks)); @@ -415,11 +407,9 @@ std::vector ClangTidyASTConsumerFactory::getCheckNames() { CheckNames.push_back(CheckFactory.first); } -#if CLANG_ENABLE_STATIC_ANALYZER for (const auto &AnalyzerCheck : getCheckersControlList( Context, Context.canEnableAnalyzerAlphaCheckers())) CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first); -#endif // CLANG_ENABLE_STATIC_ANALYZER std::sort(CheckNames.begin(), CheckNames.end()); return CheckNames; diff --git a/clang-tidy/plugin/CMakeLists.txt b/clang-tidy/plugin/CMakeLists.txt index 7a12d7fd5..3540b2be7 100644 --- a/clang-tidy/plugin/CMakeLists.txt +++ b/clang-tidy/plugin/CMakeLists.txt @@ -20,6 +20,7 @@ add_clang_library(clangTidyPlugin clangTidyLLVMModule clangTidyMiscModule clangTidyModernizeModule + clangTidyMPIModule clangTidyObjCModule clangTidyPerformanceModule clangTidyPortabilityModule @@ -27,9 +28,3 @@ add_clang_library(clangTidyPlugin clangTidyZirconModule clangTooling ) - -if(CLANG_ENABLE_STATIC_ANALYZER) - target_link_libraries(clangTidyPlugin PRIVATE - clangTidyMPIModule - ) -endif() diff --git a/clang-tidy/plugin/ClangTidyPlugin.cpp b/clang-tidy/plugin/ClangTidyPlugin.cpp index f998d6a5a..345561205 100644 --- a/clang-tidy/plugin/ClangTidyPlugin.cpp +++ b/clang-tidy/plugin/ClangTidyPlugin.cpp @@ -133,12 +133,10 @@ extern volatile int ModernizeModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED ModernizeModuleAnchorDestination = ModernizeModuleAnchorSource; -#if CLANG_ENABLE_STATIC_ANALYZER // This anchor is used to force the linker to link the MPIModule. extern volatile int MPIModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED MPIModuleAnchorDestination = MPIModuleAnchorSource; -#endif // This anchor is used to force the linker to link the ObjCModule. extern volatile int ObjCModuleAnchorSource; diff --git a/clang-tidy/tool/CMakeLists.txt b/clang-tidy/tool/CMakeLists.txt index f58cfea55..a3ec4ae75 100644 --- a/clang-tidy/tool/CMakeLists.txt +++ b/clang-tidy/tool/CMakeLists.txt @@ -29,6 +29,7 @@ target_link_libraries(clang-tidy clangTidyLLVMModule clangTidyMiscModule clangTidyModernizeModule + clangTidyMPIModule clangTidyObjCModule clangTidyPerformanceModule clangTidyPortabilityModule @@ -38,12 +39,6 @@ target_link_libraries(clang-tidy clangToolingCore ) -if(CLANG_ENABLE_STATIC_ANALYZER) - target_link_libraries(clang-tidy PRIVATE - clangTidyMPIModule - ) -endif() - install(PROGRAMS clang-tidy-diff.py DESTINATION share/clang COMPONENT clang-tidy) diff --git a/clang-tidy/tool/ClangTidyMain.cpp b/clang-tidy/tool/ClangTidyMain.cpp index fa864e824..b458b2931 100644 --- a/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tidy/tool/ClangTidyMain.cpp @@ -534,12 +534,10 @@ extern volatile int ModernizeModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED ModernizeModuleAnchorDestination = ModernizeModuleAnchorSource; -#if CLANG_ENABLE_STATIC_ANALYZER // This anchor is used to force the linker to link the MPIModule. extern volatile int MPIModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED MPIModuleAnchorDestination = MPIModuleAnchorSource; -#endif // This anchor is used to force the linker to link the PerformanceModule. extern volatile int PerformanceModuleAnchorSource; diff --git a/docs/clang-tidy/index.rst b/docs/clang-tidy/index.rst index ab165d8c5..3b7443c2d 100644 --- a/docs/clang-tidy/index.rst +++ b/docs/clang-tidy/index.rst @@ -337,10 +337,6 @@ There are a few tools particularly useful when developing clang-tidy checks: * `clang-check`_ with the ``-ast-dump`` (and optionally ``-ast-dump-filter``) provides a convenient way to dump AST of a C++ program. -If CMake is configured with ``CLANG_ENABLE_STATIC_ANALYZER``, -:program:`clang-tidy` will not be built with support for the -``clang-analyzer-*`` checks or the ``mpi-*`` checks. - .. _AST Matchers: http://clang.llvm.org/docs/LibASTMatchers.html .. _PPCallbacks: http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 375e71f5d..cc03ff74f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -60,14 +60,18 @@ set(CLANG_TOOLS_TEST_DEPS # Unit tests ExtraToolsUnitTests + ) - # For the clang-tidy libclang integration test. - c-index-test - # clang-tidy tests require it. - clang-headers +if(CLANG_ENABLE_STATIC_ANALYZER) + list(APPEND CLANG_TOOLS_TEST_DEPS + # For the clang-tidy libclang integration test. + c-index-test + # clang-tidy tests require it. + clang-headers - clang-tidy - ) + clang-tidy + ) +endif() set(llvm_utils FileCheck count not diff --git a/test/clang-tidy/enable-alpha-checks.cpp b/test/clang-tidy/enable-alpha-checks.cpp index 74bdfdb5d..b1e513034 100644 --- a/test/clang-tidy/enable-alpha-checks.cpp +++ b/test/clang-tidy/enable-alpha-checks.cpp @@ -1,5 +1,3 @@ -// REQUIRES: static-analyzer - // Check if '-allow-enabling-analyzer-alpha-checkers' is visible for users. // RUN: clang-tidy -help | not grep 'allow-enabling-analyzer-alpha-checkers' diff --git a/test/clang-tidy/mpi-buffer-deref.cpp b/test/clang-tidy/mpi-buffer-deref.cpp index 47d58a5a6..67304d230 100644 --- a/test/clang-tidy/mpi-buffer-deref.cpp +++ b/test/clang-tidy/mpi-buffer-deref.cpp @@ -1,4 +1,3 @@ -// REQUIRES: static-analyzer // RUN: %check_clang_tidy %s mpi-buffer-deref %t -- -- -I %S/Inputs/mpi-type-mismatch #include "mpimock.h" diff --git a/test/clang-tidy/mpi-type-mismatch.cpp b/test/clang-tidy/mpi-type-mismatch.cpp index bf978dcc1..5e230b89b 100644 --- a/test/clang-tidy/mpi-type-mismatch.cpp +++ b/test/clang-tidy/mpi-type-mismatch.cpp @@ -1,4 +1,3 @@ -// REQUIRES: static-analyzer // RUN: %check_clang_tidy %s mpi-type-mismatch %t -- -- -I %S/Inputs/mpi-type-mismatch #include "mpimock.h" diff --git a/test/clang-tidy/nolint.cpp b/test/clang-tidy/nolint.cpp index 24c37228d..893e81929 100644 --- a/test/clang-tidy/nolint.cpp +++ b/test/clang-tidy/nolint.cpp @@ -1,4 +1,3 @@ -// REQUIRES: static-analyzer // RUN: %check_clang_tidy %s google-explicit-constructor,clang-diagnostic-unused-variable,clang-analyzer-core.UndefinedBinaryOperatorResult %t -- -extra-arg=-Wunused-variable -- -I%S/Inputs/nolint #include "trigger_warning.h" diff --git a/test/clang-tidy/read_file_config.cpp b/test/clang-tidy/read_file_config.cpp index 3e39b4d63..3635c214f 100644 --- a/test/clang-tidy/read_file_config.cpp +++ b/test/clang-tidy/read_file_config.cpp @@ -1,4 +1,3 @@ -// REQUIRES: static-analyzer // RUN: mkdir -p %T/read-file-config/ // RUN: cp %s %T/read-file-config/test.cpp // RUN: echo 'Checks: "-*,modernize-use-nullptr"' > %T/read-file-config/.clang-tidy diff --git a/test/clang-tidy/static-analyzer-config.cpp b/test/clang-tidy/static-analyzer-config.cpp index 9ca87cf8d..cee0a4c41 100644 --- a/test/clang-tidy/static-analyzer-config.cpp +++ b/test/clang-tidy/static-analyzer-config.cpp @@ -1,4 +1,3 @@ -// REQUIRES: static-analyzer // RUN: clang-tidy %s -checks='-*,clang-analyzer-unix.Malloc' -config='{CheckOptions: [{ key: "clang-analyzer-unix.Malloc:Optimistic", value: true}]}' -- | FileCheck %s typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); diff --git a/test/clang-tidy/static-analyzer.cpp b/test/clang-tidy/static-analyzer.cpp index af9693ad0..b0015aa47 100644 --- a/test/clang-tidy/static-analyzer.cpp +++ b/test/clang-tidy/static-analyzer.cpp @@ -1,4 +1,3 @@ -// REQUIRES: static-analyzer // RUN: clang-tidy %s -checks='-*,clang-analyzer-*' -- | FileCheck %s extern void *malloc(unsigned long); extern void free(void *); diff --git a/test/clang-tidy/temporaries.cpp b/test/clang-tidy/temporaries.cpp index 0aa60ed3f..3df4c60d4 100644 --- a/test/clang-tidy/temporaries.cpp +++ b/test/clang-tidy/temporaries.cpp @@ -1,4 +1,3 @@ -// REQUIRES: static-analyzer // RUN: clang-tidy -checks='-*,clang-analyzer-core.NullDereference' %s -- | FileCheck %s struct NoReturnDtor { diff --git a/test/lit.cfg b/test/lit.cfg index 8f8ebaf98..8e755c879 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -119,22 +119,24 @@ if platform.system() not in ['Windows']: if config.clang_staticanalyzer: config.available_features.add('static-analyzer') - -check_clang_tidy = os.path.join( - config.test_source_root, "clang-tidy", "check_clang_tidy.py") -config.substitutions.append( - ('%check_clang_tidy', - '%s %s' % (config.python_executable, check_clang_tidy)) ) -clang_tidy_diff = os.path.join( - config.test_source_root, "..", "clang-tidy", "tool", "clang-tidy-diff.py") -config.substitutions.append( - ('%clang_tidy_diff', - '%s %s' % (config.python_executable, clang_tidy_diff)) ) -run_clang_tidy = os.path.join( - config.test_source_root, "..", "clang-tidy", "tool", "run-clang-tidy.py") -config.substitutions.append( - ('%run_clang_tidy', - '%s %s' % (config.python_executable, run_clang_tidy)) ) + check_clang_tidy = os.path.join( + config.test_source_root, "clang-tidy", "check_clang_tidy.py") + config.substitutions.append( + ('%check_clang_tidy', + '%s %s' % (config.python_executable, check_clang_tidy)) ) + clang_tidy_diff = os.path.join( + config.test_source_root, "..", "clang-tidy", "tool", "clang-tidy-diff.py") + config.substitutions.append( + ('%clang_tidy_diff', + '%s %s' % (config.python_executable, clang_tidy_diff)) ) + run_clang_tidy = os.path.join( + config.test_source_root, "..", "clang-tidy", "tool", "run-clang-tidy.py") + config.substitutions.append( + ('%run_clang_tidy', + '%s %s' % (config.python_executable, run_clang_tidy)) ) +else: + # exclude the clang-tidy test directory + config.excludes.append('clang-tidy') clangd_benchmarks_dir = os.path.join(os.path.dirname(config.clang_tools_dir), "tools", "clang", "tools", "extra", diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index d123700ba..82f54d5cd 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -18,6 +18,8 @@ add_subdirectory(change-namespace) add_subdirectory(clang-apply-replacements) add_subdirectory(clang-move) add_subdirectory(clang-query) -add_subdirectory(clang-tidy) +if(CLANG_ENABLE_STATIC_ANALYZER) + add_subdirectory(clang-tidy) +endif() add_subdirectory(clangd) add_subdirectory(include-fixer) From 86dc55a63df5106c68fa1a8c329a537ad132dd68 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Mon, 1 Oct 2018 08:50:49 +0000 Subject: [PATCH 267/686] [clangd] Fix header mapping for std::string. NFC Some implementation has std::string declared in . git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343448 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/CanonicalIncludes.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clangd/index/CanonicalIncludes.cpp b/clangd/index/CanonicalIncludes.cpp index 4bb7fa6c9..757c8ff2f 100644 --- a/clangd/index/CanonicalIncludes.cpp +++ b/clangd/index/CanonicalIncludes.cpp @@ -143,6 +143,7 @@ void addSystemHeadersMapping(CanonicalIncludes *Includes) { {"std::basic_stringstream", ""}, {"std::istringstream", ""}, {"std::ostringstream", ""}, + {"std::string", ""}, {"std::stringbuf", ""}, {"std::stringstream", ""}, {"std::wistringstream", ""}, From 85e69b166d65e99e47f244ede665c56349e96048 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Mon, 1 Oct 2018 10:42:51 +0000 Subject: [PATCH 268/686] [clangd] Query dex index using query-style trigrams, not identifier-style trigrams git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343453 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Dex.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index b201fd87d..6e3b14875 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -148,7 +148,7 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, bool More = false; std::vector> TopLevelChildren; - const auto TrigramTokens = generateIdentifierTrigrams(Req.Query); + const auto TrigramTokens = generateQueryTrigrams(Req.Query); // Generate query trigrams and construct AND iterator over all query // trigrams. From 518e0898658a50a80762dd8e859edcd65cf2590b Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Mon, 1 Oct 2018 14:02:02 +0000 Subject: [PATCH 269/686] [clangd] Add "check-clangd" target Summary: So we don't have to run "check-clang-tools" (which builds and tests all clang tools) to verify our clangd-related change. It'd save waiting time for clangd developers. check-clangd (build ~1000 files, run ~340 tests) vs check-clang-tools (build ~3000 files, run ~1000 tests). In the future, we probably want to add similar target for other clang-tools (e.g. clang-tidy). Reviewers: sammccall Subscribers: mgorny, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52710 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343474 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/CMakeLists.txt | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cc03ff74f..13d909458 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -43,7 +43,6 @@ set(CLANG_TOOLS_TEST_DEPS # Individual tools we test. clang-apply-replacements clang-change-namespace - clangd clang-doc clang-include-fixer clang-move @@ -53,10 +52,7 @@ set(CLANG_TOOLS_TEST_DEPS modularize pp-trace - # These individual tools have no tests, add them here to make them compile - # together with check-clang-tools, so that we won't break them in the future. - clangd-indexer - dexp + check-clangd # Unit tests ExtraToolsUnitTests @@ -73,16 +69,6 @@ if(CLANG_ENABLE_STATIC_ANALYZER) ) endif() -set(llvm_utils - FileCheck count not - ) - -foreach(t ${llvm_utils}) - if(TARGET ${t}) - list(APPEND CLANG_TOOLS_TEST_DEPS ${t}) - endif() -endforeach() - add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression tests" ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${CLANG_TOOLS_TEST_DEPS} @@ -90,3 +76,18 @@ add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression t ) set_target_properties(check-clang-tools PROPERTIES FOLDER "Clang extra tools' tests") + +# Setup an individual test for building and testing clangd-only stuff. +set(CLANGD_TEST_DEPS + clangd + ClangdTests + # clangd-related tools which don't have tests, add them to the test to make + # sure we don't introduce new changes that break their compilations. + clangd-indexer + dexp +) +add_lit_testsuite(check-clangd "Running the Clangd regression tests" + ${CMAKE_CURRENT_BINARY_DIR}/Unit/clangd;${CMAKE_CURRENT_BINARY_DIR}/clangd + DEPENDS ${CLANGD_TEST_DEPS} +) +set_target_properties(check-clangd PROPERTIES FOLDER "Clangd tests") From 7dff09c8c551ee9a573ee007d344092d6bddce6a Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Mon, 1 Oct 2018 20:21:41 +0000 Subject: [PATCH 270/686] [clangd] exclude check-clangd from check-all, fix buildbot failure. check-clangd is included via check-clang-tools, otherwise we will run two instances of check-clangd in parallel when running 'check-all', which may have race condition. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343526 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 13d909458..a1bda5090 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -86,8 +86,11 @@ set(CLANGD_TEST_DEPS clangd-indexer dexp ) +# Exclude check-clangd from check-all, as check-clangd will launch via check-clang-tools. +set(EXCLUDE_FROM_ALL ON) add_lit_testsuite(check-clangd "Running the Clangd regression tests" ${CMAKE_CURRENT_BINARY_DIR}/Unit/clangd;${CMAKE_CURRENT_BINARY_DIR}/clangd DEPENDS ${CLANGD_TEST_DEPS} ) set_target_properties(check-clangd PROPERTIES FOLDER "Clangd tests") +set(EXCLUDE_FROM_ALL OFF) From 5351cf68d392ab18b475a803102ee65f1bb7ca4d Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Mon, 1 Oct 2018 20:24:22 +0000 Subject: [PATCH 271/686] [clang-tidy] Build it even without static analyzer Conditionally compile the parts of clang-tidy which depend on the static analyzer. Funnily enough, I made the patch to exclude this from the build in 2013, and it was committed with the comment that the tool should not be fully excluded, but only the parts of it which depend on the analyzer should be excluded. http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20130617/081797.html This commit implements that idea. Reviewed By: aaron.ballman Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D52334 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343528 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 2 -- clang-tidy/CMakeLists.txt | 13 +++++++-- clang-tidy/ClangTidy.cpp | 11 +++++++ clang-tidy/plugin/CMakeLists.txt | 7 ++++- clang-tidy/plugin/ClangTidyPlugin.cpp | 3 ++ clang-tidy/tool/CMakeLists.txt | 7 ++++- clang-tidy/tool/ClangTidyMain.cpp | 3 ++ docs/clang-tidy/index.rst | 4 +++ test/CMakeLists.txt | 16 ++++------ test/clang-tidy/enable-alpha-checks.cpp | 2 ++ test/clang-tidy/mpi-buffer-deref.cpp | 1 + test/clang-tidy/mpi-type-mismatch.cpp | 1 + test/clang-tidy/nolint.cpp | 1 + test/clang-tidy/read_file_config.cpp | 1 + test/clang-tidy/static-analyzer-config.cpp | 1 + test/clang-tidy/static-analyzer.cpp | 1 + test/clang-tidy/temporaries.cpp | 1 + test/lit.cfg | 34 ++++++++++------------ unittests/CMakeLists.txt | 4 +-- 19 files changed, 75 insertions(+), 38 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c434682cf..c3137adef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,8 @@ add_subdirectory(clang-apply-replacements) add_subdirectory(clang-reorder-fields) add_subdirectory(modularize) -if(CLANG_ENABLE_STATIC_ANALYZER) add_subdirectory(clang-tidy) add_subdirectory(clang-tidy-vs) -endif() add_subdirectory(change-namespace) add_subdirectory(clang-doc) diff --git a/clang-tidy/CMakeLists.txt b/clang-tidy/CMakeLists.txt index a87246942..0037268a1 100644 --- a/clang-tidy/CMakeLists.txt +++ b/clang-tidy/CMakeLists.txt @@ -21,12 +21,17 @@ add_clang_library(clangTidy clangLex clangRewrite clangSema - clangStaticAnalyzerCore - clangStaticAnalyzerFrontend clangTooling clangToolingCore ) +if(CLANG_ENABLE_STATIC_ANALYZER) + target_link_libraries(clangTidy PRIVATE + clangStaticAnalyzerCore + clangStaticAnalyzerFrontend + ) +endif() + add_subdirectory(android) add_subdirectory(abseil) add_subdirectory(boost) @@ -39,7 +44,9 @@ add_subdirectory(hicpp) add_subdirectory(llvm) add_subdirectory(misc) add_subdirectory(modernize) -add_subdirectory(mpi) +if(CLANG_ENABLE_STATIC_ANALYZER) + add_subdirectory(mpi) +endif() add_subdirectory(objc) add_subdirectory(performance) add_subdirectory(plugin) diff --git a/clang-tidy/ClangTidy.cpp b/clang-tidy/ClangTidy.cpp index d9eb1a511..729371fb8 100644 --- a/clang-tidy/ClangTidy.cpp +++ b/clang-tidy/ClangTidy.cpp @@ -23,6 +23,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Config/config.h" #include "clang/Format/Format.h" #include "clang/Frontend/ASTConsumers.h" #include "clang/Frontend/CompilerInstance.h" @@ -34,8 +35,10 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Rewrite/Frontend/FixItRewriter.h" #include "clang/Rewrite/Frontend/FrontendActions.h" +#if CLANG_ENABLE_STATIC_ANALYZER #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#endif // CLANG_ENABLE_STATIC_ANALYZER #include "clang/Tooling/DiagnosticsYaml.h" #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/ReplacementsYaml.h" @@ -56,6 +59,7 @@ namespace clang { namespace tidy { namespace { +#if CLANG_ENABLE_STATIC_ANALYZER static const char *AnalyzerCheckNamePrefix = "clang-analyzer-"; class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer { @@ -87,6 +91,7 @@ class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer { private: ClangTidyContext &Context; }; +#endif // CLANG_ENABLE_STATIC_ANALYZER class ErrorReporter { public: @@ -296,6 +301,7 @@ ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory( } } +#if CLANG_ENABLE_STATIC_ANALYZER static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts, AnalyzerOptionsRef AnalyzerOptions) { StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix); @@ -339,6 +345,7 @@ static CheckersList getCheckersControlList(ClangTidyContext &Context, } return List; } +#endif // CLANG_ENABLE_STATIC_ANALYZER std::unique_ptr ClangTidyASTConsumerFactory::CreateASTConsumer( @@ -380,6 +387,7 @@ ClangTidyASTConsumerFactory::CreateASTConsumer( if (!Checks.empty()) Consumers.push_back(Finder->newASTConsumer()); +#if CLANG_ENABLE_STATIC_ANALYZER AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts(); AnalyzerOptions->CheckersControlList = getCheckersControlList(Context, Context.canEnableAnalyzerAlphaCheckers()); @@ -395,6 +403,7 @@ ClangTidyASTConsumerFactory::CreateASTConsumer( new AnalyzerDiagnosticConsumer(Context)); Consumers.push_back(std::move(AnalysisConsumer)); } +#endif // CLANG_ENABLE_STATIC_ANALYZER return llvm::make_unique( std::move(Consumers), std::move(Profiling), std::move(Finder), std::move(Checks)); @@ -407,9 +416,11 @@ std::vector ClangTidyASTConsumerFactory::getCheckNames() { CheckNames.push_back(CheckFactory.first); } +#if CLANG_ENABLE_STATIC_ANALYZER for (const auto &AnalyzerCheck : getCheckersControlList( Context, Context.canEnableAnalyzerAlphaCheckers())) CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first); +#endif // CLANG_ENABLE_STATIC_ANALYZER std::sort(CheckNames.begin(), CheckNames.end()); return CheckNames; diff --git a/clang-tidy/plugin/CMakeLists.txt b/clang-tidy/plugin/CMakeLists.txt index 3540b2be7..7a12d7fd5 100644 --- a/clang-tidy/plugin/CMakeLists.txt +++ b/clang-tidy/plugin/CMakeLists.txt @@ -20,7 +20,6 @@ add_clang_library(clangTidyPlugin clangTidyLLVMModule clangTidyMiscModule clangTidyModernizeModule - clangTidyMPIModule clangTidyObjCModule clangTidyPerformanceModule clangTidyPortabilityModule @@ -28,3 +27,9 @@ add_clang_library(clangTidyPlugin clangTidyZirconModule clangTooling ) + +if(CLANG_ENABLE_STATIC_ANALYZER) + target_link_libraries(clangTidyPlugin PRIVATE + clangTidyMPIModule + ) +endif() diff --git a/clang-tidy/plugin/ClangTidyPlugin.cpp b/clang-tidy/plugin/ClangTidyPlugin.cpp index 345561205..22448939e 100644 --- a/clang-tidy/plugin/ClangTidyPlugin.cpp +++ b/clang-tidy/plugin/ClangTidyPlugin.cpp @@ -9,6 +9,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" +#include "clang/Config/config.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendPluginRegistry.h" #include "clang/Frontend/MultiplexConsumer.h" @@ -133,10 +134,12 @@ extern volatile int ModernizeModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED ModernizeModuleAnchorDestination = ModernizeModuleAnchorSource; +#if CLANG_ENABLE_STATIC_ANALYZER // This anchor is used to force the linker to link the MPIModule. extern volatile int MPIModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED MPIModuleAnchorDestination = MPIModuleAnchorSource; +#endif // This anchor is used to force the linker to link the ObjCModule. extern volatile int ObjCModuleAnchorSource; diff --git a/clang-tidy/tool/CMakeLists.txt b/clang-tidy/tool/CMakeLists.txt index a3ec4ae75..f58cfea55 100644 --- a/clang-tidy/tool/CMakeLists.txt +++ b/clang-tidy/tool/CMakeLists.txt @@ -29,7 +29,6 @@ target_link_libraries(clang-tidy clangTidyLLVMModule clangTidyMiscModule clangTidyModernizeModule - clangTidyMPIModule clangTidyObjCModule clangTidyPerformanceModule clangTidyPortabilityModule @@ -39,6 +38,12 @@ target_link_libraries(clang-tidy clangToolingCore ) +if(CLANG_ENABLE_STATIC_ANALYZER) + target_link_libraries(clang-tidy PRIVATE + clangTidyMPIModule + ) +endif() + install(PROGRAMS clang-tidy-diff.py DESTINATION share/clang COMPONENT clang-tidy) diff --git a/clang-tidy/tool/ClangTidyMain.cpp b/clang-tidy/tool/ClangTidyMain.cpp index b458b2931..d272a482a 100644 --- a/clang-tidy/tool/ClangTidyMain.cpp +++ b/clang-tidy/tool/ClangTidyMain.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "../ClangTidy.h" +#include "clang/Config/config.h" #include "clang/Tooling/CommonOptionsParser.h" #include "llvm/Support/Process.h" #include "llvm/Support/TargetSelect.h" @@ -534,10 +535,12 @@ extern volatile int ModernizeModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED ModernizeModuleAnchorDestination = ModernizeModuleAnchorSource; +#if CLANG_ENABLE_STATIC_ANALYZER // This anchor is used to force the linker to link the MPIModule. extern volatile int MPIModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED MPIModuleAnchorDestination = MPIModuleAnchorSource; +#endif // This anchor is used to force the linker to link the PerformanceModule. extern volatile int PerformanceModuleAnchorSource; diff --git a/docs/clang-tidy/index.rst b/docs/clang-tidy/index.rst index 3b7443c2d..ab165d8c5 100644 --- a/docs/clang-tidy/index.rst +++ b/docs/clang-tidy/index.rst @@ -337,6 +337,10 @@ There are a few tools particularly useful when developing clang-tidy checks: * `clang-check`_ with the ``-ast-dump`` (and optionally ``-ast-dump-filter``) provides a convenient way to dump AST of a C++ program. +If CMake is configured with ``CLANG_ENABLE_STATIC_ANALYZER``, +:program:`clang-tidy` will not be built with support for the +``clang-analyzer-*`` checks or the ``mpi-*`` checks. + .. _AST Matchers: http://clang.llvm.org/docs/LibASTMatchers.html .. _PPCallbacks: http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a1bda5090..e2d7e12ef 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -56,18 +56,14 @@ set(CLANG_TOOLS_TEST_DEPS # Unit tests ExtraToolsUnitTests - ) -if(CLANG_ENABLE_STATIC_ANALYZER) - list(APPEND CLANG_TOOLS_TEST_DEPS - # For the clang-tidy libclang integration test. - c-index-test - # clang-tidy tests require it. - clang-headers + # For the clang-tidy libclang integration test. + c-index-test + # clang-tidy tests require it. + clang-headers - clang-tidy - ) -endif() + clang-tidy + ) add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression tests" ${CMAKE_CURRENT_BINARY_DIR} diff --git a/test/clang-tidy/enable-alpha-checks.cpp b/test/clang-tidy/enable-alpha-checks.cpp index b1e513034..74bdfdb5d 100644 --- a/test/clang-tidy/enable-alpha-checks.cpp +++ b/test/clang-tidy/enable-alpha-checks.cpp @@ -1,3 +1,5 @@ +// REQUIRES: static-analyzer + // Check if '-allow-enabling-analyzer-alpha-checkers' is visible for users. // RUN: clang-tidy -help | not grep 'allow-enabling-analyzer-alpha-checkers' diff --git a/test/clang-tidy/mpi-buffer-deref.cpp b/test/clang-tidy/mpi-buffer-deref.cpp index 67304d230..47d58a5a6 100644 --- a/test/clang-tidy/mpi-buffer-deref.cpp +++ b/test/clang-tidy/mpi-buffer-deref.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: %check_clang_tidy %s mpi-buffer-deref %t -- -- -I %S/Inputs/mpi-type-mismatch #include "mpimock.h" diff --git a/test/clang-tidy/mpi-type-mismatch.cpp b/test/clang-tidy/mpi-type-mismatch.cpp index 5e230b89b..bf978dcc1 100644 --- a/test/clang-tidy/mpi-type-mismatch.cpp +++ b/test/clang-tidy/mpi-type-mismatch.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: %check_clang_tidy %s mpi-type-mismatch %t -- -- -I %S/Inputs/mpi-type-mismatch #include "mpimock.h" diff --git a/test/clang-tidy/nolint.cpp b/test/clang-tidy/nolint.cpp index 893e81929..24c37228d 100644 --- a/test/clang-tidy/nolint.cpp +++ b/test/clang-tidy/nolint.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: %check_clang_tidy %s google-explicit-constructor,clang-diagnostic-unused-variable,clang-analyzer-core.UndefinedBinaryOperatorResult %t -- -extra-arg=-Wunused-variable -- -I%S/Inputs/nolint #include "trigger_warning.h" diff --git a/test/clang-tidy/read_file_config.cpp b/test/clang-tidy/read_file_config.cpp index 3635c214f..3e39b4d63 100644 --- a/test/clang-tidy/read_file_config.cpp +++ b/test/clang-tidy/read_file_config.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: mkdir -p %T/read-file-config/ // RUN: cp %s %T/read-file-config/test.cpp // RUN: echo 'Checks: "-*,modernize-use-nullptr"' > %T/read-file-config/.clang-tidy diff --git a/test/clang-tidy/static-analyzer-config.cpp b/test/clang-tidy/static-analyzer-config.cpp index cee0a4c41..9ca87cf8d 100644 --- a/test/clang-tidy/static-analyzer-config.cpp +++ b/test/clang-tidy/static-analyzer-config.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: clang-tidy %s -checks='-*,clang-analyzer-unix.Malloc' -config='{CheckOptions: [{ key: "clang-analyzer-unix.Malloc:Optimistic", value: true}]}' -- | FileCheck %s typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); diff --git a/test/clang-tidy/static-analyzer.cpp b/test/clang-tidy/static-analyzer.cpp index b0015aa47..af9693ad0 100644 --- a/test/clang-tidy/static-analyzer.cpp +++ b/test/clang-tidy/static-analyzer.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: clang-tidy %s -checks='-*,clang-analyzer-*' -- | FileCheck %s extern void *malloc(unsigned long); extern void free(void *); diff --git a/test/clang-tidy/temporaries.cpp b/test/clang-tidy/temporaries.cpp index 3df4c60d4..0aa60ed3f 100644 --- a/test/clang-tidy/temporaries.cpp +++ b/test/clang-tidy/temporaries.cpp @@ -1,3 +1,4 @@ +// REQUIRES: static-analyzer // RUN: clang-tidy -checks='-*,clang-analyzer-core.NullDereference' %s -- | FileCheck %s struct NoReturnDtor { diff --git a/test/lit.cfg b/test/lit.cfg index 8e755c879..8f8ebaf98 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -119,24 +119,22 @@ if platform.system() not in ['Windows']: if config.clang_staticanalyzer: config.available_features.add('static-analyzer') - check_clang_tidy = os.path.join( - config.test_source_root, "clang-tidy", "check_clang_tidy.py") - config.substitutions.append( - ('%check_clang_tidy', - '%s %s' % (config.python_executable, check_clang_tidy)) ) - clang_tidy_diff = os.path.join( - config.test_source_root, "..", "clang-tidy", "tool", "clang-tidy-diff.py") - config.substitutions.append( - ('%clang_tidy_diff', - '%s %s' % (config.python_executable, clang_tidy_diff)) ) - run_clang_tidy = os.path.join( - config.test_source_root, "..", "clang-tidy", "tool", "run-clang-tidy.py") - config.substitutions.append( - ('%run_clang_tidy', - '%s %s' % (config.python_executable, run_clang_tidy)) ) -else: - # exclude the clang-tidy test directory - config.excludes.append('clang-tidy') + +check_clang_tidy = os.path.join( + config.test_source_root, "clang-tidy", "check_clang_tidy.py") +config.substitutions.append( + ('%check_clang_tidy', + '%s %s' % (config.python_executable, check_clang_tidy)) ) +clang_tidy_diff = os.path.join( + config.test_source_root, "..", "clang-tidy", "tool", "clang-tidy-diff.py") +config.substitutions.append( + ('%clang_tidy_diff', + '%s %s' % (config.python_executable, clang_tidy_diff)) ) +run_clang_tidy = os.path.join( + config.test_source_root, "..", "clang-tidy", "tool", "run-clang-tidy.py") +config.substitutions.append( + ('%run_clang_tidy', + '%s %s' % (config.python_executable, run_clang_tidy)) ) clangd_benchmarks_dir = os.path.join(os.path.dirname(config.clang_tools_dir), "tools", "clang", "tools", "extra", diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 82f54d5cd..d123700ba 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -18,8 +18,6 @@ add_subdirectory(change-namespace) add_subdirectory(clang-apply-replacements) add_subdirectory(clang-move) add_subdirectory(clang-query) -if(CLANG_ENABLE_STATIC_ANALYZER) - add_subdirectory(clang-tidy) -endif() +add_subdirectory(clang-tidy) add_subdirectory(clangd) add_subdirectory(include-fixer) From 531a2fe9bbb32b2c7e357a2ac9f5fdd910b96bff Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Mon, 1 Oct 2018 20:34:15 +0000 Subject: [PATCH 272/686] [clang-query] Sort command options git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343532 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-query/QueryParser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang-query/QueryParser.cpp b/clang-query/QueryParser.cpp index bb3d1b3a3..65bd3fc7d 100644 --- a/clang-query/QueryParser.cpp +++ b/clang-query/QueryParser.cpp @@ -162,12 +162,12 @@ QueryRef QueryParser::doParse() { ParsedQueryKind QKind = LexOrCompleteWord(this, CommandStr) .Case("", PQK_NoOp) .Case("help", PQK_Help) - .Case("m", PQK_Match, /*IsCompletion=*/false) .Case("let", PQK_Let) + .Case("m", PQK_Match, /*IsCompletion=*/false) .Case("match", PQK_Match) + .Case("quit", PQK_Quit) .Case("set", PQK_Set) .Case("unlet", PQK_Unlet) - .Case("quit", PQK_Quit) .Default(PQK_Invalid); switch (QKind) { From be09607ea6f581ae8ce58923cb5e719960d6bea7 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Mon, 1 Oct 2018 20:34:21 +0000 Subject: [PATCH 273/686] [clang-query] Add missing 'l' command handling The `let` command was added in commit 045c15ba (Add new 'let' command to bind arbitrary values into constants., 2014-04-23). The `let` command and the non-existant `l` command were documented in commit 233092a0 (Add 'let' to the help message., 2015-02-27). Implement the `l` command now for completeness. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343533 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-query/QueryParser.cpp | 1 + unittests/clang-query/QueryParserTest.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/clang-query/QueryParser.cpp b/clang-query/QueryParser.cpp index 65bd3fc7d..d05a0ba1c 100644 --- a/clang-query/QueryParser.cpp +++ b/clang-query/QueryParser.cpp @@ -162,6 +162,7 @@ QueryRef QueryParser::doParse() { ParsedQueryKind QKind = LexOrCompleteWord(this, CommandStr) .Case("", PQK_NoOp) .Case("help", PQK_Help) + .Case("l", PQK_Let, /*IsCompletion=*/false) .Case("let", PQK_Let) .Case("m", PQK_Match, /*IsCompletion=*/false) .Case("match", PQK_Match) diff --git a/unittests/clang-query/QueryParserTest.cpp b/unittests/clang-query/QueryParserTest.cpp index 5588ea3c7..9929de44f 100644 --- a/unittests/clang-query/QueryParserTest.cpp +++ b/unittests/clang-query/QueryParserTest.cpp @@ -103,6 +103,12 @@ TEST_F(QueryParserTest, LetUnlet) { EXPECT_TRUE(cast(Q)->Value.isMatcher()); EXPECT_TRUE(cast(Q)->Value.getMatcher().hasTypedMatcher()); + Q = parse("l foo decl()"); + ASSERT_TRUE(isa(Q)); + EXPECT_EQ("foo", cast(Q)->Name); + EXPECT_TRUE(cast(Q)->Value.isMatcher()); + EXPECT_TRUE(cast(Q)->Value.getMatcher().hasTypedMatcher()); + Q = parse("let bar \"str\""); ASSERT_TRUE(isa(Q)); EXPECT_EQ("bar", cast(Q)->Name); From 47fd32f3bbf14a4cb39517a14b4e12cc6228c97e Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Mon, 1 Oct 2018 20:45:39 +0000 Subject: [PATCH 274/686] [clang-query] Add missing quit test git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343535 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clang-query/QueryParserTest.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/unittests/clang-query/QueryParserTest.cpp b/unittests/clang-query/QueryParserTest.cpp index 9929de44f..c814a3d16 100644 --- a/unittests/clang-query/QueryParserTest.cpp +++ b/unittests/clang-query/QueryParserTest.cpp @@ -47,6 +47,15 @@ TEST_F(QueryParserTest, Help) { EXPECT_EQ("unexpected extra input: ' me'", cast(Q)->ErrStr); } +TEST_F(QueryParserTest, Quit) { + QueryRef Q = parse("quit"); + ASSERT_TRUE(isa(Q)); + + Q = parse("quit me"); + ASSERT_TRUE(isa(Q)); + EXPECT_EQ("unexpected extra input: ' me'", cast(Q)->ErrStr); +} + TEST_F(QueryParserTest, Set) { QueryRef Q = parse("set"); ASSERT_TRUE(isa(Q)); From 66746002bdee6ea0645dc585f1f145ffa9547009 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Mon, 1 Oct 2018 20:45:44 +0000 Subject: [PATCH 275/686] [clang-query] Test non-code-completion on single letter shortcuts git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343536 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clang-query/QueryParserTest.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/unittests/clang-query/QueryParserTest.cpp b/unittests/clang-query/QueryParserTest.cpp index c814a3d16..d6153a864 100644 --- a/unittests/clang-query/QueryParserTest.cpp +++ b/unittests/clang-query/QueryParserTest.cpp @@ -170,4 +170,14 @@ TEST_F(QueryParserTest, Complete) { EXPECT_EQ("Stmt(", Comps[0].TypedText); EXPECT_EQ("Matcher whileStmt(Matcher...)", Comps[0].DisplayText); + + Comps = QueryParser::complete("m", 1, QS); + ASSERT_EQ(1u, Comps.size()); + EXPECT_EQ("atch ", Comps[0].TypedText); + EXPECT_EQ("match", Comps[0].DisplayText); + + Comps = QueryParser::complete("l", 1, QS); + ASSERT_EQ(1u, Comps.size()); + EXPECT_EQ("et ", Comps[0].TypedText); + EXPECT_EQ("let", Comps[0].DisplayText); } From 35eb918a95a05e7d91bc7cf026f19db869354eaa Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Mon, 1 Oct 2018 21:10:30 +0000 Subject: [PATCH 276/686] Sort expected test output after previous commit git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343538 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clang-query/QueryParserTest.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/unittests/clang-query/QueryParserTest.cpp b/unittests/clang-query/QueryParserTest.cpp index d6153a864..4304aa980 100644 --- a/unittests/clang-query/QueryParserTest.cpp +++ b/unittests/clang-query/QueryParserTest.cpp @@ -153,12 +153,12 @@ TEST_F(QueryParserTest, Complete) { EXPECT_EQ("let", Comps[1].DisplayText); EXPECT_EQ("match ", Comps[2].TypedText); EXPECT_EQ("match", Comps[2].DisplayText); - EXPECT_EQ("set ", Comps[3].TypedText); - EXPECT_EQ("set", Comps[3].DisplayText); - EXPECT_EQ("unlet ", Comps[4].TypedText); - EXPECT_EQ("unlet", Comps[4].DisplayText); - EXPECT_EQ("quit", Comps[5].DisplayText); - EXPECT_EQ("quit ", Comps[5].TypedText); + EXPECT_EQ("quit ", Comps[3].TypedText); + EXPECT_EQ("quit", Comps[3].DisplayText); + EXPECT_EQ("set ", Comps[4].TypedText); + EXPECT_EQ("set", Comps[4].DisplayText); + EXPECT_EQ("unlet ", Comps[5].TypedText); + EXPECT_EQ("unlet", Comps[5].DisplayText); Comps = QueryParser::complete("set o", 5, QS); ASSERT_EQ(1u, Comps.size()); From 1cf3fc2b423acda6be748db985fdaf09b791bdf4 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Tue, 2 Oct 2018 09:38:20 +0000 Subject: [PATCH 277/686] [clang-tidy] NFC use CHECK-NOTES in tests for cppcoreguidelines-owning-memory Reviewers: alexfh, aaron.ballman, hokein Reviewed By: alexfh Subscribers: nemanjai, xazax.hun, kbarton, cfe-commits Differential Revision: https://reviews.llvm.org/D52687 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343564 91177308-0d34-0410-b5e6-96231b3b80d8 --- ...oreguidelines-owning-memory-containers.cpp | 11 +-- .../cppcoreguidelines-owning-memory.cpp | 88 +++++++++---------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/test/clang-tidy/cppcoreguidelines-owning-memory-containers.cpp b/test/clang-tidy/cppcoreguidelines-owning-memory-containers.cpp index 929b33d99..d39e697c6 100644 --- a/test/clang-tidy/cppcoreguidelines-owning-memory-containers.cpp +++ b/test/clang-tidy/cppcoreguidelines-owning-memory-containers.cpp @@ -37,22 +37,23 @@ int main() { // Rangebased looping in resource vector. for (auto *Element : OwnerStdVector) { Element = new int(42); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *' + // CHECK-NOTES: [[@LINE-1]]:5: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *' } for (auto *Element : OwnerStdVector) { delete Element; - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead - // CHECK-MESSAGES: [[@LINE-3]]:8: note: variable declared here + // CHECK-NOTES: [[@LINE-1]]:5: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead + // CHECK-NOTES: [[@LINE-3]]:8: note: variable declared here } // Indexbased looping in resource vector. for (int i = 0; i < 100; ++i) { OwnerStdVector[i] = new int(42); - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *' + // CHECK-NOTES: [[@LINE-1]]:5: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *' } for (int i = 0; i < 100; ++i) { delete OwnerStdVector[i]; - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead + // CHECK-NOTES: [[@LINE-1]]:5: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead + // CHECK-NOTES: [[@LINE-21]]:3: note: variable declared here // A note gets emitted here pointing to the return value of the operator[] from the // vector implementation. Maybe this is considered misleading. } diff --git a/test/clang-tidy/cppcoreguidelines-owning-memory.cpp b/test/clang-tidy/cppcoreguidelines-owning-memory.cpp index 47b12fbaf..72b13801c 100644 --- a/test/clang-tidy/cppcoreguidelines-owning-memory.cpp +++ b/test/clang-tidy/cppcoreguidelines-owning-memory.cpp @@ -36,17 +36,17 @@ gsl::owner returns_owner2() { return new int(42); } // int *returns_no_owner1() { return nullptr; } int *returns_no_owner2() { return new int(42); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: returning a newly created resource of type 'int *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-1]]:3: warning: returning a newly created resource of type 'int *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>' } int *returns_no_owner3() { int *should_be_owner = new int(42); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' return should_be_owner; } int *returns_no_owner4() { gsl::owner owner = new int(42); return owner; - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: returning a newly created resource of type 'int *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-1]]:3: warning: returning a newly created resource of type 'int *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>' } unique_ptr returns_no_owner5() { @@ -71,11 +71,11 @@ void test_assignment_and_initialization() { int stack_int2; gsl::owner owned_int1 = &stack_int1; // BAD - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' gsl::owner owned_int2; owned_int2 = &stack_int2; // BAD since no owner, bad since uninitialized - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *' gsl::owner owned_int3 = new int(42); // Good owned_int3 = nullptr; // Good @@ -92,41 +92,41 @@ void test_assignment_and_initialization() { owned_int6 = owned_int3; // BAD, because reassignment without resource release auto owned_int7 = returns_owner1(); // Bad, since type deduction eliminates the owner wrapper - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' - // CHECK-MESSAGES: [[@LINE-2]]:3: note: type deduction did not result in an owner + // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-2]]:3: note: type deduction did not result in an owner const auto owned_int8 = returns_owner2(); // Bad, since type deduction eliminates the owner wrapper - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *const' with a newly created 'gsl::owner<>' - // CHECK-MESSAGES: [[@LINE-2]]:3: note: type deduction did not result in an owner + // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *const' with a newly created 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-2]]:3: note: type deduction did not result in an owner gsl::owner owned_int9 = returns_owner1(); // Ok int *unowned_int3 = returns_owner1(); // Bad - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' gsl::owner owned_int10; owned_int10 = returns_owner1(); // Ok int *unowned_int4; unowned_int4 = returns_owner1(); // Bad - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *' + // CHECK-NOTES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *' gsl::owner owned_int11 = returns_no_owner1(); // Bad since no owner - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' gsl::owner owned_int12; owned_int12 = returns_no_owner1(); // Bad since no owner - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'int *' int *unowned_int5 = returns_no_owner1(); // Ok int *unowned_int6; unowned_int6 = returns_no_owner1(); // Ok int *unowned_int7 = new int(42); // Bad, since resource not assigned to an owner - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' int *unowned_int8; unowned_int8 = new int(42); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *' + // CHECK-NOTES: [[@LINE-1]]:3: warning: assigning newly created 'gsl::owner<>' to non-owner 'int *' gsl::owner owned_int13 = nullptr; // Ok } @@ -139,16 +139,16 @@ void test_deletion() { delete[] owned_int2; // Good int *unowned_int1 = new int(42); // BAD, since new creates and owner - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' delete unowned_int1; // BAD, since no owner - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead - // CHECK-MESSAGES: [[@LINE-4]]:3: note: variable declared here + // CHECK-NOTES: [[@LINE-1]]:3: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead + // CHECK-NOTES: [[@LINE-4]]:3: note: variable declared here int *unowned_int2 = new int[42]; // BAD, since new creates and owner - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'int *' with a newly created 'gsl::owner<>' delete[] unowned_int2; // BAD since no owner - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead - // CHECK-MESSAGES: [[@LINE-4]]:3: note: variable declared here + // CHECK-NOTES: [[@LINE-1]]:3: warning: deleting a pointer through a type that is not marked 'gsl::owner<>'; consider using a smart pointer instead + // CHECK-NOTES: [[@LINE-4]]:3: note: variable declared here delete new int(42); // Technically ok, but stupid delete[] new int[42]; // Technically ok, but stupid @@ -158,17 +158,17 @@ void test_owner_function_calls() { int stack_int = 42; int *unowned_int1 = &stack_int; takes_owner(&stack_int); // BAD - // CHECK-MESSAGES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *' takes_owner(unowned_int1); // BAD - // CHECK-MESSAGES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *' gsl::owner owned_int1 = new int(42); takes_owner(owned_int1); // Ok takes_owner_and_more(42, &stack_int, 42.0f); // BAD - // CHECK-MESSAGES: [[@LINE-1]]:28: warning: expected argument of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:28: warning: expected argument of type 'gsl::owner<>'; got 'int *' takes_owner_and_more(42, unowned_int1, 42.0f); // BAD - // CHECK-MESSAGES: [[@LINE-1]]:28: warning: expected argument of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:28: warning: expected argument of type 'gsl::owner<>'; got 'int *' takes_owner_and_more(42, new int(42), 42.0f); // Ok, since new is consumed by owner takes_owner_and_more(42, owned_int1, 42.0f); // Ok, since owner as argument @@ -176,11 +176,11 @@ void test_owner_function_calls() { takes_templated_owner(owned_int1); // Ok takes_templated_owner(new int(42)); // Ok takes_templated_owner(unowned_int1); // Bad - // CHECK-MESSAGES: [[@LINE-1]]:25: warning: expected argument of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:25: warning: expected argument of type 'gsl::owner<>'; got 'int *' takes_owner(returns_owner1()); // Ok takes_owner(returns_no_owner1()); // BAD - // CHECK-MESSAGES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:15: warning: expected argument of type 'gsl::owner<>'; got 'int *' } void test_unowned_function_calls() { @@ -192,10 +192,10 @@ void test_unowned_function_calls() { takes_pointer(unowned_int1); // Ok takes_pointer(owned_int1); // Ok takes_pointer(new int(42)); // Bad, since new creates and owner - // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>' takes_pointer(returns_owner1()); // Bad - // CHECK-MESSAGES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-1]]:17: warning: initializing non-owner argument of type 'int *' with a newly created 'gsl::owner<>' takes_pointer(returns_no_owner1()); // Ok } @@ -239,7 +239,7 @@ struct ClassWithOwner { // Does not define destructor, necess ClassWithOwner() : owner_var(nullptr) {} // Ok ClassWithOwner(ArbitraryClass &other) : owner_var(&other) {} - // CHECK-MESSAGES: [[@LINE-1]]:43: warning: expected initialization of owner member variable with value of type 'gsl::owner<>'; got 'ArbitraryClass *' + // CHECK-NOTES: [[@LINE-1]]:43: warning: expected initialization of owner member variable with value of type 'gsl::owner<>'; got 'ArbitraryClass *' ClassWithOwner(gsl::owner other) : owner_var(other) {} // Ok @@ -249,7 +249,7 @@ struct ClassWithOwner { // Does not define destructor, necess ClassWithOwner(ArbitraryClass *bad_data, int /* unused */, int /* unused */) { owner_var = bad_data; - // CHECK-MESSAGES: [[@LINE-1]]:5: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *' + // CHECK-NOTES: [[@LINE-1]]:5: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *' } ClassWithOwner(ClassWithOwner &&other) : owner_var{other.owner_var} {} // Ok @@ -264,19 +264,19 @@ struct ClassWithOwner { // Does not define destructor, necess gsl::owner buggy_but_returns_owner() { return owner_var; } gsl::owner owner_var; - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'ClassWithOwner' to implement a destructor to release the owned resource + // CHECK-NOTES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'ClassWithOwner' to implement a destructor to release the owned resource }; class DefaultedDestructor { // Bad since default constructor with owner ~DefaultedDestructor() = default; // Bad, since will not destroy the owner gsl::owner Owner; - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'DefaultedDestructor' to implement a destructor to release the owned resource + // CHECK-NOTES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'DefaultedDestructor' to implement a destructor to release the owned resource }; struct DeletedDestructor { ~DeletedDestructor() = delete; gsl::owner Owner; - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'DeletedDestructor' to implement a destructor to release the owned resource + // CHECK-NOTES: [[@LINE-1]]:3: warning: member variable of type 'gsl::owner<>' requires the class 'DeletedDestructor' to implement a destructor to release the owned resource }; void test_class_with_owner() { @@ -286,18 +286,18 @@ void test_class_with_owner() { ClassWithOwner C3{gsl::owner(new ArbitraryClass)}; // Ok const auto Owner1 = C3.buggy_but_returns_owner(); // BAD, deduces Owner1 to ArbitraryClass *const - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'ArbitraryClass *const' with a newly created 'gsl::owner<>' - // CHECK-MESSAGES: [[@LINE-2]]:3: note: type deduction did not result in an owner + // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'ArbitraryClass *const' with a newly created 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-2]]:3: note: type deduction did not result in an owner auto Owner2 = C2.buggy_but_returns_owner(); // BAD, deduces Owner2 to ArbitraryClass * - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: initializing non-owner 'ArbitraryClass *' with a newly created 'gsl::owner<>' - // CHECK-MESSAGES: [[@LINE-2]]:3: note: type deduction did not result in an owner + // CHECK-NOTES: [[@LINE-1]]:3: warning: initializing non-owner 'ArbitraryClass *' with a newly created 'gsl::owner<>' + // CHECK-NOTES: [[@LINE-2]]:3: note: type deduction did not result in an owner Owner2 = &A; // Ok, since type deduction did NOT result in owner gsl::owner Owner3 = C1.buggy_but_returns_owner(); // Ok, still an owner Owner3 = &A; // Bad, since assignment of non-owner to owner - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *' + // CHECK-NOTES: [[@LINE-1]]:3: warning: expected assignment source to be of type 'gsl::owner<>'; got 'ArbitraryClass *' } template @@ -311,7 +311,7 @@ struct HeapArray { // Ok, since destruc _data[i] = val; // Ok } HeapArray(int size, T val, int *problematic) : _data{problematic}, size(size) {} // Bad - // CHECK-MESSAGES: [[@LINE-1]]:50: warning: expected initialization of owner member variable with value of type 'gsl::owner<>'; got 'void' + // CHECK-NOTES: [[@LINE-1]]:50: warning: expected initialization of owner member variable with value of type 'gsl::owner<>'; got 'void' // FIXME: void is incorrect type, probably wrong thing matched HeapArray(HeapArray &&other) : _data(other._data), size(other.size) { // Ok @@ -344,7 +344,7 @@ void test_inner_template() { int *NonOwningPtr = Array1.data(); // Ok gsl::owner OwningPtr = Array1.data(); // Bad, since it does not return the owner - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' } // FIXME: Typededuction removes the owner - wrapper, therefore gsl::owner can not be used @@ -364,8 +364,8 @@ struct TemplateValue { template void template_function(T t) { gsl::owner owner_t = t; // Probably bad, since type deduction still wrong - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'T' - // CHECK-MESSAGES: [[@LINE-2]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'T' + // CHECK-NOTES: [[@LINE-2]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' } // FIXME: Same typededcution problems @@ -384,7 +384,7 @@ void test_templates() { TemplateValue NonOwner1(new int(42)); // Bad, T is int *, hence dynamic memory to non-owner gsl::owner IntOwner1 = NonOwner1.getVal(); // Bad, since owner initialized with non-owner - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' + // CHECK-NOTES: [[@LINE-1]]:3: warning: expected initialization with value of type 'gsl::owner<>'; got 'int *' template_function(IntOwner1); // Ok, but not actually ok, since type deduction removes owner template_function(stack_ptr1); // Bad, but type deduction gets it wrong From 0c5ece807052148be2b975b26e3be945d77d5007 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Tue, 2 Oct 2018 09:38:26 +0000 Subject: [PATCH 278/686] [clang-tidy] NFC use CHECK-NOTES in test for cppgoreguidelines-avoid-goto Reviewers: alexfh, aaron.ballman, hokein Reviewed By: alexfh Subscribers: nemanjai, xazax.hun, kbarton, cfe-commits Differential Revision: https://reviews.llvm.org/D52686 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343565 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clang-tidy/cppcoreguidelines-avoid-goto.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/clang-tidy/cppcoreguidelines-avoid-goto.cpp b/test/clang-tidy/cppcoreguidelines-avoid-goto.cpp index 213947f37..ee236bc44 100644 --- a/test/clang-tidy/cppcoreguidelines-avoid-goto.cpp +++ b/test/clang-tidy/cppcoreguidelines-avoid-goto.cpp @@ -5,8 +5,8 @@ void noop() {} int main() { noop(); goto jump_to_me; - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid using 'goto' for flow control - // CHECK-MESSAGES: [[@LINE+3]]:1: note: label defined here + // CHECK-NOTES: [[@LINE-1]]:3: warning: avoid using 'goto' for flow control + // CHECK-NOTES: [[@LINE+3]]:1: note: label defined here noop(); jump_to_me:; @@ -14,14 +14,14 @@ jump_to_me:; jump_backwards:; noop(); goto jump_backwards; - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: avoid using 'goto' for flow control - // CHECK-MESSAGES: [[@LINE-4]]:1: note: label defined here + // CHECK-NOTES: [[@LINE-1]]:3: warning: avoid using 'goto' for flow control + // CHECK-NOTES: [[@LINE-4]]:1: note: label defined here goto jump_in_line; ; jump_in_line:; - // CHECK-MESSAGES: [[@LINE-3]]:3: warning: avoid using 'goto' for flow control - // CHECK-MESSAGES: [[@LINE-2]]:1: note: label defined here + // CHECK-NOTES: [[@LINE-3]]:3: warning: avoid using 'goto' for flow control + // CHECK-NOTES: [[@LINE-2]]:1: note: label defined here // Test the GNU extension https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html some_label:; @@ -132,8 +132,8 @@ void jump_out_backwards() { for (int j = 0; j < 10; ++j) { if (i * j > 80) goto before_the_loop; - // CHECK-MESSAGES: [[@LINE-1]]:9: warning: avoid using 'goto' for flow control - // CHECK-MESSAGES: [[@LINE-8]]:1: note: label defined here + // CHECK-NOTES: [[@LINE-1]]:9: warning: avoid using 'goto' for flow control + // CHECK-NOTES: [[@LINE-8]]:1: note: label defined here } } } From 1821c7b8314556e9f3c81542ad463b911ee725ea Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Tue, 2 Oct 2018 09:42:17 +0000 Subject: [PATCH 279/686] [clangd] Remove override result handling logic from clangd Summary: Since we plan to move handling of override suggestions to Sema with D52225 this patch just makes sure clangd-side has no logic related to that anymore and updates tests. Reviewers: ioeric, ilya-biryukov Reviewed By: ioeric Subscribers: MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D52226 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343567 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 99 ++++------------------------------------- 1 file changed, 9 insertions(+), 90 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index cd237268d..48e495afa 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -199,55 +199,6 @@ static llvm::Expected toHeaderFile(StringRef Header, return HeaderFile{std::move(*Resolved), /*Verbatim=*/false}; } -// First traverses all method definitions inside current class/struct/union -// definition. Than traverses base classes to find virtual methods that haven't -// been overriden within current context. -// FIXME(kadircet): Currently we cannot see declarations below completion point. -// It is because Sema gets run only upto completion point. Need to find a -// solution to run it for the whole class/struct/union definition. -static std::vector -getNonOverridenMethodCompletionResults(const DeclContext *DC, Sema *S) { - const auto *CR = llvm::dyn_cast(DC); - // If not inside a class/struct/union return empty. - if (!CR) - return {}; - // First store overrides within current class. - // These are stored by name to make querying fast in the later step. - llvm::StringMap> Overrides; - for (auto *Method : CR->methods()) { - if (!Method->isVirtual() || !Method->getIdentifier()) - continue; - Overrides[Method->getName()].push_back(Method); - } - - std::vector Results; - for (const auto &Base : CR->bases()) { - const auto *BR = Base.getType().getTypePtr()->getAsCXXRecordDecl(); - if (!BR) - continue; - for (auto *Method : BR->methods()) { - if (!Method->isVirtual() || !Method->getIdentifier()) - continue; - const auto it = Overrides.find(Method->getName()); - bool IsOverriden = false; - if (it != Overrides.end()) { - for (auto *MD : it->second) { - // If the method in current body is not an overload of this virtual - // function, then it overrides this one. - if (!S->IsOverload(MD, Method, false)) { - IsOverriden = true; - break; - } - } - } - if (!IsOverriden) - Results.emplace_back(Method, 0); - } - } - - return Results; -} - /// A code completion result, in clang-native form. /// It may be promoted to a CompletionItem if it's among the top-ranked results. struct CompletionCandidate { @@ -257,9 +208,6 @@ struct CompletionCandidate { const Symbol *IndexResult = nullptr; llvm::SmallVector RankedIncludeHeaders; - // States whether this item is an override suggestion. - bool IsOverride = false; - // Returns a token identifying the overload set this is part of. // 0 indicates it's not part of any overload set. size_t overloadSet() const { @@ -447,8 +395,6 @@ struct CodeCompletionBuilder { Completion.Documentation = getDocComment(ASTCtx, *C.SemaResult, /*CommentsFromHeader=*/false); } - if (C.IsOverride) - S.OverrideSuffix = true; } CodeCompletion build() { @@ -456,12 +402,6 @@ struct CodeCompletionBuilder { Completion.Signature = summarizeSignature(); Completion.SnippetSuffix = summarizeSnippet(); Completion.BundleSize = Bundled.size(); - if (summarizeOverride()) { - Completion.Name = Completion.ReturnType + ' ' + - std::move(Completion.Name) + - std::move(Completion.Signature) + " override"; - Completion.Signature.clear(); - } return std::move(Completion); } @@ -470,7 +410,6 @@ struct CodeCompletionBuilder { std::string SnippetSuffix; std::string Signature; std::string ReturnType; - bool OverrideSuffix; }; // If all BundledEntrys have the same value for a property, return it. @@ -548,12 +487,6 @@ struct CodeCompletionBuilder { return "(…)"; } - bool summarizeOverride() const { - if (auto *OverrideSuffix = onlyValue<&BundledEntry::OverrideSuffix>()) - return *OverrideSuffix; - return false; - } - ASTContext &ASTCtx; CodeCompletion Completion; SmallVector Bundled; @@ -1418,11 +1351,8 @@ class CodeCompleteFlow { ? queryIndex() : SymbolSlab(); trace::Span Tracer("Populate CodeCompleteResult"); - // Merge Sema, Index and Override results, score them, and pick the - // winners. - const auto Overrides = getNonOverridenMethodCompletionResults( - Recorder->CCSema->CurContext, Recorder->CCSema); - auto Top = mergeResults(Recorder->Results, IndexResults, Overrides); + // Merge Sema and Index results, score them, and pick the winners. + auto Top = mergeResults(Recorder->Results, IndexResults); CodeCompleteResult Output; // Convert the results to final form, assembling the expensive strings. @@ -1474,26 +1404,22 @@ class CodeCompleteFlow { return std::move(ResultsBuilder).build(); } - // Merges Sema, Index and Override results where possible, to form - // CompletionCandidates. Groups overloads if desired, to form - // CompletionCandidate::Bundles. The bundles are scored and top results are - // returned, best to worst. + // Merges Sema and Index results where possible, to form CompletionCandidates. + // Groups overloads if desired, to form CompletionCandidate::Bundles. The + // bundles are scored and top results are returned, best to worst. std::vector mergeResults(const std::vector &SemaResults, - const SymbolSlab &IndexResults, - const std::vector &OverrideResults) { + const SymbolSlab &IndexResults) { trace::Span Tracer("Merge and score results"); std::vector Bundles; llvm::DenseMap BundleLookup; auto AddToBundles = [&](const CodeCompletionResult *SemaResult, - const Symbol *IndexResult, - bool IsOverride) { + const Symbol *IndexResult) { CompletionCandidate C; C.SemaResult = SemaResult; C.IndexResult = IndexResult; if (C.IndexResult) C.RankedIncludeHeaders = getRankedIncludes(*C.IndexResult); - C.IsOverride = IsOverride; C.Name = IndexResult ? IndexResult->Name : Recorder->getName(*SemaResult); if (auto OverloadSet = Opts.BundleOverloads ? C.overloadSet() : 0) { auto Ret = BundleLookup.try_emplace(OverloadSet, Bundles.size()); @@ -1520,19 +1446,12 @@ class CodeCompleteFlow { }; // Emit all Sema results, merging them with Index results if possible. for (auto &SemaResult : Recorder->Results) - AddToBundles(&SemaResult, CorrespondingIndexResult(SemaResult), false); - // Handle OverrideResults the same way we deal with SemaResults. Since these - // results use the same structs as a SemaResult it is safe to do that, but - // we need to make sure we dont' duplicate things in future if Sema starts - // to provide them as well. - for (auto &OverrideResult : OverrideResults) - AddToBundles(&OverrideResult, CorrespondingIndexResult(OverrideResult), - true); + AddToBundles(&SemaResult, CorrespondingIndexResult(SemaResult)); // Now emit any Index-only results. for (const auto &IndexResult : IndexResults) { if (UsedIndexResults.count(&IndexResult)) continue; - AddToBundles(/*SemaResult=*/nullptr, &IndexResult, false); + AddToBundles(/*SemaResult=*/nullptr, &IndexResult); } // We only keep the best N results at any time, in "native" format. TopN Top( From d6e779e064a2ec2931119797bd948efd6ea71969 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 2 Oct 2018 10:43:55 +0000 Subject: [PATCH 280/686] [clangd] Cache FS stat() calls when building preamble. Summary: The file stats can be reused when preamble is reused (e.g. code completion). It's safe to assume that cached status is not outdated as we assume preamble files to remain unchanged. On real file system, this made code completion ~20% faster on a measured file (with big preamble). The preamble build time doesn't change much. Reviewers: sammccall, ilya-biryukov Reviewed By: sammccall Subscribers: mgorny, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52419 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343576 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 1 + clangd/ClangdServer.cpp | 13 ++--- clangd/ClangdUnit.cpp | 26 +++++---- clangd/ClangdUnit.h | 7 ++- clangd/CodeComplete.cpp | 27 ++++++---- clangd/CodeComplete.h | 8 +-- clangd/FS.cpp | 92 ++++++++++++++++++++++++++++++++ clangd/FS.h | 65 ++++++++++++++++++++++ unittests/clangd/CMakeLists.txt | 1 + unittests/clangd/ClangdTests.cpp | 65 ++++++++++++++++++++++ unittests/clangd/FSTests.cpp | 46 ++++++++++++++++ unittests/clangd/TestFS.cpp | 1 + 12 files changed, 319 insertions(+), 33 deletions(-) create mode 100644 clangd/FS.cpp create mode 100644 clangd/FS.h create mode 100644 unittests/clangd/FSTests.cpp diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index 0894a927e..bbe8df007 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -21,6 +21,7 @@ add_clang_library(clangDaemon DraftStore.cpp FindSymbols.cpp FileDistance.cpp + FS.cpp FuzzyMatch.cpp GlobalCompilationDatabase.cpp Headers.cpp diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 153dce294..01fbe34fa 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -181,8 +181,6 @@ void ClangdServer::codeComplete(PathRef File, Position Pos, if (isCancelled()) return CB(llvm::make_error()); - auto PreambleData = IP->Preamble; - llvm::Optional SpecFuzzyFind; if (CodeCompleteOpts.Index && CodeCompleteOpts.SpeculativeIndexRequest) { SpecFuzzyFind.emplace(); @@ -195,10 +193,8 @@ void ClangdServer::codeComplete(PathRef File, Position Pos, // FIXME(ibiryukov): even if Preamble is non-null, we may want to check // both the old and the new version in case only one of them matches. CodeCompleteResult Result = clangd::codeComplete( - File, IP->Command, PreambleData ? &PreambleData->Preamble : nullptr, - PreambleData ? PreambleData->Includes : IncludeStructure(), - IP->Contents, Pos, FS, PCHs, CodeCompleteOpts, - SpecFuzzyFind ? SpecFuzzyFind.getPointer() : nullptr); + File, IP->Command, IP->Preamble, IP->Contents, Pos, FS, PCHs, + CodeCompleteOpts, SpecFuzzyFind ? SpecFuzzyFind.getPointer() : nullptr); { clang::clangd::trace::Span Tracer("Completion results callback"); CB(std::move(Result)); @@ -231,9 +227,8 @@ void ClangdServer::signatureHelp(PathRef File, Position Pos, return CB(IP.takeError()); auto PreambleData = IP->Preamble; - CB(clangd::signatureHelp(File, IP->Command, - PreambleData ? &PreambleData->Preamble : nullptr, - IP->Contents, Pos, FS, PCHs, Index)); + CB(clangd::signatureHelp(File, IP->Command, PreambleData, IP->Contents, Pos, + FS, PCHs, Index)); }; // Unlike code completion, we wait for an up-to-date preamble here. diff --git a/clangd/ClangdUnit.cpp b/clangd/ClangdUnit.cpp index 6c184ff06..2ad6dc69c 100644 --- a/clangd/ClangdUnit.cpp +++ b/clangd/ClangdUnit.cpp @@ -246,9 +246,10 @@ const IncludeStructure &ParsedAST::getIncludeStructure() const { } PreambleData::PreambleData(PrecompiledPreamble Preamble, - std::vector Diags, IncludeStructure Includes) + std::vector Diags, IncludeStructure Includes, + std::unique_ptr StatCache) : Preamble(std::move(Preamble)), Diags(std::move(Diags)), - Includes(std::move(Includes)) {} + Includes(std::move(Includes)), StatCache(std::move(StatCache)) {} ParsedAST::ParsedAST(std::shared_ptr Preamble, std::unique_ptr Clang, @@ -334,9 +335,12 @@ std::shared_ptr clangd::buildPreamble( // We proceed anyway, our lit-tests rely on results for non-existing working // dirs. } + + auto StatCache = llvm::make_unique(); auto BuiltPreamble = PrecompiledPreamble::Build( - CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine, Inputs.FS, PCHs, - StoreInMemory, SerializedDeclsCollector); + CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine, + StatCache->getProducingFS(Inputs.FS), PCHs, StoreInMemory, + SerializedDeclsCollector); // When building the AST for the main file, we do want the function // bodies. @@ -347,7 +351,7 @@ std::shared_ptr clangd::buildPreamble( FileName); return std::make_shared( std::move(*BuiltPreamble), PreambleDiagnostics.take(), - SerializedDeclsCollector.takeIncludes()); + SerializedDeclsCollector.takeIncludes(), std::move(StatCache)); } else { elog("Could not build a preamble for file {0}", FileName); return nullptr; @@ -361,15 +365,19 @@ llvm::Optional clangd::buildAST( trace::Span Tracer("BuildAST"); SPAN_ATTACH(Tracer, "File", FileName); - if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) { + auto VFS = Inputs.FS; + if (Preamble && Preamble->StatCache) + VFS = Preamble->StatCache->getConsumingFS(std::move(VFS)); + if (VFS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) { log("Couldn't set working directory when building the preamble."); // We proceed anyway, our lit-tests rely on results for non-existing working // dirs. } - return ParsedAST::build( - llvm::make_unique(*Invocation), Preamble, - llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents), PCHs, Inputs.FS); + return ParsedAST::build(llvm::make_unique(*Invocation), + Preamble, + llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents), + PCHs, std::move(VFS)); } SourceLocation clangd::getBeginningOfIdentifier(ParsedAST &Unit, diff --git a/clangd/ClangdUnit.h b/clangd/ClangdUnit.h index f739270a1..184a7a6c5 100644 --- a/clangd/ClangdUnit.h +++ b/clangd/ClangdUnit.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H #include "Diagnostics.h" +#include "FS.h" #include "Function.h" #include "Headers.h" #include "Path.h" @@ -45,7 +46,8 @@ namespace clangd { // Stores Preamble and associated data. struct PreambleData { PreambleData(PrecompiledPreamble Preamble, std::vector Diags, - IncludeStructure Includes); + IncludeStructure Includes, + std::unique_ptr StatCache); tooling::CompileCommand CompileCommand; PrecompiledPreamble Preamble; @@ -53,6 +55,9 @@ struct PreambleData { // Processes like code completions and go-to-definitions will need #include // information, and their compile action skips preamble range. IncludeStructure Includes; + // Cache of FS operations performed when building the preamble. + // When reusing a preamble, this cache can be consumed to save IO. + std::unique_ptr StatCache; }; /// Information required to run clang, e.g. to parse AST or do code completion. diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 48e495afa..9e2f162b4 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -20,6 +20,7 @@ #include "CodeComplete.h" #include "AST.h" +#include "ClangdUnit.h" #include "CodeCompletionStrings.h" #include "Compiler.h" #include "Diagnostics.h" @@ -986,7 +987,7 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { struct SemaCompleteInput { PathRef FileName; const tooling::CompileCommand &Command; - PrecompiledPreamble const *Preamble; + const PreambleData *Preamble; StringRef Contents; Position Pos; IntrusiveRefCntPtr VFS; @@ -1010,12 +1011,15 @@ bool semaCodeComplete(std::unique_ptr Consumer, // working dirs. } + IntrusiveRefCntPtr VFS = Input.VFS; + if (Input.Preamble && Input.Preamble->StatCache) + VFS = Input.Preamble->StatCache->getConsumingFS(std::move(VFS)); IgnoreDiagnostics DummyDiagsConsumer; auto CI = createInvocationFromCommandLine( ArgStrs, CompilerInstance::createDiagnostics(new DiagnosticOptions, &DummyDiagsConsumer, false), - Input.VFS); + VFS); if (!CI) { elog("Couldn't create CompilerInvocation"); return false; @@ -1054,8 +1058,10 @@ bool semaCodeComplete(std::unique_ptr Consumer, // NOTE: we must call BeginSourceFile after prepareCompilerInstance. Otherwise // the remapped buffers do not get freed. auto Clang = prepareCompilerInstance( - std::move(CI), CompletingInPreamble ? nullptr : Input.Preamble, - std::move(ContentsBuffer), std::move(Input.PCHs), std::move(Input.VFS), + std::move(CI), + (Input.Preamble && !CompletingInPreamble) ? &Input.Preamble->Preamble + : nullptr, + std::move(ContentsBuffer), std::move(Input.PCHs), std::move(VFS), DummyDiagsConsumer); Clang->getPreprocessorOpts().SingleFileParseMode = CompletingInPreamble; Clang->setCodeCompletionConsumer(Consumer.release()); @@ -1565,19 +1571,20 @@ speculateCompletionFilter(llvm::StringRef Content, Position Pos) { CodeCompleteResult codeComplete(PathRef FileName, const tooling::CompileCommand &Command, - PrecompiledPreamble const *Preamble, - const IncludeStructure &PreambleInclusions, StringRef Contents, - Position Pos, IntrusiveRefCntPtr VFS, + const PreambleData *Preamble, StringRef Contents, Position Pos, + IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, CodeCompleteOptions Opts, SpeculativeFuzzyFind *SpecFuzzyFind) { - return CodeCompleteFlow(FileName, PreambleInclusions, SpecFuzzyFind, Opts) + return CodeCompleteFlow(FileName, + Preamble ? Preamble->Includes : IncludeStructure(), + SpecFuzzyFind, Opts) .run({FileName, Command, Preamble, Contents, Pos, VFS, PCHs}); } SignatureHelp signatureHelp(PathRef FileName, const tooling::CompileCommand &Command, - PrecompiledPreamble const *Preamble, - StringRef Contents, Position Pos, + const PreambleData *Preamble, StringRef Contents, + Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, const SymbolIndex *Index) { diff --git a/clangd/CodeComplete.h b/clangd/CodeComplete.h index 92fa2279a..d0c8a7067 100644 --- a/clangd/CodeComplete.h +++ b/clangd/CodeComplete.h @@ -16,6 +16,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H +#include "ClangdUnit.h" #include "Headers.h" #include "Logger.h" #include "Path.h" @@ -221,8 +222,7 @@ struct SpeculativeFuzzyFind { /// destroyed when the async request finishes. CodeCompleteResult codeComplete(PathRef FileName, const tooling::CompileCommand &Command, - PrecompiledPreamble const *Preamble, - const IncludeStructure &PreambleInclusions, + const PreambleData *Preamble, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, @@ -232,8 +232,8 @@ CodeCompleteResult codeComplete(PathRef FileName, /// Get signature help at a specified \p Pos in \p FileName. SignatureHelp signatureHelp(PathRef FileName, const tooling::CompileCommand &Command, - PrecompiledPreamble const *Preamble, - StringRef Contents, Position Pos, + const PreambleData *Preamble, StringRef Contents, + Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, const SymbolIndex *Index); diff --git a/clangd/FS.cpp b/clangd/FS.cpp new file mode 100644 index 000000000..ce62a5927 --- /dev/null +++ b/clangd/FS.cpp @@ -0,0 +1,92 @@ +//===--- FS.cpp - File system related utils ----------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FS.h" +#include "clang/Basic/VirtualFileSystem.h" +#include "llvm/ADT/None.h" + +namespace clang { +namespace clangd { + +void PreambleFileStatusCache::update(const vfs::FileSystem &FS, vfs::Status S) { + SmallString<32> PathStore(S.getName()); + if (auto Err = FS.makeAbsolute(PathStore)) + return; + // Stores the latest status in cache as it can change in a preamble build. + StatCache.insert({PathStore, std::move(S)}); +} + +llvm::Optional +PreambleFileStatusCache::lookup(llvm::StringRef File) const { + auto I = StatCache.find(File); + if (I != StatCache.end()) + return I->getValue(); + return llvm::None; +} + +IntrusiveRefCntPtr PreambleFileStatusCache::getProducingFS( + IntrusiveRefCntPtr FS) { + // This invalidates old status in cache if files are re-`open()`ed or + // re-`stat()`ed in case file status has changed during preamble build. + class CollectFS : public vfs::ProxyFileSystem { + public: + CollectFS(IntrusiveRefCntPtr FS, + PreambleFileStatusCache &StatCache) + : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {} + + llvm::ErrorOr> + openFileForRead(const Twine &Path) override { + auto File = getUnderlyingFS().openFileForRead(Path); + if (!File || !*File) + return File; + // Eagerly stat opened file, as the followup `status` call on the file + // doesn't necessarily go through this FS. This puts some extra work on + // preamble build, but it should be worth it as preamble can be reused + // many times (e.g. code completion) and the repeated status call is + // likely to be cached in the underlying file system anyway. + if (auto S = File->get()->status()) + StatCache.update(getUnderlyingFS(), std::move(*S)); + return File; + } + + llvm::ErrorOr status(const Twine &Path) override { + auto S = getUnderlyingFS().status(Path); + if (S) + StatCache.update(getUnderlyingFS(), *S); + return S; + } + + private: + PreambleFileStatusCache &StatCache; + }; + return IntrusiveRefCntPtr(new CollectFS(std::move(FS), *this)); +} + +IntrusiveRefCntPtr PreambleFileStatusCache::getConsumingFS( + IntrusiveRefCntPtr FS) const { + class CacheVFS : public vfs::ProxyFileSystem { + public: + CacheVFS(IntrusiveRefCntPtr FS, + const PreambleFileStatusCache &StatCache) + : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {} + + llvm::ErrorOr status(const Twine &Path) override { + if (auto S = StatCache.lookup(Path.str())) + return *S; + return getUnderlyingFS().status(Path); + } + + private: + const PreambleFileStatusCache &StatCache; + }; + return IntrusiveRefCntPtr(new CacheVFS(std::move(FS), *this)); +} + +} // namespace clangd +} // namespace clang diff --git a/clangd/FS.h b/clangd/FS.h new file mode 100644 index 000000000..5f0fe62c6 --- /dev/null +++ b/clangd/FS.h @@ -0,0 +1,65 @@ +//===--- FS.h - File system related utils ------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FS_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FS_H + +#include "clang/Basic/VirtualFileSystem.h" +#include "llvm/ADT/Optional.h" + +namespace clang { +namespace clangd { + +/// Records status information for files open()ed or stat()ed during preamble +/// build, so we can avoid stat()s on the underlying FS when reusing the +/// preamble. For example, code completion can re-stat files when getting FileID +/// for source locations stored in preamble (e.g. checking whether a location is +/// in the main file). +/// +/// The cache is keyed by absolute path of file name in cached status, as this +/// is what preamble stores. +/// +/// The cache is not thread-safe when updates happen, so the use pattern should +/// be: +/// - One FS writes to the cache from one thread (or several but strictly +/// sequenced), e.g. when building preamble. +/// - Sequence point (no writes after this point, no reads before). +/// - Several FSs can read from the cache, e.g. code completions. +/// +/// Note that the cache is only valid when reusing preamble. +class PreambleFileStatusCache { +public: + void update(const vfs::FileSystem &FS, vfs::Status S); + /// \p Path is a path stored in preamble. + llvm::Optional lookup(llvm::StringRef Path) const; + + /// Returns a VFS that collects file status. + /// Only cache stats for files that exist because + /// 1) we only care about existing files when reusing preamble, unlike + /// building preamble. + /// 2) we use the file name in the Status as the cache key. + /// + /// Note that the returned VFS should not outlive the cache. + IntrusiveRefCntPtr + getProducingFS(IntrusiveRefCntPtr FS); + + /// Returns a VFS that uses the cache collected. + /// + /// Note that the returned VFS should not outlive the cache. + IntrusiveRefCntPtr + getConsumingFS(IntrusiveRefCntPtr FS) const; + +private: + llvm::StringMap StatCache; +}; + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_FS_H diff --git a/unittests/clangd/CMakeLists.txt b/unittests/clangd/CMakeLists.txt index a870c2990..1c98347be 100644 --- a/unittests/clangd/CMakeLists.txt +++ b/unittests/clangd/CMakeLists.txt @@ -21,6 +21,7 @@ add_extra_unittest(ClangdTests FileDistanceTests.cpp FileIndexTests.cpp FindSymbolsTests.cpp + FSTests.cpp FuzzyMatchTests.cpp GlobalCompilationDatabaseTests.cpp HeadersTests.cpp diff --git a/unittests/clangd/ClangdTests.cpp b/unittests/clangd/ClangdTests.cpp index ace2cef4b..21300e906 100644 --- a/unittests/clangd/ClangdTests.cpp +++ b/unittests/clangd/ClangdTests.cpp @@ -963,6 +963,71 @@ TEST_F(ClangdVFSTest, ChangedHeaderFromISystem) { Field(&CodeCompletion::Name, "baz"))); } +// Check that running code completion doesn't stat() a bunch of files from the +// preamble again. (They should be using the preamble's stat-cache) +TEST(ClangdTests, PreambleVFSStatCache) { + class ListenStatsFSProvider : public FileSystemProvider { + public: + ListenStatsFSProvider(llvm::StringMap &CountStats) + : CountStats(CountStats) {} + + IntrusiveRefCntPtr getFileSystem() override { + class ListenStatVFS : public vfs::ProxyFileSystem { + public: + ListenStatVFS(IntrusiveRefCntPtr FS, + llvm::StringMap &CountStats) + : ProxyFileSystem(std::move(FS)), CountStats(CountStats) {} + + llvm::ErrorOr> + openFileForRead(const Twine &Path) override { + ++CountStats[llvm::sys::path::filename(Path.str())]; + return ProxyFileSystem::openFileForRead(Path); + } + llvm::ErrorOr status(const Twine &Path) override { + ++CountStats[llvm::sys::path::filename(Path.str())]; + return ProxyFileSystem::status(Path); + } + + private: + llvm::StringMap &CountStats; + }; + + return IntrusiveRefCntPtr( + new ListenStatVFS(buildTestFS(Files), CountStats)); + } + + // If relative paths are used, they are resolved with testPath(). + llvm::StringMap Files; + llvm::StringMap &CountStats; + }; + + llvm::StringMap CountStats; + ListenStatsFSProvider FS(CountStats); + ErrorCheckingDiagConsumer DiagConsumer; + MockCompilationDatabase CDB; + ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + + auto SourcePath = testPath("foo.cpp"); + auto HeaderPath = testPath("foo.h"); + FS.Files[HeaderPath] = "struct TestSym {};"; + Annotations Code(R"cpp( + #include "foo.h" + + int main() { + TestSy^ + })cpp"); + + runAddDocument(Server, SourcePath, Code.code()); + + EXPECT_EQ(CountStats["foo.h"], 1u); + auto Completions = cantFail(runCodeComplete(Server, SourcePath, Code.point(), + clangd::CodeCompleteOptions())) + .Completions; + EXPECT_EQ(CountStats["foo.h"], 1u); + EXPECT_THAT(Completions, + ElementsAre(Field(&CodeCompletion::Name, "TestSym"))); +} + } // namespace } // namespace clangd } // namespace clang diff --git a/unittests/clangd/FSTests.cpp b/unittests/clangd/FSTests.cpp new file mode 100644 index 000000000..3b0284260 --- /dev/null +++ b/unittests/clangd/FSTests.cpp @@ -0,0 +1,46 @@ +//===-- FSTests.cpp - File system related tests -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FS.h" +#include "TestFS.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +TEST(FSTests, PreambleStatusCache) { + llvm::StringMap Files; + Files["x"] = ""; + Files["y"] = ""; + auto FS = buildTestFS(Files); + FS->setCurrentWorkingDirectory(testRoot()); + + PreambleFileStatusCache StatCache; + auto ProduceFS = StatCache.getProducingFS(FS); + EXPECT_TRUE(ProduceFS->openFileForRead("x")); + EXPECT_TRUE(ProduceFS->status("y")); + + EXPECT_TRUE(StatCache.lookup(testPath("x")).hasValue()); + EXPECT_TRUE(StatCache.lookup(testPath("y")).hasValue()); + + vfs::Status S("fake", llvm::sys::fs::UniqueID(0, 0), + std::chrono::system_clock::now(), 0, 0, 1024, + llvm::sys::fs::file_type::regular_file, llvm::sys::fs::all_all); + StatCache.update(*FS, S); + auto ConsumeFS = StatCache.getConsumingFS(FS); + auto Cached = ConsumeFS->status(testPath("fake")); + EXPECT_TRUE(Cached); + EXPECT_EQ(Cached->getName(), S.getName()); +} + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/unittests/clangd/TestFS.cpp b/unittests/clangd/TestFS.cpp index d3112b922..bc8c5479a 100644 --- a/unittests/clangd/TestFS.cpp +++ b/unittests/clangd/TestFS.cpp @@ -23,6 +23,7 @@ buildTestFS(llvm::StringMap const &Files, llvm::StringMap const &Timestamps) { IntrusiveRefCntPtr MemFS( new vfs::InMemoryFileSystem); + MemFS->setCurrentWorkingDirectory(testRoot()); for (auto &FileAndContents : Files) { StringRef File = FileAndContents.first(); MemFS->addFile( From 7162093338b05544f776dcccdb2c1b9eb3431842 Mon Sep 17 00:00:00 2001 From: Alexander Kornienko Date: Tue, 2 Oct 2018 11:38:41 +0000 Subject: [PATCH 281/686] [clang-tidy] Ignore singe bit bitfield -> bool conversion in readability-implicit-bool-conversion git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343578 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../ImplicitBoolConversionCheck.cpp | 1 + .../readability-implicit-bool-conversion.rst | 4 +++- .../readability-implicit-bool-conversion.cpp | 21 +++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/clang-tidy/readability/ImplicitBoolConversionCheck.cpp b/clang-tidy/readability/ImplicitBoolConversionCheck.cpp index e88c14360..1cf1d03e8 100644 --- a/clang-tidy/readability/ImplicitBoolConversionCheck.cpp +++ b/clang-tidy/readability/ImplicitBoolConversionCheck.cpp @@ -266,6 +266,7 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) { auto exceptionCases = expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())), + has(ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1)))))), hasParent(explicitCastExpr()))); auto implicitCastFromBool = implicitCastExpr( anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating), diff --git a/docs/clang-tidy/checks/readability-implicit-bool-conversion.rst b/docs/clang-tidy/checks/readability-implicit-bool-conversion.rst index 64f3f7367..a09368084 100644 --- a/docs/clang-tidy/checks/readability-implicit-bool-conversion.rst +++ b/docs/clang-tidy/checks/readability-implicit-bool-conversion.rst @@ -66,7 +66,9 @@ example: In general, the following conversion types are checked: -- integer expression/literal to boolean, +- integer expression/literal to boolean (conversion from a single bit bitfield + to boolean is explicitly allowed, since there's no ambiguity / information + loss in this case), - floating expression/literal to boolean, diff --git a/test/clang-tidy/readability-implicit-bool-conversion.cpp b/test/clang-tidy/readability-implicit-bool-conversion.cpp index 0bdd6703c..1cc0f4f51 100644 --- a/test/clang-tidy/readability-implicit-bool-conversion.cpp +++ b/test/clang-tidy/readability-implicit-bool-conversion.cpp @@ -437,3 +437,24 @@ void useOfUserConversion() { } } // namespace ignoreUserDefinedConversionOperator + +namespace ignore_1bit_bitfields { + +struct S { + int a; + int b : 1; + int c : 2; +}; + +bool f(const S& s) { + functionTaking(s.a); + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool + // CHECK-FIXES: functionTaking(s.a != 0); + functionTaking(s.b); + // CHECK-FIXES: functionTaking(s.b); + functionTaking(s.c); + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit conversion 'int' -> bool + // CHECK-FIXES: functionTaking(s.c != 0); +} + +} // namespace ignore_1bit_bitfields From d0e0604c0ba2f3db56153828a4fff365ae674376 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 2 Oct 2018 11:51:36 +0000 Subject: [PATCH 282/686] [clangd] Dex iterator printer shows query structure, not iterator state. Summary: This makes it suitable for logging (which immediately found a bug, to be fixed in the next patch...) Reviewers: ioeric Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52715 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343580 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Dex.cpp | 22 +++++++++++++--------- clangd/index/dex/Iterator.cpp | 9 +++------ clangd/index/dex/PostingList.cpp | 27 +++++++++++++++------------ clangd/index/dex/PostingList.h | 6 +++--- clangd/index/dex/Token.h | 14 ++++++++++++++ unittests/clangd/DexTests.cpp | 26 ++++++++++++++------------ 6 files changed, 62 insertions(+), 42 deletions(-) diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 6e3b14875..2c0c3d738 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -16,6 +16,7 @@ #include "index/Index.h" #include "index/dex/Iterator.h" #include "llvm/ADT/StringSet.h" +#include "llvm/Support/ScopedPrinter.h" #include #include @@ -85,13 +86,13 @@ std::vector> createFileProximityIterators( // ProximityPaths. Boosting factor should depend on the distance to the // Proximity Path: the closer processed path is, the higher boosting factor. for (const auto &ParentURI : ParentURIs.keys()) { - const auto It = - InvertedIndex.find(Token(Token::Kind::ProximityURI, ParentURI)); + Token Tok(Token::Kind::ProximityURI, ParentURI); + const auto It = InvertedIndex.find(Tok); if (It != InvertedIndex.end()) { // FIXME(kbobyrev): Append LIMIT on top of every BOOST iterator. PathProximitySignals.SymbolURI = ParentURI; - BoostingIterators.push_back( - createBoost(It->second.iterator(), PathProximitySignals.evaluate())); + BoostingIterators.push_back(createBoost(It->second.iterator(&It->first), + PathProximitySignals.evaluate())); } } return BoostingIterators; @@ -142,7 +143,6 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, llvm::function_ref Callback) const { assert(!StringRef(Req.Query).contains("::") && "There must be no :: in query."); - // FIXME: attach the query tree to the trace span. trace::Span Tracer("Dex fuzzyFind"); FuzzyMatcher Filter(Req.Query); bool More = false; @@ -156,7 +156,7 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, for (const auto &Trigram : TrigramTokens) { const auto It = InvertedIndex.find(Trigram); if (It != InvertedIndex.end()) - TrigramIterators.push_back(It->second.iterator()); + TrigramIterators.push_back(It->second.iterator(&It->first)); } if (!TrigramIterators.empty()) TopLevelChildren.push_back(createAnd(move(TrigramIterators))); @@ -164,9 +164,10 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, // Generate scope tokens for search query. std::vector> ScopeIterators; for (const auto &Scope : Req.Scopes) { - const auto It = InvertedIndex.find(Token(Token::Kind::Scope, Scope)); + Token Tok(Token::Kind::Scope, Scope); + const auto It = InvertedIndex.find(Tok); if (It != InvertedIndex.end()) - ScopeIterators.push_back(It->second.iterator()); + ScopeIterators.push_back(It->second.iterator(&It->first)); } if (Req.AnyScope) ScopeIterators.push_back(createBoost(createTrue(Symbols.size()), @@ -189,7 +190,8 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, if (Req.RestrictForCodeCompletion) TopLevelChildren.push_back( - InvertedIndex.find(RestrictedForCodeCompletion)->second.iterator()); + InvertedIndex.find(RestrictedForCodeCompletion) + ->second.iterator(&RestrictedForCodeCompletion)); // Use TRUE iterator if both trigrams and scopes from the query are not // present in the symbol index. @@ -203,6 +205,8 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, // when the requested number of items is small. auto Root = Req.Limit ? createLimit(move(QueryIterator), *Req.Limit * 100) : move(QueryIterator); + SPAN_ATTACH(Tracer, "query", llvm::to_string(*Root)); + vlog("Dex query tree: {0}", *Root); using IDAndScore = std::pair; std::vector IDAndScores = consume(*Root); diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index 7009d9b20..de6232974 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -243,8 +243,7 @@ class TrueIterator : public Iterator { private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { - OS << "(TRUE {" << Index << "} out of " << Size << ")"; - return OS; + return OS << "true"; } DocID Index = 0; @@ -273,8 +272,7 @@ class BoostIterator : public Iterator { private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { - OS << "(BOOST " << Factor << ' ' << *Child << ')'; - return OS; + return OS << "(* " << Factor << ' ' << *Child << ')'; } std::unique_ptr Child; @@ -314,8 +312,7 @@ class LimitIterator : public Iterator { private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { - OS << "(LIMIT " << Limit << '(' << ItemsLeft << ") " << *Child << ')'; - return OS; + return OS << "(LIMIT " << Limit << " " << *Child << ')'; } std::unique_ptr Child; diff --git a/clangd/index/dex/PostingList.cpp b/clangd/index/dex/PostingList.cpp index e41a53d6a..cab869f6f 100644 --- a/clangd/index/dex/PostingList.cpp +++ b/clangd/index/dex/PostingList.cpp @@ -9,6 +9,7 @@ #include "PostingList.h" #include "Iterator.h" +#include "Token.h" #include "llvm/Support/Error.h" #include "llvm/Support/MathExtras.h" @@ -23,8 +24,8 @@ namespace { /// them on-the-fly when the contents of chunk are to be seen. class ChunkIterator : public Iterator { public: - explicit ChunkIterator(llvm::ArrayRef Chunks) - : Chunks(Chunks), CurrentChunk(Chunks.begin()) { + explicit ChunkIterator(const Token *Tok, llvm::ArrayRef Chunks) + : Tok(Tok), Chunks(Chunks), CurrentChunk(Chunks.begin()) { if (!Chunks.empty()) { DecompressedChunk = CurrentChunk->decompress(); CurrentID = DecompressedChunk.begin(); @@ -71,15 +72,16 @@ class ChunkIterator : public Iterator { private: llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { + if (Tok != nullptr) + return OS << *Tok; OS << '['; - if (CurrentChunk != Chunks.begin() || - (CurrentID != DecompressedChunk.begin() && !DecompressedChunk.empty())) - OS << "... "; - OS << (reachedEnd() ? "END" : std::to_string(*CurrentID)); - if (!reachedEnd() && CurrentID < DecompressedChunk.end() - 1) - OS << " ..."; - OS << ']'; - return OS; + const char *Sep = ""; + for (const Chunk &C : Chunks) + for (const DocID Doc : C.decompress()) { + OS << Sep << Doc; + Sep = " "; + } + return OS << ']'; } /// If the cursor is at the end of a chunk, place it at the start of the next @@ -110,6 +112,7 @@ class ChunkIterator : public Iterator { } } + const Token *Tok; llvm::ArrayRef Chunks; /// Iterator over chunks. /// If CurrentChunk is valid, then DecompressedChunk is @@ -217,8 +220,8 @@ llvm::SmallVector Chunk::decompress() const { PostingList::PostingList(llvm::ArrayRef Documents) : Chunks(encodeStream(Documents)) {} -std::unique_ptr PostingList::iterator() const { - return llvm::make_unique(Chunks); +std::unique_ptr PostingList::iterator(const Token *Tok) const { + return llvm::make_unique(Tok, Chunks); } } // namespace dex diff --git a/clangd/index/dex/PostingList.h b/clangd/index/dex/PostingList.h index 5b3c9b79f..81ba64c19 100644 --- a/clangd/index/dex/PostingList.h +++ b/clangd/index/dex/PostingList.h @@ -33,8 +33,7 @@ namespace clang { namespace clangd { namespace dex { - -class Iterator; +struct Token; /// NOTE: This is an implementation detail. /// @@ -64,7 +63,8 @@ class PostingList { /// Constructs DocumentIterator over given posting list. DocumentIterator will /// go through the chunks and decompress them on-the-fly when necessary. - std::unique_ptr iterator() const; + /// If given, Tok is only used for the string representation. + std::unique_ptr iterator(const Token *Tok = nullptr) const; /// Returns in-memory size of external storage. size_t bytes() const { return Chunks.capacity() * sizeof(Chunk); } diff --git a/clangd/index/dex/Token.h b/clangd/index/dex/Token.h index bae27130a..f80d92549 100644 --- a/clangd/index/dex/Token.h +++ b/clangd/index/dex/Token.h @@ -82,6 +82,20 @@ struct Token { Kind TokenKind; friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Token &T) { + switch (T.TokenKind) { + case Kind::Trigram: + OS << "T="; + break; + case Kind::Scope: + OS << "S="; + break; + case Kind::ProximityURI: + OS << "U="; + break; + case Kind::Sentinel: + OS << "?="; + break; + } return OS << T.Data; } diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 231343cd7..6d761f513 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -226,18 +226,20 @@ TEST(DexIterators, QueryTree) { } TEST(DexIterators, StringRepresentation) { - const PostingList L0({4, 7, 8, 20, 42, 100}); - const PostingList L1({1, 3, 5, 8, 9}); - const PostingList L2({1, 5, 7, 9}); - const PostingList L3({0, 5}); - const PostingList L4({0, 1, 5}); - - EXPECT_EQ(llvm::to_string(*(L0.iterator())), "[4 ...]"); - auto It = L0.iterator(); - It->advanceTo(19); - EXPECT_EQ(llvm::to_string(*It), "[... 20 ...]"); - It->advanceTo(9000); - EXPECT_EQ(llvm::to_string(*It), "[... END]"); + const PostingList L1({1, 3, 5}); + const PostingList L2({1, 7, 9}); + + // No token given, prints full posting list. + auto I1 = L1.iterator(); + EXPECT_EQ(llvm::to_string(*I1), "[1 3 5]"); + + // Token given, uses token's string representation. + Token Tok(Token::Kind::Trigram, "L2"); + auto I2 = L1.iterator(&Tok); + EXPECT_EQ(llvm::to_string(*I2), "T=L2"); + + auto Tree = createLimit(createAnd(move(I1), move(I2)), 10); + EXPECT_EQ(llvm::to_string(*Tree), "(LIMIT 10 (& [1 3 5] T=L2))"); } TEST(DexIterators, Limit) { From b7fa5dcd0731ba23b4e9e40ba0f4e8581e818483 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 2 Oct 2018 13:44:26 +0000 Subject: [PATCH 283/686] [clangd] Dex: add Corpus factory for iterators, rename, fold constant. NFC Summary: - Corpus avoids having to pass size to the true iterator, and (soon) any iterator that might optimize down to true. - Shorten names of factory functions now they're scoped to the Corpus. intersect() and unionOf() rather than createAnd() or createOr() as this seems to read better to me, and fits with other short names. Opinion wanted! - DEFAULT_BOOST_SCORE --> 1. This is a multiplier, don't obfuscate identity. - Simplify variadic templates in Iterator.h Reviewers: ioeric Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52711 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343589 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Dex.cpp | 28 +++---- clangd/index/dex/Dex.h | 3 +- clangd/index/dex/Iterator.cpp | 24 +++--- clangd/index/dex/Iterator.h | 125 ++++++++++++++++--------------- clangd/index/dex/PostingList.cpp | 2 +- unittests/clangd/DexTests.cpp | 56 ++++++++------ 6 files changed, 127 insertions(+), 111 deletions(-) diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 2c0c3d738..3b236f891 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -56,7 +56,8 @@ std::vector generateSearchTokens(const Symbol &Sym) { std::vector> createFileProximityIterators( llvm::ArrayRef ProximityPaths, llvm::ArrayRef URISchemes, - const llvm::DenseMap &InvertedIndex) { + const llvm::DenseMap &InvertedIndex, + const Corpus &Corpus) { std::vector> BoostingIterators; // Deduplicate parent URIs extracted from the ProximityPaths. llvm::StringSet<> ParentURIs; @@ -91,8 +92,8 @@ std::vector> createFileProximityIterators( if (It != InvertedIndex.end()) { // FIXME(kbobyrev): Append LIMIT on top of every BOOST iterator. PathProximitySignals.SymbolURI = ParentURI; - BoostingIterators.push_back(createBoost(It->second.iterator(&It->first), - PathProximitySignals.evaluate())); + BoostingIterators.push_back(Corpus.boost( + It->second.iterator(&It->first), PathProximitySignals.evaluate())); } } return BoostingIterators; @@ -101,6 +102,7 @@ std::vector> createFileProximityIterators( } // namespace void Dex::buildIndex() { + this->Corpus = dex::Corpus(Symbols.size()); std::vector> ScoredSymbols(Symbols.size()); for (size_t I = 0; I < Symbols.size(); ++I) { @@ -159,7 +161,7 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, TrigramIterators.push_back(It->second.iterator(&It->first)); } if (!TrigramIterators.empty()) - TopLevelChildren.push_back(createAnd(move(TrigramIterators))); + TopLevelChildren.push_back(Corpus.intersect(move(TrigramIterators))); // Generate scope tokens for search query. std::vector> ScopeIterators; @@ -170,22 +172,22 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, ScopeIterators.push_back(It->second.iterator(&It->first)); } if (Req.AnyScope) - ScopeIterators.push_back(createBoost(createTrue(Symbols.size()), - ScopeIterators.empty() ? 1.0 : 0.2)); + ScopeIterators.push_back( + Corpus.boost(Corpus.all(), ScopeIterators.empty() ? 1.0 : 0.2)); // Add OR iterator for scopes if there are any Scope Iterators. if (!ScopeIterators.empty()) - TopLevelChildren.push_back(createOr(move(ScopeIterators))); + TopLevelChildren.push_back(Corpus.unionOf(move(ScopeIterators))); // Add proximity paths boosting. auto BoostingIterators = createFileProximityIterators( - Req.ProximityPaths, URISchemes, InvertedIndex); + Req.ProximityPaths, URISchemes, InvertedIndex, Corpus); // Boosting iterators do not actually filter symbols. In order to preserve // the validity of resulting query, TRUE iterator should be added along // BOOSTs. if (!BoostingIterators.empty()) { - BoostingIterators.push_back(createTrue(Symbols.size())); - TopLevelChildren.push_back(createOr(move(BoostingIterators))); + BoostingIterators.push_back(Corpus.all()); + TopLevelChildren.push_back(Corpus.unionOf(move(BoostingIterators))); } if (Req.RestrictForCodeCompletion) @@ -196,14 +198,14 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, // Use TRUE iterator if both trigrams and scopes from the query are not // present in the symbol index. auto QueryIterator = TopLevelChildren.empty() - ? createTrue(Symbols.size()) - : createAnd(move(TopLevelChildren)); + ? Corpus.all() + : Corpus.intersect(move(TopLevelChildren)); // Retrieve more items than it was requested: some of the items with high // final score might not be retrieved otherwise. // FIXME(kbobyrev): Pre-scoring retrieval threshold should be adjusted as // using 100x of the requested number might not be good in practice, e.g. // when the requested number of items is small. - auto Root = Req.Limit ? createLimit(move(QueryIterator), *Req.Limit * 100) + auto Root = Req.Limit ? Corpus.limit(move(QueryIterator), *Req.Limit * 100) : move(QueryIterator); SPAN_ATTACH(Tracer, "query", llvm::to_string(*Root)); vlog("Dex query tree: {0}", *Root); diff --git a/clangd/index/dex/Dex.h b/clangd/index/dex/Dex.h index 4b2d85658..d8360ba3f 100644 --- a/clangd/index/dex/Dex.h +++ b/clangd/index/dex/Dex.h @@ -44,7 +44,7 @@ class Dex : public SymbolIndex { // All symbols must outlive this index. template Dex(Range &&Symbols, llvm::ArrayRef Schemes) - : URISchemes(Schemes) { + : Corpus(0), URISchemes(Schemes) { // If Schemes don't contain any items, fall back to SymbolCollector's // default URI schemes. if (URISchemes.empty()) { @@ -101,6 +101,7 @@ class Dex : public SymbolIndex { /// std. Inverted index is used to retrieve posting lists which are processed /// during the fuzzyFind process. llvm::DenseMap InvertedIndex; + Corpus Corpus; std::shared_ptr KeepAlive; // poor man's move-only std::any // Size of memory retained by KeepAlive. size_t BackingDataSize = 0; diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index de6232974..e7794d4aa 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -64,7 +64,7 @@ class AndIterator : public Iterator { float consume() override { assert(!reachedEnd() && "AND iterator can't consume() at the end."); - float Boost = DEFAULT_BOOST_SCORE; + float Boost = 1; for (const auto &Child : Children) Boost *= Child->consume(); return Boost; @@ -175,12 +175,12 @@ class OrIterator : public Iterator { return Result; } - // Returns the maximum boosting score among all Children when iterator is not - // exhausted and points to the given ID, DEFAULT_BOOST_SCORE otherwise. + // Returns the maximum boosting score among all Children when iterator + // points to the current ID. float consume() override { assert(!reachedEnd() && "OR iterator can't consume() at the end."); const DocID ID = peek(); - float Boost = DEFAULT_BOOST_SCORE; + float Boost = 1; for (const auto &Child : Children) if (!Child->reachedEnd() && Child->peek() == ID) Boost = std::max(Boost, Child->consume()); @@ -236,7 +236,7 @@ class TrueIterator : public Iterator { float consume() override { assert(!reachedEnd() && "TRUE iterator can't consume() at the end."); - return DEFAULT_BOOST_SCORE; + return 1; } size_t estimateSize() const override { return Size; } @@ -330,30 +330,30 @@ std::vector> consume(Iterator &It) { } std::unique_ptr -createAnd(std::vector> Children) { +Corpus::intersect(std::vector> Children) const { // If there is exactly one child, pull it one level up: AND(Child) -> Child. return Children.size() == 1 ? std::move(Children.front()) : llvm::make_unique(move(Children)); } std::unique_ptr -createOr(std::vector> Children) { +Corpus::unionOf(std::vector> Children) const { // If there is exactly one child, pull it one level up: OR(Child) -> Child. return Children.size() == 1 ? std::move(Children.front()) : llvm::make_unique(move(Children)); } -std::unique_ptr createTrue(DocID Size) { +std::unique_ptr Corpus::all() const { return llvm::make_unique(Size); } -std::unique_ptr createBoost(std::unique_ptr Child, - float Factor) { +std::unique_ptr Corpus::boost(std::unique_ptr Child, + float Factor) const { return llvm::make_unique(move(Child), Factor); } -std::unique_ptr createLimit(std::unique_ptr Child, - size_t Limit) { +std::unique_ptr Corpus::limit(std::unique_ptr Child, + size_t Limit) const { return llvm::make_unique(move(Child), Limit); } diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 3066a8c78..ba1efbf81 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -101,8 +101,6 @@ class Iterator { return Iterator.dump(OS); } - constexpr static float DEFAULT_BOOST_SCORE = 1; - private: virtual llvm::raw_ostream &dump(llvm::raw_ostream &OS) const = 0; }; @@ -117,69 +115,74 @@ class Iterator { /// to acquire preliminary scores of requested items. std::vector> consume(Iterator &It); -/// Returns AND Iterator which performs the intersection of the PostingLists of -/// its children. -/// -/// consume(): AND Iterator returns the product of Childrens' boosting scores -/// when not exhausted and DEFAULT_BOOST_SCORE otherwise. -std::unique_ptr -createAnd(std::vector> Children); - -/// Returns OR Iterator which performs the union of the PostingLists of its -/// children. -/// -/// consume(): OR Iterator returns the highest boost value among children -/// pointing to requested item when not exhausted and DEFAULT_BOOST_SCORE -/// otherwise. -std::unique_ptr -createOr(std::vector> Children); - -/// Returns TRUE Iterator which iterates over "virtual" PostingList containing -/// all items in range [0, Size) in an efficient manner. -/// -/// TRUE returns DEFAULT_BOOST_SCORE for each processed item. -std::unique_ptr createTrue(DocID Size); - -/// Returns BOOST iterator which multiplies the score of each item by given -/// factor. Boosting can be used as a computationally inexpensive filtering. -/// Users can return significantly more items using consumeAndBoost() and then -/// trim Top K using retrieval score. -std::unique_ptr createBoost(std::unique_ptr Child, - float Factor); - -/// Returns LIMIT iterator, which yields up to N elements of its child iterator. -/// Elements only count towards the limit if they are part of the final result -/// set. Therefore the following iterator (AND (2) (LIMIT (1 2) 1)) yields (2), -/// not (). -std::unique_ptr createLimit(std::unique_ptr Child, - size_t Limit); - -/// This allows createAnd(create(...), create(...)) syntax. -template std::unique_ptr createAnd(Args... args) { - std::vector> Children; - populateChildren(Children, args...); - return createAnd(move(Children)); -} - -/// This allows createOr(create(...), create(...)) syntax. -template std::unique_ptr createOr(Args... args) { - std::vector> Children; - populateChildren(Children, args...); - return createOr(move(Children)); -} - -template +namespace detail { +// Variadic template machinery. +inline void populateChildren(std::vector> &) {} +template void populateChildren(std::vector> &Children, - HeadT &Head, TailT &... Tail) { + std::unique_ptr Head, TailT... Tail) { Children.push_back(move(Head)); - populateChildren(Children, Tail...); + populateChildren(Children, move(Tail)...); } +} // namespace detail -template -void populateChildren(std::vector> &Children, - HeadT &Head) { - Children.push_back(move(Head)); -} +// A corpus is a set of documents, and a factory for iterators over them. +class Corpus { + DocID Size; + +public: + explicit Corpus(DocID Size) : Size(Size) {} + + /// Returns AND Iterator which performs the intersection of the PostingLists + /// of its children. + /// + /// consume(): AND Iterator returns the product of Childrens' boosting + /// scores. + std::unique_ptr + intersect(std::vector> Children) const; + + /// Returns OR Iterator which performs the union of the PostingLists of its + /// children. + /// + /// consume(): OR Iterator returns the highest boost value among children + /// containing the requested item. + std::unique_ptr + unionOf(std::vector> Children) const; + + /// Returns TRUE Iterator which iterates over "virtual" PostingList + /// containing all items in range [0, Size) in an efficient manner. + std::unique_ptr all() const; + + /// Returns BOOST iterator which multiplies the score of each item by given + /// factor. Boosting can be used as a computationally inexpensive filtering. + /// Users can return significantly more items using consumeAndBoost() and + /// then trim Top K using retrieval score. + std::unique_ptr boost(std::unique_ptr Child, + float Factor) const; + + /// Returns LIMIT iterator, which yields up to N elements of its child + /// iterator. Elements only count towards the limit if they are part of the + /// final result set. Therefore the following iterator (AND (2) (LIMIT (1 2) + /// 1)) yields (2), not (). + std::unique_ptr limit(std::unique_ptr Child, + size_t Limit) const; + + /// This allows intersect(create(...), create(...)) syntax. + template + std::unique_ptr intersect(Args... args) const { + std::vector> Children; + detail::populateChildren(Children, std::forward(args)...); + return intersect(move(Children)); + } + + /// This allows unionOf(create(...), create(...)) syntax. + template + std::unique_ptr unionOf(Args... args) const { + std::vector> Children; + detail::populateChildren(Children, std::forward(args)...); + return unionOf(move(Children)); + } +}; } // namespace dex } // namespace clangd diff --git a/clangd/index/dex/PostingList.cpp b/clangd/index/dex/PostingList.cpp index cab869f6f..4cb6395aa 100644 --- a/clangd/index/dex/PostingList.cpp +++ b/clangd/index/dex/PostingList.cpp @@ -63,7 +63,7 @@ class ChunkIterator : public Iterator { float consume() override { assert(!reachedEnd() && "Posting List iterator can't consume() at the end."); - return DEFAULT_BOOST_SCORE; + return 1; } size_t estimateSize() const override { diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 6d761f513..28ec9c5c8 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -70,15 +70,16 @@ TEST(DexIterators, DocumentIterator) { } TEST(DexIterators, AndTwoLists) { + Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); - auto And = createAnd(L1.iterator(), L0.iterator()); + auto And = C.intersect(L1.iterator(), L0.iterator()); EXPECT_FALSE(And->reachedEnd()); EXPECT_THAT(consumeIDs(*And), ElementsAre(0U, 7U, 10U, 320U, 9000U)); - And = createAnd(L0.iterator(), L1.iterator()); + And = C.intersect(L0.iterator(), L1.iterator()); And->advanceTo(0); EXPECT_EQ(And->peek(), 0U); @@ -94,11 +95,12 @@ TEST(DexIterators, AndTwoLists) { } TEST(DexIterators, AndThreeLists) { + Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); const PostingList L2({1, 4, 7, 11, 30, 60, 320, 9000}); - auto And = createAnd(L0.iterator(), L1.iterator(), L2.iterator()); + auto And = C.intersect(L0.iterator(), L1.iterator(), L2.iterator()); EXPECT_EQ(And->peek(), 7U); And->advanceTo(300); EXPECT_EQ(And->peek(), 320U); @@ -108,10 +110,11 @@ TEST(DexIterators, AndThreeLists) { } TEST(DexIterators, OrTwoLists) { + Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); - auto Or = createOr(L0.iterator(), L1.iterator()); + auto Or = C.unionOf(L0.iterator(), L1.iterator()); EXPECT_FALSE(Or->reachedEnd()); EXPECT_EQ(Or->peek(), 0U); @@ -134,18 +137,19 @@ TEST(DexIterators, OrTwoLists) { Or->advanceTo(9001); EXPECT_TRUE(Or->reachedEnd()); - Or = createOr(L0.iterator(), L1.iterator()); + Or = C.unionOf(L0.iterator(), L1.iterator()); EXPECT_THAT(consumeIDs(*Or), ElementsAre(0U, 4U, 5U, 7U, 10U, 30U, 42U, 60U, 320U, 9000U)); } TEST(DexIterators, OrThreeLists) { + Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); const PostingList L2({1, 4, 7, 11, 30, 60, 320, 9000}); - auto Or = createOr(L0.iterator(), L1.iterator(), L2.iterator()); + auto Or = C.unionOf(L0.iterator(), L1.iterator(), L2.iterator()); EXPECT_FALSE(Or->reachedEnd()); EXPECT_EQ(Or->peek(), 0U); @@ -194,17 +198,18 @@ TEST(DexIterators, QueryTree) { // |1, 5, 7, 9| |1, 5| |0, 3, 5| // +----------+ +----+ +-------+ // + Corpus C{10}; const PostingList L0({1, 3, 5, 8, 9}); const PostingList L1({1, 5, 7, 9}); const PostingList L2({1, 5}); const PostingList L3({0, 3, 5}); // Root of the query tree: [1, 5] - auto Root = createAnd( + auto Root = C.intersect( // Lower And Iterator: [1, 5, 9] - createAnd(L0.iterator(), createBoost(L1.iterator(), 2U)), + C.intersect(L0.iterator(), C.boost(L1.iterator(), 2U)), // Lower Or Iterator: [0, 1, 5] - createOr(createBoost(L2.iterator(), 3U), createBoost(L3.iterator(), 4U))); + C.unionOf(C.boost(L2.iterator(), 3U), C.boost(L3.iterator(), 4U))); EXPECT_FALSE(Root->reachedEnd()); EXPECT_EQ(Root->peek(), 1U); @@ -226,6 +231,7 @@ TEST(DexIterators, QueryTree) { } TEST(DexIterators, StringRepresentation) { + Corpus C{10}; const PostingList L1({1, 3, 5}); const PostingList L2({1, 7, 9}); @@ -238,56 +244,60 @@ TEST(DexIterators, StringRepresentation) { auto I2 = L1.iterator(&Tok); EXPECT_EQ(llvm::to_string(*I2), "T=L2"); - auto Tree = createLimit(createAnd(move(I1), move(I2)), 10); + auto Tree = C.limit(C.intersect(move(I1), move(I2)), 10); EXPECT_EQ(llvm::to_string(*Tree), "(LIMIT 10 (& [1 3 5] T=L2))"); } TEST(DexIterators, Limit) { + Corpus C{10000}; const PostingList L0({3, 6, 7, 20, 42, 100}); const PostingList L1({1, 3, 5, 6, 7, 30, 100}); const PostingList L2({0, 3, 5, 7, 8, 100}); - auto DocIterator = createLimit(L0.iterator(), 42); + auto DocIterator = C.limit(L0.iterator(), 42); EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(3, 6, 7, 20, 42, 100)); - DocIterator = createLimit(L0.iterator(), 3); + DocIterator = C.limit(L0.iterator(), 3); EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(3, 6, 7)); - DocIterator = createLimit(L0.iterator(), 0); + DocIterator = C.limit(L0.iterator(), 0); EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre()); - auto AndIterator = createAnd( - createLimit(createTrue(9000), 343), createLimit(L0.iterator(), 2), - createLimit(L1.iterator(), 3), createLimit(L2.iterator(), 42)); + auto AndIterator = + C.intersect(C.limit(C.all(), 343), C.limit(L0.iterator(), 2), + C.limit(L1.iterator(), 3), C.limit(L2.iterator(), 42)); EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(3, 7)); } TEST(DexIterators, True) { - auto TrueIterator = createTrue(0U); + Corpus C{0}; + auto TrueIterator = C.all(); EXPECT_TRUE(TrueIterator->reachedEnd()); EXPECT_THAT(consumeIDs(*TrueIterator), ElementsAre()); + C = Corpus{7}; const PostingList L0({1, 2, 5, 7}); - TrueIterator = createTrue(7U); + TrueIterator = C.all(); EXPECT_THAT(TrueIterator->peek(), 0); - auto AndIterator = createAnd(L0.iterator(), move(TrueIterator)); + auto AndIterator = C.intersect(L0.iterator(), move(TrueIterator)); EXPECT_FALSE(AndIterator->reachedEnd()); EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(1, 2, 5)); } TEST(DexIterators, Boost) { - auto BoostIterator = createBoost(createTrue(5U), 42U); + Corpus C{5}; + auto BoostIterator = C.boost(C.all(), 42U); EXPECT_FALSE(BoostIterator->reachedEnd()); auto ElementBoost = BoostIterator->consume(); EXPECT_THAT(ElementBoost, 42U); const PostingList L0({2, 4}); const PostingList L1({1, 4}); - auto Root = createOr(createTrue(5U), createBoost(L0.iterator(), 2U), - createBoost(L1.iterator(), 3U)); + auto Root = C.unionOf(C.all(), C.boost(L0.iterator(), 2U), + C.boost(L1.iterator(), 3U)); ElementBoost = Root->consume(); - EXPECT_THAT(ElementBoost, Iterator::DEFAULT_BOOST_SCORE); + EXPECT_THAT(ElementBoost, 1); Root->advance(); EXPECT_THAT(Root->peek(), 1U); ElementBoost = Root->consume(); From 02a35f7eef04b6cfd559bd4eb7fd101651032f94 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 2 Oct 2018 13:51:43 +0000 Subject: [PATCH 284/686] [clangd] Zap TODONEs git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343590 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index ba1efbf81..2b3bd4481 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -53,9 +53,6 @@ using DocID = uint32_t; /// (their children) to form a multi-level Query Tree. The interface is designed /// to be extensible in order to support multiple types of iterators. class Iterator { - // FIXME(kbobyrev): Implement iterator cost, an estimate of advance() calls - // before iterator exhaustion. - // FIXME(kbobyrev): Implement Limit iterator. public: /// Returns true if all valid DocIDs were processed and hence the iterator is /// exhausted. From 34bd48566a5d3eb17ea5b58ddd4412c57a51e176 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Tue, 2 Oct 2018 14:46:08 +0000 Subject: [PATCH 285/686] [clangd] Add a #include completion test that triggers an assertion. Summary: Test for https://reviews.llvm.org/D52774. Reviewers: sammccall Reviewed By: sammccall Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52775 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343593 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/CodeCompleteTests.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp index 68b13da17..e056699ab 100644 --- a/unittests/clangd/CodeCompleteTests.cpp +++ b/unittests/clangd/CodeCompleteTests.cpp @@ -2093,6 +2093,15 @@ TEST(CompletionTest, IncludedCompletionKinds) { Has("bar.h\"", CompletionItemKind::File))); } +TEST(CompletionTest, NoCrashAtNonAlphaIncludeHeader) { + auto Results = completions( + R"cpp( + #include "./^" + )cpp" + ); + EXPECT_TRUE(Results.Completions.empty()); +} + TEST(CompletionTest, NoAllScopesCompletionWhenQualified) { clangd::CodeCompleteOptions Opts = {}; Opts.AllScopes = true; From 318537b1621a386574211082a152c63a33e176c8 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Tue, 2 Oct 2018 17:22:11 +0000 Subject: [PATCH 286/686] [clangd] Don't make check-clangd as a dependency in check-clang-tools Summary: check-clang-tools will run check-clangd first, and then run the rest tests. If clangd tests fails, check-clang-tools would be stopped. This would block other clang-tools developers if clangd is broken. Reviewers: sammccall Subscribers: mgorny, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52781 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343608 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/CMakeLists.txt | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e2d7e12ef..af11b26c1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -52,8 +52,6 @@ set(CLANG_TOOLS_TEST_DEPS modularize pp-trace - check-clangd - # Unit tests ExtraToolsUnitTests @@ -65,6 +63,19 @@ set(CLANG_TOOLS_TEST_DEPS clang-tidy ) +set(CLANGD_TEST_DEPS + clangd + ClangdTests + # clangd-related tools which don't have tests, add them to the test to make + # sure we don't introduce new changes that break their compilations. + clangd-indexer + dexp + ) +foreach(clangd_dep ${CLANGD_TEST_DEPS}) + list(APPEND CLANG_TOOLS_TEST_DEPS + ${clangd_dep}) +endforeach() + add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression tests" ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${CLANG_TOOLS_TEST_DEPS} @@ -74,15 +85,9 @@ add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression t set_target_properties(check-clang-tools PROPERTIES FOLDER "Clang extra tools' tests") # Setup an individual test for building and testing clangd-only stuff. -set(CLANGD_TEST_DEPS - clangd - ClangdTests - # clangd-related tools which don't have tests, add them to the test to make - # sure we don't introduce new changes that break their compilations. - clangd-indexer - dexp -) -# Exclude check-clangd from check-all, as check-clangd will launch via check-clang-tools. +# Note: all clangd tests have been covered in check-clang-tools, this is a +# convenient target for clangd developers. +# Exclude check-clangd from check-all. set(EXCLUDE_FROM_ALL ON) add_lit_testsuite(check-clangd "Running the Clangd regression tests" ${CMAKE_CURRENT_BINARY_DIR}/Unit/clangd;${CMAKE_CURRENT_BINARY_DIR}/clangd From e5f28c5c70009e7156612faddca26ba99453346b Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Tue, 2 Oct 2018 17:31:43 +0000 Subject: [PATCH 287/686] Revert r343589 "[clangd] Dex: add Corpus factory for iterators, rename, fold constant. NFC" Declaring a field with the same name as a type causes GCC to error out: Dex.h:104:10: error: declaration of 'clang::clangd::dex::Corpus clang::clangd::dex::Dex::Corpus' [-fpermissive] Corpus Corpus; ^ Iterator.h:127:7: error: changes meaning of 'Corpus' from 'class clang::clangd::dex::Corpus' [-fpermissive] class Corpus { git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343610 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Dex.cpp | 28 ++++--- clangd/index/dex/Dex.h | 3 +- clangd/index/dex/Iterator.cpp | 24 +++--- clangd/index/dex/Iterator.h | 125 +++++++++++++++---------------- clangd/index/dex/PostingList.cpp | 2 +- unittests/clangd/DexTests.cpp | 56 ++++++-------- 6 files changed, 111 insertions(+), 127 deletions(-) diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 3b236f891..2c0c3d738 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -56,8 +56,7 @@ std::vector generateSearchTokens(const Symbol &Sym) { std::vector> createFileProximityIterators( llvm::ArrayRef ProximityPaths, llvm::ArrayRef URISchemes, - const llvm::DenseMap &InvertedIndex, - const Corpus &Corpus) { + const llvm::DenseMap &InvertedIndex) { std::vector> BoostingIterators; // Deduplicate parent URIs extracted from the ProximityPaths. llvm::StringSet<> ParentURIs; @@ -92,8 +91,8 @@ std::vector> createFileProximityIterators( if (It != InvertedIndex.end()) { // FIXME(kbobyrev): Append LIMIT on top of every BOOST iterator. PathProximitySignals.SymbolURI = ParentURI; - BoostingIterators.push_back(Corpus.boost( - It->second.iterator(&It->first), PathProximitySignals.evaluate())); + BoostingIterators.push_back(createBoost(It->second.iterator(&It->first), + PathProximitySignals.evaluate())); } } return BoostingIterators; @@ -102,7 +101,6 @@ std::vector> createFileProximityIterators( } // namespace void Dex::buildIndex() { - this->Corpus = dex::Corpus(Symbols.size()); std::vector> ScoredSymbols(Symbols.size()); for (size_t I = 0; I < Symbols.size(); ++I) { @@ -161,7 +159,7 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, TrigramIterators.push_back(It->second.iterator(&It->first)); } if (!TrigramIterators.empty()) - TopLevelChildren.push_back(Corpus.intersect(move(TrigramIterators))); + TopLevelChildren.push_back(createAnd(move(TrigramIterators))); // Generate scope tokens for search query. std::vector> ScopeIterators; @@ -172,22 +170,22 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, ScopeIterators.push_back(It->second.iterator(&It->first)); } if (Req.AnyScope) - ScopeIterators.push_back( - Corpus.boost(Corpus.all(), ScopeIterators.empty() ? 1.0 : 0.2)); + ScopeIterators.push_back(createBoost(createTrue(Symbols.size()), + ScopeIterators.empty() ? 1.0 : 0.2)); // Add OR iterator for scopes if there are any Scope Iterators. if (!ScopeIterators.empty()) - TopLevelChildren.push_back(Corpus.unionOf(move(ScopeIterators))); + TopLevelChildren.push_back(createOr(move(ScopeIterators))); // Add proximity paths boosting. auto BoostingIterators = createFileProximityIterators( - Req.ProximityPaths, URISchemes, InvertedIndex, Corpus); + Req.ProximityPaths, URISchemes, InvertedIndex); // Boosting iterators do not actually filter symbols. In order to preserve // the validity of resulting query, TRUE iterator should be added along // BOOSTs. if (!BoostingIterators.empty()) { - BoostingIterators.push_back(Corpus.all()); - TopLevelChildren.push_back(Corpus.unionOf(move(BoostingIterators))); + BoostingIterators.push_back(createTrue(Symbols.size())); + TopLevelChildren.push_back(createOr(move(BoostingIterators))); } if (Req.RestrictForCodeCompletion) @@ -198,14 +196,14 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, // Use TRUE iterator if both trigrams and scopes from the query are not // present in the symbol index. auto QueryIterator = TopLevelChildren.empty() - ? Corpus.all() - : Corpus.intersect(move(TopLevelChildren)); + ? createTrue(Symbols.size()) + : createAnd(move(TopLevelChildren)); // Retrieve more items than it was requested: some of the items with high // final score might not be retrieved otherwise. // FIXME(kbobyrev): Pre-scoring retrieval threshold should be adjusted as // using 100x of the requested number might not be good in practice, e.g. // when the requested number of items is small. - auto Root = Req.Limit ? Corpus.limit(move(QueryIterator), *Req.Limit * 100) + auto Root = Req.Limit ? createLimit(move(QueryIterator), *Req.Limit * 100) : move(QueryIterator); SPAN_ATTACH(Tracer, "query", llvm::to_string(*Root)); vlog("Dex query tree: {0}", *Root); diff --git a/clangd/index/dex/Dex.h b/clangd/index/dex/Dex.h index d8360ba3f..4b2d85658 100644 --- a/clangd/index/dex/Dex.h +++ b/clangd/index/dex/Dex.h @@ -44,7 +44,7 @@ class Dex : public SymbolIndex { // All symbols must outlive this index. template Dex(Range &&Symbols, llvm::ArrayRef Schemes) - : Corpus(0), URISchemes(Schemes) { + : URISchemes(Schemes) { // If Schemes don't contain any items, fall back to SymbolCollector's // default URI schemes. if (URISchemes.empty()) { @@ -101,7 +101,6 @@ class Dex : public SymbolIndex { /// std. Inverted index is used to retrieve posting lists which are processed /// during the fuzzyFind process. llvm::DenseMap InvertedIndex; - Corpus Corpus; std::shared_ptr KeepAlive; // poor man's move-only std::any // Size of memory retained by KeepAlive. size_t BackingDataSize = 0; diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index e7794d4aa..de6232974 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -64,7 +64,7 @@ class AndIterator : public Iterator { float consume() override { assert(!reachedEnd() && "AND iterator can't consume() at the end."); - float Boost = 1; + float Boost = DEFAULT_BOOST_SCORE; for (const auto &Child : Children) Boost *= Child->consume(); return Boost; @@ -175,12 +175,12 @@ class OrIterator : public Iterator { return Result; } - // Returns the maximum boosting score among all Children when iterator - // points to the current ID. + // Returns the maximum boosting score among all Children when iterator is not + // exhausted and points to the given ID, DEFAULT_BOOST_SCORE otherwise. float consume() override { assert(!reachedEnd() && "OR iterator can't consume() at the end."); const DocID ID = peek(); - float Boost = 1; + float Boost = DEFAULT_BOOST_SCORE; for (const auto &Child : Children) if (!Child->reachedEnd() && Child->peek() == ID) Boost = std::max(Boost, Child->consume()); @@ -236,7 +236,7 @@ class TrueIterator : public Iterator { float consume() override { assert(!reachedEnd() && "TRUE iterator can't consume() at the end."); - return 1; + return DEFAULT_BOOST_SCORE; } size_t estimateSize() const override { return Size; } @@ -330,30 +330,30 @@ std::vector> consume(Iterator &It) { } std::unique_ptr -Corpus::intersect(std::vector> Children) const { +createAnd(std::vector> Children) { // If there is exactly one child, pull it one level up: AND(Child) -> Child. return Children.size() == 1 ? std::move(Children.front()) : llvm::make_unique(move(Children)); } std::unique_ptr -Corpus::unionOf(std::vector> Children) const { +createOr(std::vector> Children) { // If there is exactly one child, pull it one level up: OR(Child) -> Child. return Children.size() == 1 ? std::move(Children.front()) : llvm::make_unique(move(Children)); } -std::unique_ptr Corpus::all() const { +std::unique_ptr createTrue(DocID Size) { return llvm::make_unique(Size); } -std::unique_ptr Corpus::boost(std::unique_ptr Child, - float Factor) const { +std::unique_ptr createBoost(std::unique_ptr Child, + float Factor) { return llvm::make_unique(move(Child), Factor); } -std::unique_ptr Corpus::limit(std::unique_ptr Child, - size_t Limit) const { +std::unique_ptr createLimit(std::unique_ptr Child, + size_t Limit) { return llvm::make_unique(move(Child), Limit); } diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 2b3bd4481..04b344123 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -98,6 +98,8 @@ class Iterator { return Iterator.dump(OS); } + constexpr static float DEFAULT_BOOST_SCORE = 1; + private: virtual llvm::raw_ostream &dump(llvm::raw_ostream &OS) const = 0; }; @@ -112,74 +114,69 @@ class Iterator { /// to acquire preliminary scores of requested items. std::vector> consume(Iterator &It); -namespace detail { -// Variadic template machinery. -inline void populateChildren(std::vector> &) {} -template -void populateChildren(std::vector> &Children, - std::unique_ptr Head, TailT... Tail) { - Children.push_back(move(Head)); - populateChildren(Children, move(Tail)...); -} -} // namespace detail - -// A corpus is a set of documents, and a factory for iterators over them. -class Corpus { - DocID Size; +/// Returns AND Iterator which performs the intersection of the PostingLists of +/// its children. +/// +/// consume(): AND Iterator returns the product of Childrens' boosting scores +/// when not exhausted and DEFAULT_BOOST_SCORE otherwise. +std::unique_ptr +createAnd(std::vector> Children); -public: - explicit Corpus(DocID Size) : Size(Size) {} +/// Returns OR Iterator which performs the union of the PostingLists of its +/// children. +/// +/// consume(): OR Iterator returns the highest boost value among children +/// pointing to requested item when not exhausted and DEFAULT_BOOST_SCORE +/// otherwise. +std::unique_ptr +createOr(std::vector> Children); + +/// Returns TRUE Iterator which iterates over "virtual" PostingList containing +/// all items in range [0, Size) in an efficient manner. +/// +/// TRUE returns DEFAULT_BOOST_SCORE for each processed item. +std::unique_ptr createTrue(DocID Size); + +/// Returns BOOST iterator which multiplies the score of each item by given +/// factor. Boosting can be used as a computationally inexpensive filtering. +/// Users can return significantly more items using consumeAndBoost() and then +/// trim Top K using retrieval score. +std::unique_ptr createBoost(std::unique_ptr Child, + float Factor); + +/// Returns LIMIT iterator, which yields up to N elements of its child iterator. +/// Elements only count towards the limit if they are part of the final result +/// set. Therefore the following iterator (AND (2) (LIMIT (1 2) 1)) yields (2), +/// not (). +std::unique_ptr createLimit(std::unique_ptr Child, + size_t Limit); + +/// This allows createAnd(create(...), create(...)) syntax. +template std::unique_ptr createAnd(Args... args) { + std::vector> Children; + populateChildren(Children, args...); + return createAnd(move(Children)); +} - /// Returns AND Iterator which performs the intersection of the PostingLists - /// of its children. - /// - /// consume(): AND Iterator returns the product of Childrens' boosting - /// scores. - std::unique_ptr - intersect(std::vector> Children) const; +/// This allows createOr(create(...), create(...)) syntax. +template std::unique_ptr createOr(Args... args) { + std::vector> Children; + populateChildren(Children, args...); + return createOr(move(Children)); +} - /// Returns OR Iterator which performs the union of the PostingLists of its - /// children. - /// - /// consume(): OR Iterator returns the highest boost value among children - /// containing the requested item. - std::unique_ptr - unionOf(std::vector> Children) const; - - /// Returns TRUE Iterator which iterates over "virtual" PostingList - /// containing all items in range [0, Size) in an efficient manner. - std::unique_ptr all() const; - - /// Returns BOOST iterator which multiplies the score of each item by given - /// factor. Boosting can be used as a computationally inexpensive filtering. - /// Users can return significantly more items using consumeAndBoost() and - /// then trim Top K using retrieval score. - std::unique_ptr boost(std::unique_ptr Child, - float Factor) const; - - /// Returns LIMIT iterator, which yields up to N elements of its child - /// iterator. Elements only count towards the limit if they are part of the - /// final result set. Therefore the following iterator (AND (2) (LIMIT (1 2) - /// 1)) yields (2), not (). - std::unique_ptr limit(std::unique_ptr Child, - size_t Limit) const; - - /// This allows intersect(create(...), create(...)) syntax. - template - std::unique_ptr intersect(Args... args) const { - std::vector> Children; - detail::populateChildren(Children, std::forward(args)...); - return intersect(move(Children)); - } +template +void populateChildren(std::vector> &Children, + HeadT &Head, TailT &... Tail) { + Children.push_back(move(Head)); + populateChildren(Children, Tail...); +} - /// This allows unionOf(create(...), create(...)) syntax. - template - std::unique_ptr unionOf(Args... args) const { - std::vector> Children; - detail::populateChildren(Children, std::forward(args)...); - return unionOf(move(Children)); - } -}; +template +void populateChildren(std::vector> &Children, + HeadT &Head) { + Children.push_back(move(Head)); +} } // namespace dex } // namespace clangd diff --git a/clangd/index/dex/PostingList.cpp b/clangd/index/dex/PostingList.cpp index 4cb6395aa..cab869f6f 100644 --- a/clangd/index/dex/PostingList.cpp +++ b/clangd/index/dex/PostingList.cpp @@ -63,7 +63,7 @@ class ChunkIterator : public Iterator { float consume() override { assert(!reachedEnd() && "Posting List iterator can't consume() at the end."); - return 1; + return DEFAULT_BOOST_SCORE; } size_t estimateSize() const override { diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 28ec9c5c8..6d761f513 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -70,16 +70,15 @@ TEST(DexIterators, DocumentIterator) { } TEST(DexIterators, AndTwoLists) { - Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); - auto And = C.intersect(L1.iterator(), L0.iterator()); + auto And = createAnd(L1.iterator(), L0.iterator()); EXPECT_FALSE(And->reachedEnd()); EXPECT_THAT(consumeIDs(*And), ElementsAre(0U, 7U, 10U, 320U, 9000U)); - And = C.intersect(L0.iterator(), L1.iterator()); + And = createAnd(L0.iterator(), L1.iterator()); And->advanceTo(0); EXPECT_EQ(And->peek(), 0U); @@ -95,12 +94,11 @@ TEST(DexIterators, AndTwoLists) { } TEST(DexIterators, AndThreeLists) { - Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); const PostingList L2({1, 4, 7, 11, 30, 60, 320, 9000}); - auto And = C.intersect(L0.iterator(), L1.iterator(), L2.iterator()); + auto And = createAnd(L0.iterator(), L1.iterator(), L2.iterator()); EXPECT_EQ(And->peek(), 7U); And->advanceTo(300); EXPECT_EQ(And->peek(), 320U); @@ -110,11 +108,10 @@ TEST(DexIterators, AndThreeLists) { } TEST(DexIterators, OrTwoLists) { - Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); - auto Or = C.unionOf(L0.iterator(), L1.iterator()); + auto Or = createOr(L0.iterator(), L1.iterator()); EXPECT_FALSE(Or->reachedEnd()); EXPECT_EQ(Or->peek(), 0U); @@ -137,19 +134,18 @@ TEST(DexIterators, OrTwoLists) { Or->advanceTo(9001); EXPECT_TRUE(Or->reachedEnd()); - Or = C.unionOf(L0.iterator(), L1.iterator()); + Or = createOr(L0.iterator(), L1.iterator()); EXPECT_THAT(consumeIDs(*Or), ElementsAre(0U, 4U, 5U, 7U, 10U, 30U, 42U, 60U, 320U, 9000U)); } TEST(DexIterators, OrThreeLists) { - Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); const PostingList L2({1, 4, 7, 11, 30, 60, 320, 9000}); - auto Or = C.unionOf(L0.iterator(), L1.iterator(), L2.iterator()); + auto Or = createOr(L0.iterator(), L1.iterator(), L2.iterator()); EXPECT_FALSE(Or->reachedEnd()); EXPECT_EQ(Or->peek(), 0U); @@ -198,18 +194,17 @@ TEST(DexIterators, QueryTree) { // |1, 5, 7, 9| |1, 5| |0, 3, 5| // +----------+ +----+ +-------+ // - Corpus C{10}; const PostingList L0({1, 3, 5, 8, 9}); const PostingList L1({1, 5, 7, 9}); const PostingList L2({1, 5}); const PostingList L3({0, 3, 5}); // Root of the query tree: [1, 5] - auto Root = C.intersect( + auto Root = createAnd( // Lower And Iterator: [1, 5, 9] - C.intersect(L0.iterator(), C.boost(L1.iterator(), 2U)), + createAnd(L0.iterator(), createBoost(L1.iterator(), 2U)), // Lower Or Iterator: [0, 1, 5] - C.unionOf(C.boost(L2.iterator(), 3U), C.boost(L3.iterator(), 4U))); + createOr(createBoost(L2.iterator(), 3U), createBoost(L3.iterator(), 4U))); EXPECT_FALSE(Root->reachedEnd()); EXPECT_EQ(Root->peek(), 1U); @@ -231,7 +226,6 @@ TEST(DexIterators, QueryTree) { } TEST(DexIterators, StringRepresentation) { - Corpus C{10}; const PostingList L1({1, 3, 5}); const PostingList L2({1, 7, 9}); @@ -244,60 +238,56 @@ TEST(DexIterators, StringRepresentation) { auto I2 = L1.iterator(&Tok); EXPECT_EQ(llvm::to_string(*I2), "T=L2"); - auto Tree = C.limit(C.intersect(move(I1), move(I2)), 10); + auto Tree = createLimit(createAnd(move(I1), move(I2)), 10); EXPECT_EQ(llvm::to_string(*Tree), "(LIMIT 10 (& [1 3 5] T=L2))"); } TEST(DexIterators, Limit) { - Corpus C{10000}; const PostingList L0({3, 6, 7, 20, 42, 100}); const PostingList L1({1, 3, 5, 6, 7, 30, 100}); const PostingList L2({0, 3, 5, 7, 8, 100}); - auto DocIterator = C.limit(L0.iterator(), 42); + auto DocIterator = createLimit(L0.iterator(), 42); EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(3, 6, 7, 20, 42, 100)); - DocIterator = C.limit(L0.iterator(), 3); + DocIterator = createLimit(L0.iterator(), 3); EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(3, 6, 7)); - DocIterator = C.limit(L0.iterator(), 0); + DocIterator = createLimit(L0.iterator(), 0); EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre()); - auto AndIterator = - C.intersect(C.limit(C.all(), 343), C.limit(L0.iterator(), 2), - C.limit(L1.iterator(), 3), C.limit(L2.iterator(), 42)); + auto AndIterator = createAnd( + createLimit(createTrue(9000), 343), createLimit(L0.iterator(), 2), + createLimit(L1.iterator(), 3), createLimit(L2.iterator(), 42)); EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(3, 7)); } TEST(DexIterators, True) { - Corpus C{0}; - auto TrueIterator = C.all(); + auto TrueIterator = createTrue(0U); EXPECT_TRUE(TrueIterator->reachedEnd()); EXPECT_THAT(consumeIDs(*TrueIterator), ElementsAre()); - C = Corpus{7}; const PostingList L0({1, 2, 5, 7}); - TrueIterator = C.all(); + TrueIterator = createTrue(7U); EXPECT_THAT(TrueIterator->peek(), 0); - auto AndIterator = C.intersect(L0.iterator(), move(TrueIterator)); + auto AndIterator = createAnd(L0.iterator(), move(TrueIterator)); EXPECT_FALSE(AndIterator->reachedEnd()); EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(1, 2, 5)); } TEST(DexIterators, Boost) { - Corpus C{5}; - auto BoostIterator = C.boost(C.all(), 42U); + auto BoostIterator = createBoost(createTrue(5U), 42U); EXPECT_FALSE(BoostIterator->reachedEnd()); auto ElementBoost = BoostIterator->consume(); EXPECT_THAT(ElementBoost, 42U); const PostingList L0({2, 4}); const PostingList L1({1, 4}); - auto Root = C.unionOf(C.all(), C.boost(L0.iterator(), 2U), - C.boost(L1.iterator(), 3U)); + auto Root = createOr(createTrue(5U), createBoost(L0.iterator(), 2U), + createBoost(L1.iterator(), 3U)); ElementBoost = Root->consume(); - EXPECT_THAT(ElementBoost, 1); + EXPECT_THAT(ElementBoost, Iterator::DEFAULT_BOOST_SCORE); Root->advance(); EXPECT_THAT(Root->peek(), 1U); ElementBoost = Root->consume(); From b7c580978ed2dc9314ee790163095b51b5704c9e Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 2 Oct 2018 19:59:23 +0000 Subject: [PATCH 288/686] Reland r343589 "[clangd] Dex: add Corpus factory for iterators, rename, fold constant. NFC"" This reverts commit r343610. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343622 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Dex.cpp | 28 +++---- clangd/index/dex/Dex.h | 3 +- clangd/index/dex/Iterator.cpp | 24 +++--- clangd/index/dex/Iterator.h | 125 ++++++++++++++++--------------- clangd/index/dex/PostingList.cpp | 2 +- unittests/clangd/DexTests.cpp | 56 ++++++++------ 6 files changed, 127 insertions(+), 111 deletions(-) diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 2c0c3d738..3b236f891 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -56,7 +56,8 @@ std::vector generateSearchTokens(const Symbol &Sym) { std::vector> createFileProximityIterators( llvm::ArrayRef ProximityPaths, llvm::ArrayRef URISchemes, - const llvm::DenseMap &InvertedIndex) { + const llvm::DenseMap &InvertedIndex, + const Corpus &Corpus) { std::vector> BoostingIterators; // Deduplicate parent URIs extracted from the ProximityPaths. llvm::StringSet<> ParentURIs; @@ -91,8 +92,8 @@ std::vector> createFileProximityIterators( if (It != InvertedIndex.end()) { // FIXME(kbobyrev): Append LIMIT on top of every BOOST iterator. PathProximitySignals.SymbolURI = ParentURI; - BoostingIterators.push_back(createBoost(It->second.iterator(&It->first), - PathProximitySignals.evaluate())); + BoostingIterators.push_back(Corpus.boost( + It->second.iterator(&It->first), PathProximitySignals.evaluate())); } } return BoostingIterators; @@ -101,6 +102,7 @@ std::vector> createFileProximityIterators( } // namespace void Dex::buildIndex() { + this->Corpus = dex::Corpus(Symbols.size()); std::vector> ScoredSymbols(Symbols.size()); for (size_t I = 0; I < Symbols.size(); ++I) { @@ -159,7 +161,7 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, TrigramIterators.push_back(It->second.iterator(&It->first)); } if (!TrigramIterators.empty()) - TopLevelChildren.push_back(createAnd(move(TrigramIterators))); + TopLevelChildren.push_back(Corpus.intersect(move(TrigramIterators))); // Generate scope tokens for search query. std::vector> ScopeIterators; @@ -170,22 +172,22 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, ScopeIterators.push_back(It->second.iterator(&It->first)); } if (Req.AnyScope) - ScopeIterators.push_back(createBoost(createTrue(Symbols.size()), - ScopeIterators.empty() ? 1.0 : 0.2)); + ScopeIterators.push_back( + Corpus.boost(Corpus.all(), ScopeIterators.empty() ? 1.0 : 0.2)); // Add OR iterator for scopes if there are any Scope Iterators. if (!ScopeIterators.empty()) - TopLevelChildren.push_back(createOr(move(ScopeIterators))); + TopLevelChildren.push_back(Corpus.unionOf(move(ScopeIterators))); // Add proximity paths boosting. auto BoostingIterators = createFileProximityIterators( - Req.ProximityPaths, URISchemes, InvertedIndex); + Req.ProximityPaths, URISchemes, InvertedIndex, Corpus); // Boosting iterators do not actually filter symbols. In order to preserve // the validity of resulting query, TRUE iterator should be added along // BOOSTs. if (!BoostingIterators.empty()) { - BoostingIterators.push_back(createTrue(Symbols.size())); - TopLevelChildren.push_back(createOr(move(BoostingIterators))); + BoostingIterators.push_back(Corpus.all()); + TopLevelChildren.push_back(Corpus.unionOf(move(BoostingIterators))); } if (Req.RestrictForCodeCompletion) @@ -196,14 +198,14 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, // Use TRUE iterator if both trigrams and scopes from the query are not // present in the symbol index. auto QueryIterator = TopLevelChildren.empty() - ? createTrue(Symbols.size()) - : createAnd(move(TopLevelChildren)); + ? Corpus.all() + : Corpus.intersect(move(TopLevelChildren)); // Retrieve more items than it was requested: some of the items with high // final score might not be retrieved otherwise. // FIXME(kbobyrev): Pre-scoring retrieval threshold should be adjusted as // using 100x of the requested number might not be good in practice, e.g. // when the requested number of items is small. - auto Root = Req.Limit ? createLimit(move(QueryIterator), *Req.Limit * 100) + auto Root = Req.Limit ? Corpus.limit(move(QueryIterator), *Req.Limit * 100) : move(QueryIterator); SPAN_ATTACH(Tracer, "query", llvm::to_string(*Root)); vlog("Dex query tree: {0}", *Root); diff --git a/clangd/index/dex/Dex.h b/clangd/index/dex/Dex.h index 4b2d85658..baa215310 100644 --- a/clangd/index/dex/Dex.h +++ b/clangd/index/dex/Dex.h @@ -44,7 +44,7 @@ class Dex : public SymbolIndex { // All symbols must outlive this index. template Dex(Range &&Symbols, llvm::ArrayRef Schemes) - : URISchemes(Schemes) { + : Corpus(0), URISchemes(Schemes) { // If Schemes don't contain any items, fall back to SymbolCollector's // default URI schemes. if (URISchemes.empty()) { @@ -101,6 +101,7 @@ class Dex : public SymbolIndex { /// std. Inverted index is used to retrieve posting lists which are processed /// during the fuzzyFind process. llvm::DenseMap InvertedIndex; + dex::Corpus Corpus; std::shared_ptr KeepAlive; // poor man's move-only std::any // Size of memory retained by KeepAlive. size_t BackingDataSize = 0; diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index de6232974..e7794d4aa 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -64,7 +64,7 @@ class AndIterator : public Iterator { float consume() override { assert(!reachedEnd() && "AND iterator can't consume() at the end."); - float Boost = DEFAULT_BOOST_SCORE; + float Boost = 1; for (const auto &Child : Children) Boost *= Child->consume(); return Boost; @@ -175,12 +175,12 @@ class OrIterator : public Iterator { return Result; } - // Returns the maximum boosting score among all Children when iterator is not - // exhausted and points to the given ID, DEFAULT_BOOST_SCORE otherwise. + // Returns the maximum boosting score among all Children when iterator + // points to the current ID. float consume() override { assert(!reachedEnd() && "OR iterator can't consume() at the end."); const DocID ID = peek(); - float Boost = DEFAULT_BOOST_SCORE; + float Boost = 1; for (const auto &Child : Children) if (!Child->reachedEnd() && Child->peek() == ID) Boost = std::max(Boost, Child->consume()); @@ -236,7 +236,7 @@ class TrueIterator : public Iterator { float consume() override { assert(!reachedEnd() && "TRUE iterator can't consume() at the end."); - return DEFAULT_BOOST_SCORE; + return 1; } size_t estimateSize() const override { return Size; } @@ -330,30 +330,30 @@ std::vector> consume(Iterator &It) { } std::unique_ptr -createAnd(std::vector> Children) { +Corpus::intersect(std::vector> Children) const { // If there is exactly one child, pull it one level up: AND(Child) -> Child. return Children.size() == 1 ? std::move(Children.front()) : llvm::make_unique(move(Children)); } std::unique_ptr -createOr(std::vector> Children) { +Corpus::unionOf(std::vector> Children) const { // If there is exactly one child, pull it one level up: OR(Child) -> Child. return Children.size() == 1 ? std::move(Children.front()) : llvm::make_unique(move(Children)); } -std::unique_ptr createTrue(DocID Size) { +std::unique_ptr Corpus::all() const { return llvm::make_unique(Size); } -std::unique_ptr createBoost(std::unique_ptr Child, - float Factor) { +std::unique_ptr Corpus::boost(std::unique_ptr Child, + float Factor) const { return llvm::make_unique(move(Child), Factor); } -std::unique_ptr createLimit(std::unique_ptr Child, - size_t Limit) { +std::unique_ptr Corpus::limit(std::unique_ptr Child, + size_t Limit) const { return llvm::make_unique(move(Child), Limit); } diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 04b344123..2b3bd4481 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -98,8 +98,6 @@ class Iterator { return Iterator.dump(OS); } - constexpr static float DEFAULT_BOOST_SCORE = 1; - private: virtual llvm::raw_ostream &dump(llvm::raw_ostream &OS) const = 0; }; @@ -114,69 +112,74 @@ class Iterator { /// to acquire preliminary scores of requested items. std::vector> consume(Iterator &It); -/// Returns AND Iterator which performs the intersection of the PostingLists of -/// its children. -/// -/// consume(): AND Iterator returns the product of Childrens' boosting scores -/// when not exhausted and DEFAULT_BOOST_SCORE otherwise. -std::unique_ptr -createAnd(std::vector> Children); - -/// Returns OR Iterator which performs the union of the PostingLists of its -/// children. -/// -/// consume(): OR Iterator returns the highest boost value among children -/// pointing to requested item when not exhausted and DEFAULT_BOOST_SCORE -/// otherwise. -std::unique_ptr -createOr(std::vector> Children); - -/// Returns TRUE Iterator which iterates over "virtual" PostingList containing -/// all items in range [0, Size) in an efficient manner. -/// -/// TRUE returns DEFAULT_BOOST_SCORE for each processed item. -std::unique_ptr createTrue(DocID Size); - -/// Returns BOOST iterator which multiplies the score of each item by given -/// factor. Boosting can be used as a computationally inexpensive filtering. -/// Users can return significantly more items using consumeAndBoost() and then -/// trim Top K using retrieval score. -std::unique_ptr createBoost(std::unique_ptr Child, - float Factor); - -/// Returns LIMIT iterator, which yields up to N elements of its child iterator. -/// Elements only count towards the limit if they are part of the final result -/// set. Therefore the following iterator (AND (2) (LIMIT (1 2) 1)) yields (2), -/// not (). -std::unique_ptr createLimit(std::unique_ptr Child, - size_t Limit); - -/// This allows createAnd(create(...), create(...)) syntax. -template std::unique_ptr createAnd(Args... args) { - std::vector> Children; - populateChildren(Children, args...); - return createAnd(move(Children)); -} - -/// This allows createOr(create(...), create(...)) syntax. -template std::unique_ptr createOr(Args... args) { - std::vector> Children; - populateChildren(Children, args...); - return createOr(move(Children)); -} - -template +namespace detail { +// Variadic template machinery. +inline void populateChildren(std::vector> &) {} +template void populateChildren(std::vector> &Children, - HeadT &Head, TailT &... Tail) { + std::unique_ptr Head, TailT... Tail) { Children.push_back(move(Head)); - populateChildren(Children, Tail...); + populateChildren(Children, move(Tail)...); } +} // namespace detail -template -void populateChildren(std::vector> &Children, - HeadT &Head) { - Children.push_back(move(Head)); -} +// A corpus is a set of documents, and a factory for iterators over them. +class Corpus { + DocID Size; + +public: + explicit Corpus(DocID Size) : Size(Size) {} + + /// Returns AND Iterator which performs the intersection of the PostingLists + /// of its children. + /// + /// consume(): AND Iterator returns the product of Childrens' boosting + /// scores. + std::unique_ptr + intersect(std::vector> Children) const; + + /// Returns OR Iterator which performs the union of the PostingLists of its + /// children. + /// + /// consume(): OR Iterator returns the highest boost value among children + /// containing the requested item. + std::unique_ptr + unionOf(std::vector> Children) const; + + /// Returns TRUE Iterator which iterates over "virtual" PostingList + /// containing all items in range [0, Size) in an efficient manner. + std::unique_ptr all() const; + + /// Returns BOOST iterator which multiplies the score of each item by given + /// factor. Boosting can be used as a computationally inexpensive filtering. + /// Users can return significantly more items using consumeAndBoost() and + /// then trim Top K using retrieval score. + std::unique_ptr boost(std::unique_ptr Child, + float Factor) const; + + /// Returns LIMIT iterator, which yields up to N elements of its child + /// iterator. Elements only count towards the limit if they are part of the + /// final result set. Therefore the following iterator (AND (2) (LIMIT (1 2) + /// 1)) yields (2), not (). + std::unique_ptr limit(std::unique_ptr Child, + size_t Limit) const; + + /// This allows intersect(create(...), create(...)) syntax. + template + std::unique_ptr intersect(Args... args) const { + std::vector> Children; + detail::populateChildren(Children, std::forward(args)...); + return intersect(move(Children)); + } + + /// This allows unionOf(create(...), create(...)) syntax. + template + std::unique_ptr unionOf(Args... args) const { + std::vector> Children; + detail::populateChildren(Children, std::forward(args)...); + return unionOf(move(Children)); + } +}; } // namespace dex } // namespace clangd diff --git a/clangd/index/dex/PostingList.cpp b/clangd/index/dex/PostingList.cpp index cab869f6f..4cb6395aa 100644 --- a/clangd/index/dex/PostingList.cpp +++ b/clangd/index/dex/PostingList.cpp @@ -63,7 +63,7 @@ class ChunkIterator : public Iterator { float consume() override { assert(!reachedEnd() && "Posting List iterator can't consume() at the end."); - return DEFAULT_BOOST_SCORE; + return 1; } size_t estimateSize() const override { diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 6d761f513..28ec9c5c8 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -70,15 +70,16 @@ TEST(DexIterators, DocumentIterator) { } TEST(DexIterators, AndTwoLists) { + Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); - auto And = createAnd(L1.iterator(), L0.iterator()); + auto And = C.intersect(L1.iterator(), L0.iterator()); EXPECT_FALSE(And->reachedEnd()); EXPECT_THAT(consumeIDs(*And), ElementsAre(0U, 7U, 10U, 320U, 9000U)); - And = createAnd(L0.iterator(), L1.iterator()); + And = C.intersect(L0.iterator(), L1.iterator()); And->advanceTo(0); EXPECT_EQ(And->peek(), 0U); @@ -94,11 +95,12 @@ TEST(DexIterators, AndTwoLists) { } TEST(DexIterators, AndThreeLists) { + Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); const PostingList L2({1, 4, 7, 11, 30, 60, 320, 9000}); - auto And = createAnd(L0.iterator(), L1.iterator(), L2.iterator()); + auto And = C.intersect(L0.iterator(), L1.iterator(), L2.iterator()); EXPECT_EQ(And->peek(), 7U); And->advanceTo(300); EXPECT_EQ(And->peek(), 320U); @@ -108,10 +110,11 @@ TEST(DexIterators, AndThreeLists) { } TEST(DexIterators, OrTwoLists) { + Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); - auto Or = createOr(L0.iterator(), L1.iterator()); + auto Or = C.unionOf(L0.iterator(), L1.iterator()); EXPECT_FALSE(Or->reachedEnd()); EXPECT_EQ(Or->peek(), 0U); @@ -134,18 +137,19 @@ TEST(DexIterators, OrTwoLists) { Or->advanceTo(9001); EXPECT_TRUE(Or->reachedEnd()); - Or = createOr(L0.iterator(), L1.iterator()); + Or = C.unionOf(L0.iterator(), L1.iterator()); EXPECT_THAT(consumeIDs(*Or), ElementsAre(0U, 4U, 5U, 7U, 10U, 30U, 42U, 60U, 320U, 9000U)); } TEST(DexIterators, OrThreeLists) { + Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); const PostingList L1({0, 4, 7, 10, 30, 60, 320, 9000}); const PostingList L2({1, 4, 7, 11, 30, 60, 320, 9000}); - auto Or = createOr(L0.iterator(), L1.iterator(), L2.iterator()); + auto Or = C.unionOf(L0.iterator(), L1.iterator(), L2.iterator()); EXPECT_FALSE(Or->reachedEnd()); EXPECT_EQ(Or->peek(), 0U); @@ -194,17 +198,18 @@ TEST(DexIterators, QueryTree) { // |1, 5, 7, 9| |1, 5| |0, 3, 5| // +----------+ +----+ +-------+ // + Corpus C{10}; const PostingList L0({1, 3, 5, 8, 9}); const PostingList L1({1, 5, 7, 9}); const PostingList L2({1, 5}); const PostingList L3({0, 3, 5}); // Root of the query tree: [1, 5] - auto Root = createAnd( + auto Root = C.intersect( // Lower And Iterator: [1, 5, 9] - createAnd(L0.iterator(), createBoost(L1.iterator(), 2U)), + C.intersect(L0.iterator(), C.boost(L1.iterator(), 2U)), // Lower Or Iterator: [0, 1, 5] - createOr(createBoost(L2.iterator(), 3U), createBoost(L3.iterator(), 4U))); + C.unionOf(C.boost(L2.iterator(), 3U), C.boost(L3.iterator(), 4U))); EXPECT_FALSE(Root->reachedEnd()); EXPECT_EQ(Root->peek(), 1U); @@ -226,6 +231,7 @@ TEST(DexIterators, QueryTree) { } TEST(DexIterators, StringRepresentation) { + Corpus C{10}; const PostingList L1({1, 3, 5}); const PostingList L2({1, 7, 9}); @@ -238,56 +244,60 @@ TEST(DexIterators, StringRepresentation) { auto I2 = L1.iterator(&Tok); EXPECT_EQ(llvm::to_string(*I2), "T=L2"); - auto Tree = createLimit(createAnd(move(I1), move(I2)), 10); + auto Tree = C.limit(C.intersect(move(I1), move(I2)), 10); EXPECT_EQ(llvm::to_string(*Tree), "(LIMIT 10 (& [1 3 5] T=L2))"); } TEST(DexIterators, Limit) { + Corpus C{10000}; const PostingList L0({3, 6, 7, 20, 42, 100}); const PostingList L1({1, 3, 5, 6, 7, 30, 100}); const PostingList L2({0, 3, 5, 7, 8, 100}); - auto DocIterator = createLimit(L0.iterator(), 42); + auto DocIterator = C.limit(L0.iterator(), 42); EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(3, 6, 7, 20, 42, 100)); - DocIterator = createLimit(L0.iterator(), 3); + DocIterator = C.limit(L0.iterator(), 3); EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre(3, 6, 7)); - DocIterator = createLimit(L0.iterator(), 0); + DocIterator = C.limit(L0.iterator(), 0); EXPECT_THAT(consumeIDs(*DocIterator), ElementsAre()); - auto AndIterator = createAnd( - createLimit(createTrue(9000), 343), createLimit(L0.iterator(), 2), - createLimit(L1.iterator(), 3), createLimit(L2.iterator(), 42)); + auto AndIterator = + C.intersect(C.limit(C.all(), 343), C.limit(L0.iterator(), 2), + C.limit(L1.iterator(), 3), C.limit(L2.iterator(), 42)); EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(3, 7)); } TEST(DexIterators, True) { - auto TrueIterator = createTrue(0U); + Corpus C{0}; + auto TrueIterator = C.all(); EXPECT_TRUE(TrueIterator->reachedEnd()); EXPECT_THAT(consumeIDs(*TrueIterator), ElementsAre()); + C = Corpus{7}; const PostingList L0({1, 2, 5, 7}); - TrueIterator = createTrue(7U); + TrueIterator = C.all(); EXPECT_THAT(TrueIterator->peek(), 0); - auto AndIterator = createAnd(L0.iterator(), move(TrueIterator)); + auto AndIterator = C.intersect(L0.iterator(), move(TrueIterator)); EXPECT_FALSE(AndIterator->reachedEnd()); EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(1, 2, 5)); } TEST(DexIterators, Boost) { - auto BoostIterator = createBoost(createTrue(5U), 42U); + Corpus C{5}; + auto BoostIterator = C.boost(C.all(), 42U); EXPECT_FALSE(BoostIterator->reachedEnd()); auto ElementBoost = BoostIterator->consume(); EXPECT_THAT(ElementBoost, 42U); const PostingList L0({2, 4}); const PostingList L1({1, 4}); - auto Root = createOr(createTrue(5U), createBoost(L0.iterator(), 2U), - createBoost(L1.iterator(), 3U)); + auto Root = C.unionOf(C.all(), C.boost(L0.iterator(), 2U), + C.boost(L1.iterator(), 3U)); ElementBoost = Root->consume(); - EXPECT_THAT(ElementBoost, Iterator::DEFAULT_BOOST_SCORE); + EXPECT_THAT(ElementBoost, 1); Root->advance(); EXPECT_THAT(Root->peek(), 1U); ElementBoost = Root->consume(); From 42ad06204b8e9126c0fc3c0d19678fa72c8bbdf3 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 2 Oct 2018 20:00:32 +0000 Subject: [PATCH 289/686] [clangd] Try to fix windows buildbot after r343576 http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast/builds/20347/steps/test/logs/stdio git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343623 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/ClangdTests.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/unittests/clangd/ClangdTests.cpp b/unittests/clangd/ClangdTests.cpp index 21300e906..937e7627b 100644 --- a/unittests/clangd/ClangdTests.cpp +++ b/unittests/clangd/ClangdTests.cpp @@ -1019,11 +1019,12 @@ TEST(ClangdTests, PreambleVFSStatCache) { runAddDocument(Server, SourcePath, Code.code()); - EXPECT_EQ(CountStats["foo.h"], 1u); + unsigned Before = CountStats["foo.h"]; + EXPECT_GT(Before, 0u); auto Completions = cantFail(runCodeComplete(Server, SourcePath, Code.point(), clangd::CodeCompleteOptions())) .Completions; - EXPECT_EQ(CountStats["foo.h"], 1u); + EXPECT_EQ(CountStats["foo.h"], Before); EXPECT_THAT(Completions, ElementsAre(Field(&CodeCompletion::Name, "TestSym"))); } From fd7d5a1be4a5c2f2d928d19294e736b9acfafc4b Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 2 Oct 2018 21:47:41 +0000 Subject: [PATCH 290/686] [clangd] Temporarily disable VFS stats cache test for windows. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343637 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/ClangdTests.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/unittests/clangd/ClangdTests.cpp b/unittests/clangd/ClangdTests.cpp index 937e7627b..b9003a863 100644 --- a/unittests/clangd/ClangdTests.cpp +++ b/unittests/clangd/ClangdTests.cpp @@ -963,6 +963,8 @@ TEST_F(ClangdVFSTest, ChangedHeaderFromISystem) { Field(&CodeCompletion::Name, "baz"))); } +// FIXME(ioeric): make this work for windows again. +#ifndef _WIN32 // Check that running code completion doesn't stat() a bunch of files from the // preamble again. (They should be using the preamble's stat-cache) TEST(ClangdTests, PreambleVFSStatCache) { @@ -1028,6 +1030,7 @@ TEST(ClangdTests, PreambleVFSStatCache) { EXPECT_THAT(Completions, ElementsAre(Field(&CodeCompletion::Name, "TestSym"))); } +#endif } // namespace } // namespace clangd From 01ab3fa30b98db3e1565ecf0ff9f6cb0fc8e1af8 Mon Sep 17 00:00:00 2001 From: Mikael Holmen Date: Wed, 3 Oct 2018 05:41:14 +0000 Subject: [PATCH 291/686] Fix compilation warning by removing unused variable [NFC] clang complained with ../tools/clang/tools/extra/clangd/FS.cpp:19:12: error: unused variable 'Err' [-Werror,-Wunused-variable] if (auto Err = FS.makeAbsolute(PathStore)) ^ 1 error generated. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343661 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/FS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/FS.cpp b/clangd/FS.cpp index ce62a5927..3fe0c8e56 100644 --- a/clangd/FS.cpp +++ b/clangd/FS.cpp @@ -16,7 +16,7 @@ namespace clangd { void PreambleFileStatusCache::update(const vfs::FileSystem &FS, vfs::Status S) { SmallString<32> PathStore(S.getName()); - if (auto Err = FS.makeAbsolute(PathStore)) + if (FS.makeAbsolute(PathStore)) return; // Stores the latest status in cache as it can change in a preamble build. StatCache.insert({PathStore, std::move(S)}); From 88ee6a9db03dab1d475b97645c05957f08453d2a Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Wed, 3 Oct 2018 07:52:44 +0000 Subject: [PATCH 292/686] [clang-query] Add single-letter 'q' alias for 'quit' Reviewers: aaron.ballman, pcc Reviewed By: aaron.ballman Subscribers: Szelethus, cfe-commits Differential Revision: https://reviews.llvm.org/D52746 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343664 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-query/Query.cpp | 2 +- clang-query/QueryParser.cpp | 1 + unittests/clang-query/QueryParserTest.cpp | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/clang-query/Query.cpp b/clang-query/Query.cpp index 072ee6b9c..63b4f3579 100644 --- a/clang-query/Query.cpp +++ b/clang-query/Query.cpp @@ -45,7 +45,7 @@ bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const { "Set whether to print bindings as diagnostics,\n" " " "AST pretty prints or AST dumps.\n" - " quit " + " quit, q " "Terminates the query session.\n\n"; return true; } diff --git a/clang-query/QueryParser.cpp b/clang-query/QueryParser.cpp index d05a0ba1c..ddedd1ece 100644 --- a/clang-query/QueryParser.cpp +++ b/clang-query/QueryParser.cpp @@ -166,6 +166,7 @@ QueryRef QueryParser::doParse() { .Case("let", PQK_Let) .Case("m", PQK_Match, /*IsCompletion=*/false) .Case("match", PQK_Match) + .Case("q", PQK_Quit, /*IsCompletion=*/false) .Case("quit", PQK_Quit) .Case("set", PQK_Set) .Case("unlet", PQK_Unlet) diff --git a/unittests/clang-query/QueryParserTest.cpp b/unittests/clang-query/QueryParserTest.cpp index 4304aa980..729862b5f 100644 --- a/unittests/clang-query/QueryParserTest.cpp +++ b/unittests/clang-query/QueryParserTest.cpp @@ -51,6 +51,9 @@ TEST_F(QueryParserTest, Quit) { QueryRef Q = parse("quit"); ASSERT_TRUE(isa(Q)); + Q = parse("q"); + ASSERT_TRUE(isa(Q)); + Q = parse("quit me"); ASSERT_TRUE(isa(Q)); EXPECT_EQ("unexpected extra input: ' me'", cast(Q)->ErrStr); From 875fedefa516a2c87b7b34be02a29cfc769d8022 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Wed, 3 Oct 2018 08:21:54 +0000 Subject: [PATCH 293/686] [clang-query] Add comment token handling Summary: It is possible to pass a file of commands to clang-query using the command line option -f or --preload. Make it possible to write comments in such files. Reviewers: aaron.ballman Reviewed By: aaron.ballman Subscribers: mgorny, cfe-commits Differential Revision: https://reviews.llvm.org/D52752 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343666 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-query/QueryParser.cpp | 8 ++++++++ unittests/clang-query/QueryParserTest.cpp | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/clang-query/QueryParser.cpp b/clang-query/QueryParser.cpp index ddedd1ece..55504f70c 100644 --- a/clang-query/QueryParser.cpp +++ b/clang-query/QueryParser.cpp @@ -37,6 +37,11 @@ StringRef QueryParser::lexWord() { ++Begin; } + if (*Begin == '#') { + End = Begin; + return StringRef(); + } + const char *WordBegin = Begin; while (true) { @@ -127,6 +132,7 @@ namespace { enum ParsedQueryKind { PQK_Invalid, + PQK_Comment, PQK_NoOp, PQK_Help, PQK_Let, @@ -161,6 +167,7 @@ QueryRef QueryParser::doParse() { StringRef CommandStr; ParsedQueryKind QKind = LexOrCompleteWord(this, CommandStr) .Case("", PQK_NoOp) + .Case("#", PQK_Comment, /*IsCompletion=*/false) .Case("help", PQK_Help) .Case("l", PQK_Let, /*IsCompletion=*/false) .Case("let", PQK_Let) @@ -173,6 +180,7 @@ QueryRef QueryParser::doParse() { .Default(PQK_Invalid); switch (QKind) { + case PQK_Comment: case PQK_NoOp: return new NoOpQuery; diff --git a/unittests/clang-query/QueryParserTest.cpp b/unittests/clang-query/QueryParserTest.cpp index 729862b5f..62844c896 100644 --- a/unittests/clang-query/QueryParserTest.cpp +++ b/unittests/clang-query/QueryParserTest.cpp @@ -146,6 +146,17 @@ TEST_F(QueryParserTest, LetUnlet) { cast(Q)->ErrStr); } +TEST_F(QueryParserTest, Comment) { + QueryRef Q = parse("# let foo decl()"); + ASSERT_TRUE(isa(Q)); + + Q = parse("let foo decl() # creates a decl() matcher called foo"); + ASSERT_TRUE(isa(Q)); + + Q = parse("set bind-root false # reduce noise"); + ASSERT_TRUE(isa>(Q)); +} + TEST_F(QueryParserTest, Complete) { std::vector Comps = QueryParser::complete("", 0, QS); From 88a94859252632b792037d5dc8e1f9c4590e8f0d Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Wed, 3 Oct 2018 10:37:19 +0000 Subject: [PATCH 294/686] [clang-tidy] NFC reorder registering in CppCoreGuidelines module git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343673 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp index 69ecd0c46..4c42661fb 100644 --- a/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ b/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -40,10 +40,10 @@ class CppCoreGuidelinesModule : public ClangTidyModule { void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck( "cppcoreguidelines-avoid-goto"); - CheckFactories.registerCheck( - "cppcoreguidelines-interfaces-global-init"); CheckFactories.registerCheck( "cppcoreguidelines-avoid-magic-numbers"); + CheckFactories.registerCheck( + "cppcoreguidelines-interfaces-global-init"); CheckFactories.registerCheck( "cppcoreguidelines-narrowing-conversions"); CheckFactories.registerCheck("cppcoreguidelines-no-malloc"); From 2afebbc02c98975b0787e7a285da6167030aeb5d Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Wed, 3 Oct 2018 18:25:27 +0000 Subject: [PATCH 295/686] [clang-doc] Avoid parsing undefined base classes Don't try to parse base classes for declarations that are not definitions (segfaults, as there is no DefinitionData to access). Differential Revision: https://reviews.llvm.org/D52313 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343703 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/Serialize.cpp | 3 + test/clang-doc/bc-record.cpp | 178 ++++++++++++---------- test/clang-doc/mapper-record.cpp | 142 +++++++++-------- test/clang-doc/md-record.cpp | 5 + test/clang-doc/public-record.cpp | 200 ++++++++++++------------ test/clang-doc/test_cases/record.cpp | 2 + test/clang-doc/yaml-record.cpp | 220 ++++++++++++++------------- 7 files changed, 406 insertions(+), 344 deletions(-) diff --git a/clang-doc/Serialize.cpp b/clang-doc/Serialize.cpp index 450822127..eb72c19c3 100644 --- a/clang-doc/Serialize.cpp +++ b/clang-doc/Serialize.cpp @@ -244,6 +244,9 @@ static void parseParameters(FunctionInfo &I, const FunctionDecl *D) { } static void parseBases(RecordInfo &I, const CXXRecordDecl *D) { + // Don't parse bases if this isn't a definition. + if (!D->isThisDeclarationADefinition()) + return; for (const CXXBaseSpecifier &B : D->bases()) { if (B.isVirtual()) continue; diff --git a/test/clang-doc/bc-record.cpp b/test/clang-doc/bc-record.cpp index 053954ad3..07f0da2eb 100644 --- a/test/clang-doc/bc-record.cpp +++ b/test/clang-doc/bc-record.cpp @@ -39,6 +39,8 @@ class X { class Y {}; }; +class G; + // RUN: clang-doc --dump-intermediate --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs @@ -158,118 +160,130 @@ class X { // CHECK-2-NEXT: // CHECK-2-NEXT:
-// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-3 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/4202E8BF0ECB12AE354C8499C52725B0EE30AED5.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Y' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'X' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: blob data = 'G' +// CHECK-3-NEXT: blob data = '{{.*}}' // CHECK-3-NEXT: // CHECK-3-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-4 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'H' -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'void' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'Y' +// CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'B' -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: blob data = 'X' -// CHECK-4-NEXT: blob data = 'Y' -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'Bc' -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'A' -// CHECK-4-NEXT: blob data = 'B' -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'X' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-5 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'D' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'H' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'void' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'B' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: blob data = 'X' +// CHECK-5-NEXT: blob data = 'Y' +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'Bc' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'A' +// CHECK-5-NEXT: blob data = 'B' +// CHECK-5-NEXT: +// CHECK-5-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-6 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-6 // CHECK-6: // CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: +// CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'F' -// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: blob data = 'D' +// CHECK-6-NEXT: blob data = '{{.*}}' // CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'D' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: // CHECK-6-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-7 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-7 // CHECK-7: // CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'A' -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'int' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'X' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'int' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'Y' -// CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'F' +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'E' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'D' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-8 +// CHECK-8: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'A' +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'int' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'X' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'int' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'Y' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: diff --git a/test/clang-doc/mapper-record.cpp b/test/clang-doc/mapper-record.cpp index 9f699219d..90f76b527 100644 --- a/test/clang-doc/mapper-record.cpp +++ b/test/clang-doc/mapper-record.cpp @@ -39,6 +39,8 @@ class X { class Y {}; }; +class G; + // RUN: clang-doc --dump-mapper --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs @@ -105,98 +107,110 @@ class X { // CHECK-2-NEXT: // CHECK-2-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-3 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/4202E8BF0ECB12AE354C8499C52725B0EE30AED5.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'Y' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = 'X' -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: blob data = 'G' +// CHECK-3-NEXT: blob data = '{{.*}}' // CHECK-3-NEXT: // CHECK-3-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-4 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = 'Y' +// CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'Bc' -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = 'A' -// CHECK-4-NEXT: blob data = 'B' -// CHECK-4-NEXT: -// CHECK-4-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-5 +// CHECK-4-NEXT: blob data = 'X' +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: +// CHECK-4-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = 'D' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: - -// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-6 +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'Bc' +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = 'A' +// CHECK-5-NEXT: blob data = 'B' +// CHECK-5-NEXT: +// CHECK-5-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-6 // CHECK-6: // CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: +// CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'F' -// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: blob data = 'D' +// CHECK-6-NEXT: blob data = '{{.*}}' // CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'E' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: blob data = 'D' -// CHECK-6-NEXT: -// CHECK-6-NEXT: -// CHECK-6-NEXT: // CHECK-6-NEXT: -// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-7 +// RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-7 // CHECK-7: // CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'A' -// CHECK-7-NEXT: blob data = '{{.*}}' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'int' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'X' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'int' -// CHECK-7-NEXT: -// CHECK-7-NEXT: -// CHECK-7-NEXT: blob data = 'Y' -// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'F' +// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'E' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: blob data = 'D' +// CHECK-7-NEXT: +// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: + +// RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-8 +// CHECK-8: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'A' +// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'int' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'X' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'int' +// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: blob data = 'Y' +// CHECK-8-NEXT: +// CHECK-8-NEXT: diff --git a/test/clang-doc/md-record.cpp b/test/clang-doc/md-record.cpp index 1c5a1adf3..0d3e0ccc2 100644 --- a/test/clang-doc/md-record.cpp +++ b/test/clang-doc/md-record.cpp @@ -39,6 +39,8 @@ class X { class Y {}; }; +class G; + // RUN: clang-doc --format=md --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs @@ -95,3 +97,6 @@ class X { // CHECK-6: ## Members // CHECK-6: int X // CHECK-6: int Y + +// RUN: cat %t/docs/./G.md | FileCheck %s --check-prefix CHECK-7 +// CHECK-7: # class G diff --git a/test/clang-doc/public-record.cpp b/test/clang-doc/public-record.cpp index d3302193f..2f3aca9fd 100644 --- a/test/clang-doc/public-record.cpp +++ b/test/clang-doc/public-record.cpp @@ -39,6 +39,8 @@ class X { class Y {}; }; +class G; + // RUN: clang-doc --format=yaml --doxygen --public --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs @@ -90,119 +92,129 @@ class X { // CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-2-NEXT: ... -// RUN: cat %t/docs/./E.yaml | FileCheck %s --check-prefix CHECK-3 +// RUN: cat %t/docs/./G.yaml | FileCheck %s --check-prefix CHECK-3 // CHECK-3: --- // CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 25 -// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: Name: 'G' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 42 +// CHECK-3-NEXT: Filename: 'test' // CHECK-3-NEXT: TagType: Class -// CHECK-3-NEXT: ChildFunctions: -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 27 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: '~E' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 28 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'ProtectedMethod' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 34 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: Location: -// CHECK-3-NEXT: - LineNumber: 31 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' // CHECK-3-NEXT: ... -// RUN: cat %t/docs/./D.yaml | FileCheck %s --check-prefix CHECK-4 +// RUN: cat %t/docs/./E.yaml | FileCheck %s --check-prefix CHECK-4 // CHECK-4: --- // CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: Name: 'D' +// CHECK-4-NEXT: Name: 'E' // CHECK-4-NEXT: DefLocation: -// CHECK-4-NEXT: LineNumber: 23 +// CHECK-4-NEXT: LineNumber: 25 // CHECK-4-NEXT: Filename: 'test' // CHECK-4-NEXT: TagType: Class +// CHECK-4-NEXT: ChildFunctions: +// CHECK-4-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Record +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 27 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: IsMethod: true +// CHECK-4-NEXT: Parent: +// CHECK-4-NEXT: Type: Record +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: ReturnType: +// CHECK-4-NEXT: Type: +// CHECK-4-NEXT: Name: 'void' +// CHECK-4-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: '~E' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Record +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 28 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: IsMethod: true +// CHECK-4-NEXT: Parent: +// CHECK-4-NEXT: Type: Record +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: ReturnType: +// CHECK-4-NEXT: Type: +// CHECK-4-NEXT: Name: 'void' +// CHECK-4-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: 'ProtectedMethod' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Record +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 34 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: Location: +// CHECK-4-NEXT: - LineNumber: 31 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: IsMethod: true +// CHECK-4-NEXT: Parent: +// CHECK-4-NEXT: Type: Record +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: ReturnType: +// CHECK-4-NEXT: Type: +// CHECK-4-NEXT: Name: 'void' // CHECK-4-NEXT: ... -// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-5 +// RUN: cat %t/docs/./D.yaml | FileCheck %s --check-prefix CHECK-5 // CHECK-5: --- // CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Name: 'X' +// CHECK-5-NEXT: Name: 'D' // CHECK-5-NEXT: DefLocation: -// CHECK-5-NEXT: LineNumber: 38 +// CHECK-5-NEXT: LineNumber: 23 // CHECK-5-NEXT: Filename: 'test' // CHECK-5-NEXT: TagType: Class // CHECK-5-NEXT: ... -// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-6 +// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-6 // CHECK-6: --- // CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: ChildFunctions: -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'H' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 11 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: ReturnType: -// CHECK-6-NEXT: Type: -// CHECK-6-NEXT: Name: 'void' -// CHECK-6-NEXT: ChildEnums: -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'B' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 17 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: Members: -// CHECK-6-NEXT: - 'X' -// CHECK-6-NEXT: - 'Y' -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'Bc' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 19 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: Scoped: true -// CHECK-6-NEXT: Members: -// CHECK-6-NEXT: - 'A' -// CHECK-6-NEXT: - 'B' +// CHECK-6-NEXT: Name: 'X' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 38 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: TagType: Class // CHECK-6-NEXT: ... + +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-7 +// CHECK-7: --- +// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: ChildFunctions: +// CHECK-7-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: Name: 'H' +// CHECK-7-NEXT: DefLocation: +// CHECK-7-NEXT: LineNumber: 11 +// CHECK-7-NEXT: Filename: 'test' +// CHECK-7-NEXT: ReturnType: +// CHECK-7-NEXT: Type: +// CHECK-7-NEXT: Name: 'void' +// CHECK-7-NEXT: ChildEnums: +// CHECK-7-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: Name: 'B' +// CHECK-7-NEXT: DefLocation: +// CHECK-7-NEXT: LineNumber: 17 +// CHECK-7-NEXT: Filename: 'test' +// CHECK-7-NEXT: Members: +// CHECK-7-NEXT: - 'X' +// CHECK-7-NEXT: - 'Y' +// CHECK-7-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: Name: 'Bc' +// CHECK-7-NEXT: DefLocation: +// CHECK-7-NEXT: LineNumber: 19 +// CHECK-7-NEXT: Filename: 'test' +// CHECK-7-NEXT: Scoped: true +// CHECK-7-NEXT: Members: +// CHECK-7-NEXT: - 'A' +// CHECK-7-NEXT: - 'B' +// CHECK-7-NEXT: ... diff --git a/test/clang-doc/test_cases/record.cpp b/test/clang-doc/test_cases/record.cpp index 03419a9e1..d71232933 100644 --- a/test/clang-doc/test_cases/record.cpp +++ b/test/clang-doc/test_cases/record.cpp @@ -38,3 +38,5 @@ class F : virtual private D, public E {}; class X { class Y {}; }; + +class G; diff --git a/test/clang-doc/yaml-record.cpp b/test/clang-doc/yaml-record.cpp index 2006baa12..0b4e288a9 100644 --- a/test/clang-doc/yaml-record.cpp +++ b/test/clang-doc/yaml-record.cpp @@ -39,6 +39,8 @@ class X { class Y {}; }; +class G; + // RUN: clang-doc --format=yaml --doxygen --extra-arg=-fmodules-ts -p %t %t/test.cpp -output=%t/docs @@ -90,133 +92,143 @@ class X { // CHECK-2-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' // CHECK-2-NEXT: ... -// RUN: cat %t/docs/./E.yaml | FileCheck %s --check-prefix CHECK-3 +// RUN: cat %t/docs/./G.yaml | FileCheck %s --check-prefix CHECK-3 // CHECK-3: --- // CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 25 -// CHECK-3-NEXT: Filename: 'test' +// CHECK-3-NEXT: Name: 'G' +// CHECK-3-NEXT: Location: +// CHECK-3-NEXT: - LineNumber: 42 +// CHECK-3-NEXT: Filename: 'test' // CHECK-3-NEXT: TagType: Class -// CHECK-3-NEXT: ChildFunctions: -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 27 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: '~E' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 28 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' -// CHECK-3-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: Name: 'ProtectedMethod' -// CHECK-3-NEXT: Namespace: -// CHECK-3-NEXT: - Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: DefLocation: -// CHECK-3-NEXT: LineNumber: 34 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: Location: -// CHECK-3-NEXT: - LineNumber: 31 -// CHECK-3-NEXT: Filename: 'test' -// CHECK-3-NEXT: IsMethod: true -// CHECK-3-NEXT: Parent: -// CHECK-3-NEXT: Type: Record -// CHECK-3-NEXT: Name: 'E' -// CHECK-3-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-3-NEXT: ReturnType: -// CHECK-3-NEXT: Type: -// CHECK-3-NEXT: Name: 'void' // CHECK-3-NEXT: ... -// RUN: cat %t/docs/./D.yaml | FileCheck %s --check-prefix CHECK-4 +// RUN: cat %t/docs/./E.yaml | FileCheck %s --check-prefix CHECK-4 // CHECK-4: --- // CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-4-NEXT: Name: 'D' +// CHECK-4-NEXT: Name: 'E' // CHECK-4-NEXT: DefLocation: -// CHECK-4-NEXT: LineNumber: 23 +// CHECK-4-NEXT: LineNumber: 25 // CHECK-4-NEXT: Filename: 'test' // CHECK-4-NEXT: TagType: Class +// CHECK-4-NEXT: ChildFunctions: +// CHECK-4-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Record +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 27 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: IsMethod: true +// CHECK-4-NEXT: Parent: +// CHECK-4-NEXT: Type: Record +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: ReturnType: +// CHECK-4-NEXT: Type: +// CHECK-4-NEXT: Name: 'void' +// CHECK-4-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: '~E' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Record +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 28 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: IsMethod: true +// CHECK-4-NEXT: Parent: +// CHECK-4-NEXT: Type: Record +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: ReturnType: +// CHECK-4-NEXT: Type: +// CHECK-4-NEXT: Name: 'void' +// CHECK-4-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: Name: 'ProtectedMethod' +// CHECK-4-NEXT: Namespace: +// CHECK-4-NEXT: - Type: Record +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: DefLocation: +// CHECK-4-NEXT: LineNumber: 34 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: Location: +// CHECK-4-NEXT: - LineNumber: 31 +// CHECK-4-NEXT: Filename: 'test' +// CHECK-4-NEXT: IsMethod: true +// CHECK-4-NEXT: Parent: +// CHECK-4-NEXT: Type: Record +// CHECK-4-NEXT: Name: 'E' +// CHECK-4-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-4-NEXT: ReturnType: +// CHECK-4-NEXT: Type: +// CHECK-4-NEXT: Name: 'void' // CHECK-4-NEXT: ... -// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-5 +// RUN: cat %t/docs/./D.yaml | FileCheck %s --check-prefix CHECK-5 // CHECK-5: --- // CHECK-5-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-5-NEXT: Name: 'X' +// CHECK-5-NEXT: Name: 'D' // CHECK-5-NEXT: DefLocation: -// CHECK-5-NEXT: LineNumber: 38 +// CHECK-5-NEXT: LineNumber: 23 // CHECK-5-NEXT: Filename: 'test' // CHECK-5-NEXT: TagType: Class // CHECK-5-NEXT: ... -// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-6 +// RUN: cat %t/docs/./X.yaml | FileCheck %s --check-prefix CHECK-6 // CHECK-6: --- // CHECK-6-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: ChildFunctions: -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'H' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 11 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: ReturnType: -// CHECK-6-NEXT: Type: -// CHECK-6-NEXT: Name: 'void' -// CHECK-6-NEXT: ChildEnums: -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'B' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 17 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: Members: -// CHECK-6-NEXT: - 'X' -// CHECK-6-NEXT: - 'Y' -// CHECK-6-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-6-NEXT: Name: 'Bc' -// CHECK-6-NEXT: DefLocation: -// CHECK-6-NEXT: LineNumber: 19 -// CHECK-6-NEXT: Filename: 'test' -// CHECK-6-NEXT: Scoped: true -// CHECK-6-NEXT: Members: -// CHECK-6-NEXT: - 'A' -// CHECK-6-NEXT: - 'B' +// CHECK-6-NEXT: Name: 'X' +// CHECK-6-NEXT: DefLocation: +// CHECK-6-NEXT: LineNumber: 38 +// CHECK-6-NEXT: Filename: 'test' +// CHECK-6-NEXT: TagType: Class // CHECK-6-NEXT: ... -// RUN: cat %t/docs/X/Y.yaml | FileCheck %s --check-prefix CHECK-7 +// RUN: cat %t/docs/./GlobalNamespace.yaml | FileCheck %s --check-prefix CHECK-7 // CHECK-7: --- // CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: Name: 'Y' -// CHECK-7-NEXT: Namespace: -// CHECK-7-NEXT: - Type: Record -// CHECK-7-NEXT: Name: 'X' -// CHECK-7-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' -// CHECK-7-NEXT: DefLocation: -// CHECK-7-NEXT: LineNumber: 39 -// CHECK-7-NEXT: Filename: 'test' -// CHECK-7-NEXT: TagType: Class +// CHECK-7-NEXT: ChildFunctions: +// CHECK-7-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: Name: 'H' +// CHECK-7-NEXT: DefLocation: +// CHECK-7-NEXT: LineNumber: 11 +// CHECK-7-NEXT: Filename: 'test' +// CHECK-7-NEXT: ReturnType: +// CHECK-7-NEXT: Type: +// CHECK-7-NEXT: Name: 'void' +// CHECK-7-NEXT: ChildEnums: +// CHECK-7-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: Name: 'B' +// CHECK-7-NEXT: DefLocation: +// CHECK-7-NEXT: LineNumber: 17 +// CHECK-7-NEXT: Filename: 'test' +// CHECK-7-NEXT: Members: +// CHECK-7-NEXT: - 'X' +// CHECK-7-NEXT: - 'Y' +// CHECK-7-NEXT: - USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-7-NEXT: Name: 'Bc' +// CHECK-7-NEXT: DefLocation: +// CHECK-7-NEXT: LineNumber: 19 +// CHECK-7-NEXT: Filename: 'test' +// CHECK-7-NEXT: Scoped: true +// CHECK-7-NEXT: Members: +// CHECK-7-NEXT: - 'A' +// CHECK-7-NEXT: - 'B' // CHECK-7-NEXT: ... + +// RUN: cat %t/docs/X/Y.yaml | FileCheck %s --check-prefix CHECK-8 +// CHECK-8: --- +// CHECK-8-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-8-NEXT: Name: 'Y' +// CHECK-8-NEXT: Namespace: +// CHECK-8-NEXT: - Type: Record +// CHECK-8-NEXT: Name: 'X' +// CHECK-8-NEXT: USR: '{{[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]}}' +// CHECK-8-NEXT: DefLocation: +// CHECK-8-NEXT: LineNumber: 39 +// CHECK-8-NEXT: Filename: 'test' +// CHECK-8-NEXT: TagType: Class +// CHECK-8-NEXT: ... From 3c749a4b6ee108f6df1bcf6c04e7d4f99775c884 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 4 Oct 2018 08:30:03 +0000 Subject: [PATCH 296/686] [clangd] clangd-indexer: Drop support for MR-via-YAML Summary: It's slow, and the open-source reduce implementation doesn't scale properly. While here, tidy up some dead headers and comments. Reviewers: kadircet Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D52517 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343759 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Serialization.h | 9 +- clangd/index/YAMLSerialization.cpp | 8 -- clangd/indexer/IndexerMain.cpp | 151 ++++++----------------------- 3 files changed, 32 insertions(+), 136 deletions(-) diff --git a/clangd/index/Serialization.h b/clangd/index/Serialization.h index 4240bdb72..3cb86dbd2 100644 --- a/clangd/index/Serialization.h +++ b/clangd/index/Serialization.h @@ -27,11 +27,6 @@ #include "Index.h" #include "llvm/Support/Error.h" -namespace llvm { -namespace yaml { -class Input; -} -} // namespace llvm namespace clang { namespace clangd { @@ -61,10 +56,8 @@ struct IndexFileOut { // Serializes an index file. llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IndexFileOut &O); +// Convert a single symbol to YAML, a nice debug representation. std::string toYAML(const Symbol &); -// Returned symbol is backed by the YAML input. -// FIXME: this is only needed for IndexerMain, find a better solution. -llvm::Expected symbolFromYAML(llvm::yaml::Input &); // Build an in-memory static index from an index file. // The size should be relatively small, so data can be managed in memory. diff --git a/clangd/index/YAMLSerialization.cpp b/clangd/index/YAMLSerialization.cpp index cf3e2a69e..73df61878 100644 --- a/clangd/index/YAMLSerialization.cpp +++ b/clangd/index/YAMLSerialization.cpp @@ -218,13 +218,5 @@ std::string toYAML(const Symbol &S) { return Buf; } -Expected symbolFromYAML(llvm::yaml::Input &Yin) { - Symbol S; - Yin >> S; - if (Yin.error()) - return llvm::errorCodeToError(Yin.error()); - return S; -} - } // namespace clangd } // namespace clang diff --git a/clangd/indexer/IndexerMain.cpp b/clangd/indexer/IndexerMain.cpp index 913669382..8db755959 100644 --- a/clangd/indexer/IndexerMain.cpp +++ b/clangd/indexer/IndexerMain.cpp @@ -12,26 +12,17 @@ // //===----------------------------------------------------------------------===// -#include "RIFF.h" -#include "index/CanonicalIncludes.h" #include "index/Index.h" #include "index/IndexAction.h" #include "index/Merge.h" #include "index/Serialization.h" #include "index/SymbolCollector.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/FrontendActions.h" -#include "clang/Index/IndexDataConsumer.h" -#include "clang/Index/IndexingAction.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Execution.h" #include "clang/Tooling/Tooling.h" #include "llvm/Support/CommandLine.h" -#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Signals.h" -#include "llvm/Support/ThreadPool.h" -#include "llvm/Support/YAMLTraits.h" using namespace llvm; using namespace clang::tooling; @@ -50,16 +41,6 @@ static llvm::cl::opt AssumedHeaderDir( "not given, such headers will have relative paths."), llvm::cl::init("")); -static llvm::cl::opt MergeOnTheFly( - "merge-on-the-fly", - llvm::cl::desc( - "Merges symbols for each processed translation unit as soon " - "they become available. This results in a smaller memory " - "usage and an almost instant reduce stage. Optimal for running as a " - "standalone tool, but cannot be used with multi-process executors like " - "MapReduce."), - llvm::cl::init(true), llvm::cl::Hidden); - static llvm::cl::opt Format("format", llvm::cl::desc("Format of the index to be written"), llvm::cl::values(clEnumValN(IndexFileFormat::YAML, "yaml", @@ -68,89 +49,36 @@ static llvm::cl::opt "binary RIFF format")), llvm::cl::init(IndexFileFormat::YAML)); -/// Responsible for aggregating symbols from each processed file and producing -/// the final results. All methods in this class must be thread-safe, -/// 'consumeSymbols' may be called from multiple threads. -class SymbolsConsumer { -public: - virtual ~SymbolsConsumer() = default; - - /// Consume a SymbolSlab build for a file. - virtual void consumeSymbols(SymbolSlab Symbols) = 0; - /// Produce a resulting symbol slab, by combining occurrences of the same - /// symbols across translation units. - virtual SymbolSlab mergeResults() = 0; -}; - -class SymbolIndexActionFactory : public tooling::FrontendActionFactory { +class IndexActionFactory : public tooling::FrontendActionFactory { public: - SymbolIndexActionFactory(SymbolsConsumer &Consumer) : Consumer(Consumer) {} + IndexActionFactory(IndexFileIn &Result) : Result(Result) {} clang::FrontendAction *create() override { - auto CollectorOpts = SymbolCollector::Options(); - CollectorOpts.FallbackDir = AssumedHeaderDir; + SymbolCollector::Options Opts; + Opts.FallbackDir = AssumedHeaderDir; return createStaticIndexingAction( - CollectorOpts, - [&](SymbolSlab S) { Consumer.consumeSymbols(std::move(S)); }) + Opts, + [&](SymbolSlab S) { + // Merge as we go. + std::lock_guard Lock(SymbolsMu); + for (const auto &Sym : S) { + if (const auto *Existing = Symbols.find(Sym.ID)) + Symbols.insert(mergeSymbol(*Existing, Sym)); + else + Symbols.insert(Sym); + } + }) .release(); } - SymbolsConsumer &Consumer; -}; - -/// Stashes per-file results inside ExecutionContext, merges all of them at the -/// end. Useful for running on MapReduce infrastructure to avoid keeping symbols -/// from multiple files in memory. -class ToolExecutorConsumer : public SymbolsConsumer { -public: - ToolExecutorConsumer(ToolExecutor &Executor) : Executor(Executor) {} - - void consumeSymbols(SymbolSlab Symbols) override { - for (const auto &Sym : Symbols) - Executor.getExecutionContext()->reportResult(Sym.ID.str(), toYAML(Sym)); - } - - SymbolSlab mergeResults() override { - SymbolSlab::Builder UniqueSymbols; - Executor.getToolResults()->forEachResult( - [&](llvm::StringRef Key, llvm::StringRef Value) { - llvm::yaml::Input Yin(Value); - auto Sym = cantFail(clang::clangd::symbolFromYAML(Yin)); - auto ID = cantFail(clang::clangd::SymbolID::fromStr(Key)); - if (const auto *Existing = UniqueSymbols.find(ID)) - UniqueSymbols.insert(mergeSymbol(*Existing, Sym)); - else - UniqueSymbols.insert(Sym); - }); - return std::move(UniqueSymbols).build(); - } - -private: - ToolExecutor &Executor; -}; - -/// Merges symbols for each translation unit as soon as the file is processed. -/// Optimal choice for standalone tools. -class OnTheFlyConsumer : public SymbolsConsumer { -public: - void consumeSymbols(SymbolSlab Symbols) override { - std::lock_guard Lock(Mut); - for (auto &&Sym : Symbols) { - if (const auto *Existing = Result.find(Sym.ID)) - Result.insert(mergeSymbol(*Existing, Sym)); - else - Result.insert(Sym); - } - } - - SymbolSlab mergeResults() override { - std::lock_guard Lock(Mut); - return std::move(Result).build(); - } + // Awkward: we write the result in the destructor, because the executor + // takes ownership so it's the easiest way to get our data back out. + ~IndexActionFactory() { Result.Symbols = std::move(Symbols).build(); } private: - std::mutex Mut; - SymbolSlab::Builder Result; + IndexFileIn &Result; + std::mutex SymbolsMu; + SymbolSlab::Builder Symbols; }; } // namespace @@ -161,12 +89,10 @@ int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); const char *Overview = R"( - This is an **experimental** tool to extract symbols from a whole project - for clangd (global code completion). It will be changed and deprecated - eventually. Don't use it in production code! + Creates an index of symbol information etc in a whole project. + This is **experimental** and not production-ready! - Example usage for building index for the whole project using CMake compile - commands: + Example usage for a project using CMake compile commands: $ clangd-indexer --executor=all-TUs compile_commands.json > index.yaml @@ -174,7 +100,7 @@ int main(int argc, const char **argv) { $ clangd-indexer File1.cpp File2.cpp ... FileN.cpp > index.yaml - Note: only symbols from header files will be collected. + Note: only symbols from header files will be indexed. )"; auto Executor = clang::tooling::createExecutorFromCommandLineArgs( @@ -191,31 +117,16 @@ int main(int argc, const char **argv) { return 1; } - if (clang::clangd::MergeOnTheFly && !Executor->get()->isSingleProcess()) { - llvm::errs() - << "Found multi-process executor, forcing the use of intermediate YAML " - "serialization instead of the on-the-fly merge.\n"; - clang::clangd::MergeOnTheFly = false; - } - - std::unique_ptr Consumer; - if (clang::clangd::MergeOnTheFly) - Consumer = llvm::make_unique(); - else - Consumer = - llvm::make_unique(**Executor); - - // Map phase: emit symbols found in each translation unit. + // Collect symbols found in each translation unit, merging as we go. + clang::clangd::IndexFileIn Data; auto Err = Executor->get()->execute( - llvm::make_unique(*Consumer)); + llvm::make_unique(Data)); if (Err) { llvm::errs() << llvm::toString(std::move(Err)) << "\n"; } - // Reduce phase: combine symbols with the same IDs. - auto UniqueSymbols = Consumer->mergeResults(); - // Output phase: emit result symbols. - clang::clangd::IndexFileOut Out; - Out.Symbols = &UniqueSymbols; + + // Emit collected data. + clang::clangd::IndexFileOut Out(Data); Out.Format = clang::clangd::Format; llvm::outs() << Out; return 0; From 8b1d957ff5cdfe892ab42864b010073f71f3ba7e Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 4 Oct 2018 09:16:12 +0000 Subject: [PATCH 297/686] [clangd] Support refs() in dex. Largely cloned from MemIndex. Reviewers: hokein Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52726 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343760 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Serialization.cpp | 5 +-- clangd/index/dex/Dex.cpp | 15 ++++++++- clangd/index/dex/Dex.h | 30 ++++++++--------- clangd/indexer/IndexerMain.cpp | 2 +- unittests/clangd/DexTests.cpp | 61 +++++++++++++++++++++++++--------- 5 files changed, 78 insertions(+), 35 deletions(-) diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index 5e16137f8..2ebd2041f 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -435,8 +435,9 @@ std::unique_ptr loadIndex(llvm::StringRef SymbolFilename, } trace::Span Tracer("BuildIndex"); - auto Index = UseDex ? dex::Dex::build(std::move(Symbols), URISchemes) - : MemIndex::build(std::move(Symbols), std::move(Refs)); + auto Index = + UseDex ? dex::Dex::build(std::move(Symbols), std::move(Refs), URISchemes) + : MemIndex::build(std::move(Symbols), std::move(Refs)); vlog("Loaded {0} from {1} with estimated memory usage {2}", UseDex ? "Dex" : "MemIndex", SymbolFilename, Index->estimateMemoryUsage()); diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 3b236f891..08f168ddd 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -24,6 +24,15 @@ namespace clang { namespace clangd { namespace dex { +std::unique_ptr +Dex::build(SymbolSlab Symbols, RefSlab Refs, + llvm::ArrayRef URISchemes) { + auto Size = Symbols.bytes() + Refs.bytes(); + auto Data = std::make_pair(std::move(Symbols), std::move(Refs)); + return llvm::make_unique(Data.first, Data.second, std::move(Data), Size, + std::move(URISchemes)); +} + namespace { // Mark symbols which are can be used for code completion. @@ -254,7 +263,10 @@ void Dex::lookup(const LookupRequest &Req, void Dex::refs(const RefsRequest &Req, llvm::function_ref Callback) const { trace::Span Tracer("Dex refs"); - log("refs is not implemented."); + for (const auto &ID : Req.IDs) + for (const auto &Ref : Refs.lookup(ID)) + if (static_cast(Req.Filter & Ref.Kind)) + Callback(Ref); } size_t Dex::estimateMemoryUsage() const { @@ -264,6 +276,7 @@ size_t Dex::estimateMemoryUsage() const { Bytes += InvertedIndex.getMemorySize(); for (const auto &TokenToPostingList : InvertedIndex) Bytes += TokenToPostingList.second.bytes(); + Bytes += Refs.getMemorySize(); return Bytes + BackingDataSize; } diff --git a/clangd/index/dex/Dex.h b/clangd/index/dex/Dex.h index baa215310..d89e6e15b 100644 --- a/clangd/index/dex/Dex.h +++ b/clangd/index/dex/Dex.h @@ -41,9 +41,10 @@ namespace dex { // index on disk and then load it if available. class Dex : public SymbolIndex { public: - // All symbols must outlive this index. - template - Dex(Range &&Symbols, llvm::ArrayRef Schemes) + // All data must outlive this index. + template + Dex(SymbolRange &&Symbols, RefsRange &&Refs, + llvm::ArrayRef Schemes) : Corpus(0), URISchemes(Schemes) { // If Schemes don't contain any items, fall back to SymbolCollector's // default URI schemes. @@ -53,26 +54,24 @@ class Dex : public SymbolIndex { } for (auto &&Sym : Symbols) this->Symbols.push_back(&Sym); + for (auto &&Ref : Refs) + this->Refs.try_emplace(Ref.first, Ref.second); buildIndex(); } - // Symbols are owned by BackingData, Index takes ownership. - template - Dex(Range &&Symbols, Payload &&BackingData, size_t BackingDataSize, - llvm::ArrayRef URISchemes) - : Dex(std::forward(Symbols), URISchemes) { + // Symbols and Refs are owned by BackingData, Index takes ownership. + template + Dex(SymbolRange &&Symbols, RefsRange &&Refs, Payload &&BackingData, + size_t BackingDataSize, llvm::ArrayRef URISchemes) + : Dex(std::forward(Symbols), std::forward(Refs), + URISchemes) { KeepAlive = std::shared_ptr( std::make_shared(std::move(BackingData)), nullptr); this->BackingDataSize = BackingDataSize; } - /// Builds an index from a slab. The index takes ownership of the slab. + /// Builds an index from slabs. The index takes ownership of the slab. static std::unique_ptr - build(SymbolSlab Slab, llvm::ArrayRef URISchemes) { - // Store Slab size before it is moved. - const auto BackingDataSize = Slab.bytes(); - return llvm::make_unique(Slab, std::move(Slab), BackingDataSize, - URISchemes); - } + build(SymbolSlab, RefSlab, llvm::ArrayRef URISchemes); bool fuzzyFind(const FuzzyFindRequest &Req, @@ -102,6 +101,7 @@ class Dex : public SymbolIndex { /// during the fuzzyFind process. llvm::DenseMap InvertedIndex; dex::Corpus Corpus; + llvm::DenseMap> Refs; std::shared_ptr KeepAlive; // poor man's move-only std::any // Size of memory retained by KeepAlive. size_t BackingDataSize = 0; diff --git a/clangd/indexer/IndexerMain.cpp b/clangd/indexer/IndexerMain.cpp index 8db755959..5a5b3178f 100644 --- a/clangd/indexer/IndexerMain.cpp +++ b/clangd/indexer/IndexerMain.cpp @@ -47,7 +47,7 @@ static llvm::cl::opt "human-readable YAML format"), clEnumValN(IndexFileFormat::RIFF, "binary", "binary RIFF format")), - llvm::cl::init(IndexFileFormat::YAML)); + llvm::cl::init(IndexFileFormat::RIFF)); class IndexActionFactory : public tooling::FrontendActionFactory { public: diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 28ec9c5c8..d1d5b4c7e 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -423,7 +423,8 @@ TEST(DexSearchTokens, SymbolPath) { //===----------------------------------------------------------------------===// TEST(Dex, Lookup) { - auto I = Dex::build(generateSymbols({"ns::abc", "ns::xyz"}), URISchemes); + auto I = Dex::build(generateSymbols({"ns::abc", "ns::xyz"}), RefSlab(), + URISchemes); EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::abc", "ns::xyz")); @@ -436,7 +437,7 @@ TEST(Dex, FuzzyFind) { auto Index = Dex::build(generateSymbols({"ns::ABC", "ns::BCD", "::ABC", "ns::nested::ABC", "other::ABC", "other::A"}), - URISchemes); + RefSlab(), URISchemes); FuzzyFindRequest Req; Req.Query = "ABC"; Req.Scopes = {"ns::"}; @@ -466,13 +467,13 @@ TEST(DexTest, DexDeduplicate) { symbol("2") /* duplicate */}; FuzzyFindRequest Req; Req.Query = "2"; - Dex I(Symbols, URISchemes); + Dex I(Symbols, RefSlab(), URISchemes); EXPECT_FALSE(Req.Limit); EXPECT_THAT(match(I, Req), ElementsAre("2", "2")); } TEST(DexTest, DexLimitedNumMatches) { - auto I = Dex::build(generateNumSymbols(0, 100), URISchemes); + auto I = Dex::build(generateNumSymbols(0, 100), RefSlab(), URISchemes); FuzzyFindRequest Req; Req.Query = "5"; Req.Limit = 3; @@ -486,7 +487,7 @@ TEST(DexTest, DexLimitedNumMatches) { TEST(DexTest, FuzzyMatch) { auto I = Dex::build( generateSymbols({"LaughingOutLoud", "LionPopulation", "LittleOldLady"}), - URISchemes); + RefSlab(), URISchemes); FuzzyFindRequest Req; Req.Query = "lol"; Req.Limit = 2; @@ -495,14 +496,16 @@ TEST(DexTest, FuzzyMatch) { } TEST(DexTest, MatchQualifiedNamesWithoutSpecificScope) { - auto I = Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), URISchemes); + auto I = Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab(), + URISchemes); FuzzyFindRequest Req; Req.Query = "y"; EXPECT_THAT(match(*I, Req), UnorderedElementsAre("a::y1", "b::y2", "y3")); } TEST(DexTest, MatchQualifiedNamesWithGlobalScope) { - auto I = Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), URISchemes); + auto I = Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab(), + URISchemes); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {""}; @@ -510,8 +513,9 @@ TEST(DexTest, MatchQualifiedNamesWithGlobalScope) { } TEST(DexTest, MatchQualifiedNamesWithOneScope) { - auto I = Dex::build( - generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}), URISchemes); + auto I = + Dex::build(generateSymbols({"a::y1", "a::y2", "a::x", "b::y2", "y3"}), + RefSlab(), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; @@ -520,7 +524,7 @@ TEST(DexTest, MatchQualifiedNamesWithOneScope) { TEST(DexTest, MatchQualifiedNamesWithMultipleScopes) { auto I = Dex::build( - generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}), URISchemes); + generateSymbols({"a::y1", "a::y2", "a::x", "b::y3", "y3"}), RefSlab(), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::", "b::"}; @@ -528,7 +532,7 @@ TEST(DexTest, MatchQualifiedNamesWithMultipleScopes) { } TEST(DexTest, NoMatchNestedScopes) { - auto I = Dex::build(generateSymbols({"a::y1", "a::b::y2"}), URISchemes); + auto I = Dex::build(generateSymbols({"a::y1", "a::b::y2"}), RefSlab(), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; @@ -537,7 +541,7 @@ TEST(DexTest, NoMatchNestedScopes) { TEST(DexTest, WildcardScope) { auto I = - Dex::build(generateSymbols({"a::y1", "a::b::y2", "c::y3"}), URISchemes); + Dex::build(generateSymbols({"a::y1", "a::b::y2", "c::y3"}), RefSlab(), URISchemes); FuzzyFindRequest Req; Req.Query = "y"; Req.Scopes = {"a::"}; @@ -547,7 +551,7 @@ TEST(DexTest, WildcardScope) { } TEST(DexTest, IgnoreCases) { - auto I = Dex::build(generateSymbols({"ns::ABC", "ns::abc"}), URISchemes); + auto I = Dex::build(generateSymbols({"ns::ABC", "ns::abc"}), RefSlab(), URISchemes); FuzzyFindRequest Req; Req.Query = "AB"; Req.Scopes = {"ns::"}; @@ -555,7 +559,7 @@ TEST(DexTest, IgnoreCases) { } TEST(DexTest, Lookup) { - auto I = Dex::build(generateSymbols({"ns::abc", "ns::xyz"}), URISchemes); + auto I = Dex::build(generateSymbols({"ns::abc", "ns::xyz"}), RefSlab(), URISchemes); EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); EXPECT_THAT(lookup(*I, {SymbolID("ns::abc"), SymbolID("ns::xyz")}), UnorderedElementsAre("ns::abc", "ns::xyz")); @@ -570,7 +574,7 @@ TEST(DexTest, SymbolIndexOptionsFilter) { CodeCompletionSymbol.Flags = Symbol::SymbolFlag::IndexedForCodeCompletion; NonCodeCompletionSymbol.Flags = Symbol::SymbolFlag::None; std::vector Symbols{CodeCompletionSymbol, NonCodeCompletionSymbol}; - Dex I(Symbols, URISchemes); + Dex I(Symbols, RefSlab(), URISchemes); FuzzyFindRequest Req; Req.RestrictForCodeCompletion = false; EXPECT_THAT(match(I, Req), ElementsAre("Completion", "NoCompletion")); @@ -585,7 +589,7 @@ TEST(DexTest, ProximityPathsBoosting) { CloseSymbol.CanonicalDeclaration.FileURI = "unittest:///a/b/c/d/e/f/file.h"; std::vector Symbols{CloseSymbol, RootSymbol}; - Dex I(Symbols, URISchemes); + Dex I(Symbols, RefSlab(), URISchemes); FuzzyFindRequest Req; Req.Query = "abc"; @@ -603,6 +607,31 @@ TEST(DexTest, ProximityPathsBoosting) { EXPECT_THAT(match(I, Req), ElementsAre("root::abc")); } +TEST(DexTests, Refs) { + DenseMap> Refs; + auto AddRef = [&](const Symbol& Sym, StringRef Filename, RefKind Kind) { + auto& SymbolRefs = Refs[Sym.ID]; + SymbolRefs.emplace_back(); + SymbolRefs.back().Kind = Kind; + SymbolRefs.back().Location.FileURI = Filename; + }; + auto Foo = symbol("foo"); + auto Bar = symbol("bar"); + AddRef(Foo, "foo.h", RefKind::Declaration); + AddRef(Foo, "reffoo.h", RefKind::Reference); + AddRef(Bar, "bar.h", RefKind::Declaration); + + std::vector Files; + RefsRequest Req; + Req.IDs.insert(Foo.ID); + Req.Filter = RefKind::Declaration | RefKind::Definition; + Dex(std::vector{Foo, Bar}, Refs, {}).refs(Req, [&](const Ref &R) { + Files.push_back(R.Location.FileURI); + }); + + EXPECT_THAT(Files, ElementsAre("foo.h")); +} + } // namespace } // namespace dex } // namespace clangd From 76a57e402028c734236a3a014d42b043e8aaa0ae Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Thu, 4 Oct 2018 09:56:08 +0000 Subject: [PATCH 298/686] [clangd] Use canonical declarations in ReferenceFinder. Summary: handleDeclOccurrencce reports a canonical declartion, so stick to use canonical declarations to determine whether a declaration is in the target set. Also fix a previous ref test which misses a matched label (it fails without this patch). Reviewers: sammccall Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52871 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343763 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/XRefs.cpp | 26 ++++++++++---------- unittests/clangd/XRefsTests.cpp | 42 ++++++++++++++++++++------------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index da98d88f7..69c6aad21 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -361,7 +361,7 @@ namespace { class ReferenceFinder : public index::IndexDataConsumer { public: struct Reference { - const Decl *Target; + const Decl *CanonicalTarget; SourceLocation Loc; index::SymbolRoleSet Role; }; @@ -370,22 +370,23 @@ class ReferenceFinder : public index::IndexDataConsumer { const std::vector &TargetDecls) : AST(AST) { for (const Decl *D : TargetDecls) - Targets.insert(D); + CanonicalTargets.insert(D->getCanonicalDecl()); } std::vector take() && { std::sort(References.begin(), References.end(), [](const Reference &L, const Reference &R) { - return std::tie(L.Loc, L.Target, L.Role) < - std::tie(R.Loc, R.Target, R.Role); + return std::tie(L.Loc, L.CanonicalTarget, L.Role) < + std::tie(R.Loc, R.CanonicalTarget, R.Role); }); // We sometimes see duplicates when parts of the AST get traversed twice. - References.erase(std::unique(References.begin(), References.end(), - [](const Reference &L, const Reference &R) { - return std::tie(L.Target, L.Loc, L.Role) == - std::tie(R.Target, R.Loc, R.Role); - }), - References.end()); + References.erase( + std::unique(References.begin(), References.end(), + [](const Reference &L, const Reference &R) { + return std::tie(L.CanonicalTarget, L.Loc, L.Role) == + std::tie(R.CanonicalTarget, R.Loc, R.Role); + }), + References.end()); return std::move(References); } @@ -394,15 +395,16 @@ class ReferenceFinder : public index::IndexDataConsumer { ArrayRef Relations, SourceLocation Loc, index::IndexDataConsumer::ASTNodeInfo ASTNode) override { + assert(D->isCanonicalDecl() && "expect D to be a canonical declaration"); const SourceManager &SM = AST.getSourceManager(); Loc = SM.getFileLoc(Loc); - if (SM.isWrittenInMainFile(Loc) && Targets.count(D)) + if (SM.isWrittenInMainFile(Loc) && CanonicalTargets.count(D)) References.push_back({D, Loc, Roles}); return true; } private: - llvm::SmallSet Targets; + llvm::SmallSet CanonicalTargets; std::vector References; const ASTContext &AST; }; diff --git a/unittests/clangd/XRefsTests.cpp b/unittests/clangd/XRefsTests.cpp index be4acf097..f6fad9112 100644 --- a/unittests/clangd/XRefsTests.cpp +++ b/unittests/clangd/XRefsTests.cpp @@ -1113,37 +1113,45 @@ TEST(FindReferences, WithinAST) { const char *Tests[] = { R"cpp(// Local variable int main() { - int $foo[[foo]]; - $foo[[^foo]] = 2; - int test1 = $foo[[foo]]; + int [[foo]]; + [[^foo]] = 2; + int test1 = [[foo]]; } )cpp", R"cpp(// Struct namespace ns1 { - struct $foo[[Foo]] {}; + struct [[Foo]] {}; } // namespace ns1 int main() { - ns1::$foo[[Fo^o]]* Params; + ns1::[[Fo^o]]* Params; + } + )cpp", + + R"cpp(// Forward declaration + class [[Foo]]; + class [[Foo]] {} + int main() { + [[Fo^o]] foo; } )cpp", R"cpp(// Function - int $foo[[foo]](int) {} + int [[foo]](int) {} int main() { - auto *X = &$foo[[^foo]]; - $foo[[foo]](42) + auto *X = &[[^foo]]; + [[foo]](42) } )cpp", R"cpp(// Field struct Foo { - int $foo[[foo]]; - Foo() : $foo[[foo]](0) {} + int [[foo]]; + Foo() : [[foo]](0) {} }; int main() { Foo f; - f.$foo[[f^oo]] = 1; + f.[[f^oo]] = 1; } )cpp", @@ -1152,29 +1160,29 @@ TEST(FindReferences, WithinAST) { int Foo::[[foo]]() {} int main() { Foo f; - f.^foo(); + f.[[^foo]](); } )cpp", R"cpp(// Typedef - typedef int $foo[[Foo]]; + typedef int [[Foo]]; int main() { - $foo[[^Foo]] bar; + [[^Foo]] bar; } )cpp", R"cpp(// Namespace - namespace $foo[[ns]] { + namespace [[ns]] { struct Foo {}; } // namespace ns - int main() { $foo[[^ns]]::Foo foo; } + int main() { [[^ns]]::Foo foo; } )cpp", }; for (const char *Test : Tests) { Annotations T(Test); auto AST = TestTU::withCode(T.code()).build(); std::vector> ExpectedLocations; - for (const auto &R : T.ranges("foo")) + for (const auto &R : T.ranges()) ExpectedLocations.push_back(RangeIs(R)); EXPECT_THAT(findReferences(AST, T.point()), ElementsAreArray(ExpectedLocations)) From 301ffe30956968a9ce679fe0f2774967b4b591ea Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 4 Oct 2018 10:10:35 +0000 Subject: [PATCH 299/686] [clangd] Revert accidental flag change git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343764 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/indexer/IndexerMain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/indexer/IndexerMain.cpp b/clangd/indexer/IndexerMain.cpp index 5a5b3178f..8db755959 100644 --- a/clangd/indexer/IndexerMain.cpp +++ b/clangd/indexer/IndexerMain.cpp @@ -47,7 +47,7 @@ static llvm::cl::opt "human-readable YAML format"), clEnumValN(IndexFileFormat::RIFF, "binary", "binary RIFF format")), - llvm::cl::init(IndexFileFormat::RIFF)); + llvm::cl::init(IndexFileFormat::YAML)); class IndexActionFactory : public tooling::FrontendActionFactory { public: From d0e7354d701bd62517d2ee6cc4dc965b22f08865 Mon Sep 17 00:00:00 2001 From: Martin Bohme Date: Thu, 4 Oct 2018 11:36:39 +0000 Subject: [PATCH 300/686] [clang-tidy] Sequence statements with multiple parents correctly (PR39149) Summary: Before this fix, the bugprone-use-after-move check could incorrectly conclude that a use and move in a function template were not sequenced. For details, see https://bugs.llvm.org/show_bug.cgi?id=39149 Reviewers: alexfh, hokein, aaron.ballman, JonasToth Reviewed By: aaron.ballman Subscribers: xazax.hun, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D52782 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343768 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/bugprone/UseAfterMoveCheck.cpp | 5 +++-- clang-tidy/utils/ExprSequence.cpp | 10 ++++++++-- clang-tidy/utils/ExprSequence.h | 5 +++-- test/clang-tidy/bugprone-use-after-move.cpp | 12 ++++++++++++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tidy/bugprone/UseAfterMoveCheck.cpp index ea6454920..724598666 100644 --- a/clang-tidy/bugprone/UseAfterMoveCheck.cpp +++ b/clang-tidy/bugprone/UseAfterMoveCheck.cpp @@ -102,8 +102,9 @@ bool UseAfterMoveFinder::find(Stmt *FunctionBody, const Expr *MovingCall, if (!TheCFG) return false; - Sequence.reset(new ExprSequence(TheCFG.get(), Context)); - BlockMap.reset(new StmtToBlockMap(TheCFG.get(), Context)); + Sequence = + llvm::make_unique(TheCFG.get(), FunctionBody, Context); + BlockMap = llvm::make_unique(TheCFG.get(), Context); Visited.clear(); const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall); diff --git a/clang-tidy/utils/ExprSequence.cpp b/clang-tidy/utils/ExprSequence.cpp index 48c3de545..c3602ff8a 100644 --- a/clang-tidy/utils/ExprSequence.cpp +++ b/clang-tidy/utils/ExprSequence.cpp @@ -63,8 +63,9 @@ bool isDescendantOrEqual(const Stmt *Descendant, const Stmt *Ancestor, } } -ExprSequence::ExprSequence(const CFG *TheCFG, ASTContext *TheContext) - : Context(TheContext) { +ExprSequence::ExprSequence(const CFG *TheCFG, const Stmt *Root, + ASTContext *TheContext) + : Context(TheContext), Root(Root) { for (const auto &SyntheticStmt : TheCFG->synthetic_stmts()) { SyntheticStmtSourceMap[SyntheticStmt.first] = SyntheticStmt.second; } @@ -99,6 +100,11 @@ bool ExprSequence::potentiallyAfter(const Stmt *After, const Stmt *ExprSequence::getSequenceSuccessor(const Stmt *S) const { for (const Stmt *Parent : getParentStmts(S, Context)) { + // If a statement has multiple parents, make sure we're using the parent + // that lies within the sub-tree under Root. + if (!isDescendantOrEqual(Parent, Root, Context)) + continue; + if (const auto *BO = dyn_cast(Parent)) { // Comma operator: Right-hand side is sequenced after the left-hand side. if (BO->getLHS() == S && BO->getOpcode() == BO_Comma) diff --git a/clang-tidy/utils/ExprSequence.h b/clang-tidy/utils/ExprSequence.h index 2b355d9a9..0868a8997 100644 --- a/clang-tidy/utils/ExprSequence.h +++ b/clang-tidy/utils/ExprSequence.h @@ -69,8 +69,8 @@ namespace utils { class ExprSequence { public: /// Initializes this `ExprSequence` with sequence information for the given - /// `CFG`. - ExprSequence(const CFG *TheCFG, ASTContext *TheContext); + /// `CFG`. `Root` is the root statement the CFG was built from. + ExprSequence(const CFG *TheCFG, const Stmt *Root, ASTContext *TheContext); /// Returns whether \p Before is sequenced before \p After. bool inSequence(const Stmt *Before, const Stmt *After) const; @@ -94,6 +94,7 @@ class ExprSequence { const Stmt *resolveSyntheticStmt(const Stmt *S) const; ASTContext *Context; + const Stmt *Root; llvm::DenseMap SyntheticStmtSourceMap; }; diff --git a/test/clang-tidy/bugprone-use-after-move.cpp b/test/clang-tidy/bugprone-use-after-move.cpp index ae1f021e0..59dcb90cb 100644 --- a/test/clang-tidy/bugprone-use-after-move.cpp +++ b/test/clang-tidy/bugprone-use-after-move.cpp @@ -1195,6 +1195,18 @@ void ifWhileAndSwitchSequenceInitDeclAndCondition() { } } +// Some statements in templates (e.g. null, break and continue statements) may +// be shared between the uninstantiated and instantiated versions of the +// template and therefore have multiple parents. Make sure the sequencing code +// handles this correctly. +template void nullStatementSequencesInTemplate() { + int c = 0; + (void)c; + ; + std::move(c); +} +template void nullStatementSequencesInTemplate(); + namespace PR33020 { class D { ~D(); From 5af121fecda13fa271284dbef73847e002549f61 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 4 Oct 2018 13:12:23 +0000 Subject: [PATCH 301/686] [clangd] Dex: FALSE iterator, peephole optimizations, fix AND bug Summary: The FALSE iterator will be used in a followup patch to fix a logic bug in Dex (currently, tokens that don't have posting lists in the index are simply dropped from the query, changing semantics). It can usually be optimized away, so added the following opmitizations: - simplify booleans inside AND/OR - replace effectively-empty AND/OR with booleans - flatten nested AND/ORs While working on this, found a bug in the AND iterator: its constructor sync() assumes that ReachedEnd is set if applicable, but the constructor never sets it. This crashes if a non-first iterator is nonempty. Reviewers: ilya-biryukov Subscribers: ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52789 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343774 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.cpp | 110 ++++++++++++++++++++++++++++++---- clangd/index/dex/Iterator.h | 11 ++++ unittests/clangd/DexTests.cpp | 58 ++++++++++++++---- 3 files changed, 154 insertions(+), 25 deletions(-) diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index e7794d4aa..5155827f6 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "Iterator.h" +#include "llvm/Support/Casting.h" #include #include #include @@ -26,9 +27,11 @@ namespace { class AndIterator : public Iterator { public: explicit AndIterator(std::vector> AllChildren) - : Children(std::move(AllChildren)) { + : Iterator(Kind::And), Children(std::move(AllChildren)) { assert(!Children.empty() && "AND iterator should have at least one child."); // Establish invariants. + for (const auto &Child : Children) + ReachedEnd |= Child->reachedEnd(); sync(); // When children are sorted by the estimateSize(), sync() calls are more // effective. Each sync() starts with the first child and makes sure all @@ -122,6 +125,7 @@ class AndIterator : public Iterator { /// update the field, rather than traversing the whole subtree in each /// reachedEnd() call. bool ReachedEnd = false; + friend Corpus; // For optimizations. }; /// Implements Iterator over the union of other iterators. @@ -133,7 +137,7 @@ class AndIterator : public Iterator { class OrIterator : public Iterator { public: explicit OrIterator(std::vector> AllChildren) - : Children(std::move(AllChildren)) { + : Iterator(Kind::Or), Children(std::move(AllChildren)) { assert(!Children.empty() && "OR iterator should have at least one child."); } @@ -208,6 +212,7 @@ class OrIterator : public Iterator { // FIXME(kbobyrev): Would storing Children in min-heap be faster? std::vector> Children; + friend Corpus; // For optimizations. }; /// TrueIterator handles PostingLists which contain all items of the index. It @@ -215,7 +220,7 @@ class OrIterator : public Iterator { /// in O(1). class TrueIterator : public Iterator { public: - explicit TrueIterator(DocID Size) : Size(Size) {} + explicit TrueIterator(DocID Size) : Iterator(Kind::True), Size(Size) {} bool reachedEnd() const override { return Index >= Size; } @@ -251,12 +256,35 @@ class TrueIterator : public Iterator { DocID Size; }; +/// FalseIterator yields no results. +class FalseIterator : public Iterator { +public: + FalseIterator() : Iterator(Kind::False) {} + bool reachedEnd() const override { return true; } + void advance() override { assert(false); } + void advanceTo(DocID ID) override { assert(false); } + DocID peek() const override { + assert(false); + return 0; + } + float consume() override { + assert(false); + return 1; + } + size_t estimateSize() const override { return 0; } + +private: + llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override { + return OS << "false"; + } +}; + /// Boost iterator is a wrapper around its child which multiplies scores of /// each retrieved item by a given factor. class BoostIterator : public Iterator { public: BoostIterator(std::unique_ptr Child, float Factor) - : Child(move(Child)), Factor(Factor) {} + : Child(std::move(Child)), Factor(Factor) {} bool reachedEnd() const override { return Child->reachedEnd(); } @@ -286,7 +314,7 @@ class BoostIterator : public Iterator { class LimitIterator : public Iterator { public: LimitIterator(std::unique_ptr Child, size_t Limit) - : Child(move(Child)), Limit(Limit), ItemsLeft(Limit) {} + : Child(std::move(Child)), Limit(Limit), ItemsLeft(Limit) {} bool reachedEnd() const override { return ItemsLeft == 0 || Child->reachedEnd(); @@ -331,30 +359,86 @@ std::vector> consume(Iterator &It) { std::unique_ptr Corpus::intersect(std::vector> Children) const { - // If there is exactly one child, pull it one level up: AND(Child) -> Child. - return Children.size() == 1 ? std::move(Children.front()) - : llvm::make_unique(move(Children)); + std::vector> RealChildren; + for (auto &Child : Children) { + switch (Child->kind()) { + case Iterator::Kind::True: + break; // No effect, drop the iterator. + case Iterator::Kind::False: + return std::move(Child); // Intersection is empty. + case Iterator::Kind::And: { + // Inline nested AND into parent AND. + auto &NewChildren = static_cast(Child.get())->Children; + std::move(NewChildren.begin(), NewChildren.end(), + std::back_inserter(RealChildren)); + break; + } + default: + RealChildren.push_back(std::move(Child)); + } + } + switch (RealChildren.size()) { + case 0: + return all(); + case 1: + return std::move(RealChildren.front()); + default: + return llvm::make_unique(std::move(RealChildren)); + } } std::unique_ptr Corpus::unionOf(std::vector> Children) const { - // If there is exactly one child, pull it one level up: OR(Child) -> Child. - return Children.size() == 1 ? std::move(Children.front()) - : llvm::make_unique(move(Children)); + std::vector> RealChildren; + for (auto &Child : Children) { + switch (Child->kind()) { + case Iterator::Kind::False: + break; // No effect, drop the iterator. + case Iterator::Kind::Or: { + // Inline nested OR into parent OR. + auto &NewChildren = static_cast(Child.get())->Children; + std::move(NewChildren.begin(), NewChildren.end(), + std::back_inserter(RealChildren)); + break; + } + case Iterator::Kind::True: + // Don't return all(), which would discard sibling boosts. + default: + RealChildren.push_back(std::move(Child)); + } + } + switch (RealChildren.size()) { + case 0: + return none(); + case 1: + return std::move(RealChildren.front()); + default: + return llvm::make_unique(std::move(RealChildren)); + } } std::unique_ptr Corpus::all() const { return llvm::make_unique(Size); } +std::unique_ptr Corpus::none() const { + return llvm::make_unique(); +} + std::unique_ptr Corpus::boost(std::unique_ptr Child, float Factor) const { - return llvm::make_unique(move(Child), Factor); + if (Factor == 1) + return Child; + if (Child->kind() == Iterator::Kind::False) + return Child; + return llvm::make_unique(std::move(Child), Factor); } std::unique_ptr Corpus::limit(std::unique_ptr Child, size_t Limit) const { - return llvm::make_unique(move(Child), Limit); + if (Child->kind() == Iterator::Kind::False) + return Child; + return llvm::make_unique(std::move(Child), Limit); } } // namespace dex diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 2b3bd4481..149fd43ad 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -98,8 +98,16 @@ class Iterator { return Iterator.dump(OS); } + /// Inspect iterator type, used internally for optimizing query trees. + enum class Kind { And, Or, True, False, Other }; + Kind kind() const { return MyKind; } + +protected: + Iterator(Kind MyKind = Kind::Other) : MyKind(MyKind) {} + private: virtual llvm::raw_ostream &dump(llvm::raw_ostream &OS) const = 0; + Kind MyKind; }; /// Advances the iterator until it is exhausted. Returns pairs of document IDs @@ -150,6 +158,9 @@ class Corpus { /// containing all items in range [0, Size) in an efficient manner. std::unique_ptr all() const; + /// Returns FALSE Iterator which iterates over no documents. + std::unique_ptr none() const; + /// Returns BOOST iterator which multiplies the score of each item by given /// factor. Boosting can be used as a computationally inexpensive filtering. /// Users can return significantly more items using consumeAndBoost() and diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index d1d5b4c7e..c36573c1a 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -109,6 +109,18 @@ TEST(DexIterators, AndThreeLists) { EXPECT_TRUE(And->reachedEnd()); } +TEST(DexIterators, AndEmpty) { + Corpus C{10000}; + const PostingList L1({1}); + const PostingList L2({2}); + // These iterators are empty, but the optimizer can't tell. + auto Empty1 = C.intersect(L1.iterator(), L2.iterator()); + auto Empty2 = C.intersect(L1.iterator(), L2.iterator()); + // And syncs iterators on construction, and used to fail on empty children. + auto And = C.intersect(std::move(Empty1), std::move(Empty2)); + EXPECT_TRUE(And->reachedEnd()); +} + TEST(DexIterators, OrTwoLists) { Corpus C{10000}; const PostingList L0({0, 5, 7, 10, 42, 320, 9000}); @@ -270,18 +282,8 @@ TEST(DexIterators, Limit) { } TEST(DexIterators, True) { - Corpus C{0}; - auto TrueIterator = C.all(); - EXPECT_TRUE(TrueIterator->reachedEnd()); - EXPECT_THAT(consumeIDs(*TrueIterator), ElementsAre()); - - C = Corpus{7}; - const PostingList L0({1, 2, 5, 7}); - TrueIterator = C.all(); - EXPECT_THAT(TrueIterator->peek(), 0); - auto AndIterator = C.intersect(L0.iterator(), move(TrueIterator)); - EXPECT_FALSE(AndIterator->reachedEnd()); - EXPECT_THAT(consumeIDs(*AndIterator), ElementsAre(1, 2, 5)); + EXPECT_TRUE(Corpus{0}.all()->reachedEnd()); + EXPECT_THAT(consumeIDs(*Corpus{4}.all()), ElementsAre(0, 1, 2, 3)); } TEST(DexIterators, Boost) { @@ -313,6 +315,38 @@ TEST(DexIterators, Boost) { EXPECT_THAT(ElementBoost, 3); } +TEST(DexIterators, Optimizations) { + Corpus C{5}; + const PostingList L1({1}); + const PostingList L2({2}); + const PostingList L3({3}); + + // empty and/or yield true/false + EXPECT_EQ(llvm::to_string(*C.intersect()), "true"); + EXPECT_EQ(llvm::to_string(*C.unionOf()), "false"); + + // true/false inside and/or short-circuit + EXPECT_EQ(llvm::to_string(*C.intersect(L1.iterator(), C.all())), "[1]"); + EXPECT_EQ(llvm::to_string(*C.intersect(L1.iterator(), C.none())), "false"); + // Not optimized to avoid breaking boosts. + EXPECT_EQ(llvm::to_string(*C.unionOf(L1.iterator(), C.all())), + "(| [1] true)"); + EXPECT_EQ(llvm::to_string(*C.unionOf(L1.iterator(), C.none())), "[1]"); + + // and/or nested inside and/or are flattened + EXPECT_EQ(llvm::to_string(*C.intersect( + L1.iterator(), C.intersect(L1.iterator(), L1.iterator()))), + "(& [1] [1] [1])"); + EXPECT_EQ(llvm::to_string(*C.unionOf( + L1.iterator(), C.unionOf(L2.iterator(), L3.iterator()))), + "(| [1] [2] [3])"); + + // optimizations combine over multiple levels + EXPECT_EQ(llvm::to_string(*C.intersect( + C.intersect(L1.iterator(), C.intersect()), C.unionOf(C.all()))), + "[1]"); +} + //===----------------------------------------------------------------------===// // Search token tests. //===----------------------------------------------------------------------===// From 24b65c7827cf401fbb2d61acef7799b8f7a54a9f Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 4 Oct 2018 14:01:55 +0000 Subject: [PATCH 302/686] [cland] Dex: fix/simplify short-trigram generation Summary: 1) Instead of x$$ for a short-query trigram, just use x 2) Make rules more coherent: prefixes of length 1-2, and first char + next head 3) Fix Dex::fuzzyFind to mark results as incomplete, because short-trigram rules only yield a subset of results. Reviewers: ioeric Subscribers: ilya-biryukov, jkorous, mgrang, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52808 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343775 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Dex.cpp | 4 +- clangd/index/dex/Trigram.cpp | 98 ++++++++++------------------------- clangd/index/dex/Trigram.h | 29 +++++------ unittests/clangd/DexTests.cpp | 74 +++++++++++++++++--------- 4 files changed, 93 insertions(+), 112 deletions(-) diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 08f168ddd..59cd8fc43 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -156,7 +156,9 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, "There must be no :: in query."); trace::Span Tracer("Dex fuzzyFind"); FuzzyMatcher Filter(Req.Query); - bool More = false; + // For short queries we use specialized trigrams that don't yield all results. + // Prevent clients from postfiltering them for longer queries. + bool More = !Req.Query.empty() && Req.Query.size() < 3; std::vector> TopLevelChildren; const auto TrigramTokens = generateQueryTrigrams(Req.Query); diff --git a/clangd/index/dex/Trigram.cpp b/clangd/index/dex/Trigram.cpp index a646a38de..eb5473ddd 100644 --- a/clangd/index/dex/Trigram.cpp +++ b/clangd/index/dex/Trigram.cpp @@ -23,11 +23,6 @@ namespace clang { namespace clangd { namespace dex { -/// This is used to mark unigrams and bigrams and distinct them from complete -/// trigrams. Since '$' is not present in valid identifier names, it is safe to -/// use it as the special symbol. -static const char END_MARKER = '$'; - std::vector generateIdentifierTrigrams(llvm::StringRef Identifier) { // Apply fuzzy matching text segmentation. std::vector Roles(Identifier.size()); @@ -47,40 +42,22 @@ std::vector generateIdentifierTrigrams(llvm::StringRef Identifier) { // not available then 0 is stored. std::vector> Next(LowercaseIdentifier.size()); unsigned NextTail = 0, NextHead = 0, NextNextHead = 0; - // Store two first HEAD characters in the identifier (if present). - std::deque TwoHeads; for (int I = LowercaseIdentifier.size() - 1; I >= 0; --I) { Next[I] = {{NextTail, NextHead, NextNextHead}}; NextTail = Roles[I] == Tail ? I : 0; if (Roles[I] == Head) { NextNextHead = NextHead; NextHead = I; - TwoHeads.push_front(LowercaseIdentifier[I]); - if (TwoHeads.size() > 2) - TwoHeads.pop_back(); } } DenseSet UniqueTrigrams; - auto add = [&](std::string Chars) { + auto Add = [&](std::string Chars) { UniqueTrigrams.insert(Token(Token::Kind::Trigram, Chars)); }; - if (TwoHeads.size() == 2) - add({{TwoHeads.front(), TwoHeads.back(), END_MARKER}}); - - if (!LowercaseIdentifier.empty()) - add({{LowercaseIdentifier.front(), END_MARKER, END_MARKER}}); - - if (LowercaseIdentifier.size() >= 2) - add({{LowercaseIdentifier[0], LowercaseIdentifier[1], END_MARKER}}); - - if (LowercaseIdentifier.size() >= 3) - add({{LowercaseIdentifier[0], LowercaseIdentifier[1], - LowercaseIdentifier[2]}}); - - // Iterate through valid seqneces of three characters Fuzzy Matcher can + // Iterate through valid sequneces of three characters Fuzzy Matcher can // process. for (size_t I = 0; I < LowercaseIdentifier.size(); ++I) { // Skip delimiters. @@ -92,66 +69,47 @@ std::vector generateIdentifierTrigrams(llvm::StringRef Identifier) { for (const unsigned K : Next[J]) { if (K == 0) continue; - add({{LowercaseIdentifier[I], LowercaseIdentifier[J], + Add({{LowercaseIdentifier[I], LowercaseIdentifier[J], LowercaseIdentifier[K]}}); } } } + // Emit short-query trigrams: FooBar -> f, fo, fb. + if (!LowercaseIdentifier.empty()) + Add({LowercaseIdentifier[0]}); + if (LowercaseIdentifier.size() >= 2) + Add({LowercaseIdentifier[0], LowercaseIdentifier[1]}); + for (size_t I = 1; I < LowercaseIdentifier.size(); ++I) + if (Roles[I] == Head) { + Add({LowercaseIdentifier[0], LowercaseIdentifier[I]}); + break; + } - std::vector Result; - for (const auto &Trigram : UniqueTrigrams) - Result.push_back(Trigram); - - return Result; + return {UniqueTrigrams.begin(), UniqueTrigrams.end()}; } std::vector generateQueryTrigrams(llvm::StringRef Query) { + std::string LowercaseQuery = Query.lower(); + if (Query.size() < 3) // short-query trigrams only + return {Token(Token::Kind::Trigram, LowercaseQuery)}; + // Apply fuzzy matching text segmentation. std::vector Roles(Query.size()); calculateRoles(Query, llvm::makeMutableArrayRef(Roles.data(), Query.size())); - // Additional pass is necessary to count valid identifier characters. - // Depending on that, this function might return incomplete trigram. - unsigned ValidSymbolsCount = 0; - for (const auto Role : Roles) - if (Role == Head || Role == Tail) - ++ValidSymbolsCount; - - std::string LowercaseQuery = Query.lower(); - DenseSet UniqueTrigrams; - - // If the number of symbols which can form fuzzy matching trigram is not - // sufficient, generate a single incomplete trigram for query. - if (ValidSymbolsCount < 3) { - std::string Chars = - LowercaseQuery.substr(0, std::min(3UL, Query.size())); - Chars.append(3 - Chars.size(), END_MARKER); - UniqueTrigrams.insert(Token(Token::Kind::Trigram, Chars)); - } else { - std::deque Chars; - for (size_t I = 0; I < LowercaseQuery.size(); ++I) { - // If current symbol is delimiter, just skip it. - if (Roles[I] != Head && Roles[I] != Tail) - continue; - - Chars.push_back(LowercaseQuery[I]); - - if (Chars.size() > 3) - Chars.pop_front(); - - if (Chars.size() == 3) { - UniqueTrigrams.insert( - Token(Token::Kind::Trigram, std::string(begin(Chars), end(Chars)))); - } - } + std::string Chars; + for (unsigned I = 0; I < Query.size(); ++I) { + if (Roles[I] != Head && Roles[I] != Tail) + continue; // Skip delimiters. + Chars.push_back(LowercaseQuery[I]); + if (Chars.size() > 3) + Chars.erase(Chars.begin()); + if (Chars.size() == 3) + UniqueTrigrams.insert(Token(Token::Kind::Trigram, Chars)); } - std::vector Result; - for (const auto &Trigram : UniqueTrigrams) - Result.push_back(Trigram); - - return Result; + return {UniqueTrigrams.begin(), UniqueTrigrams.end()}; } } // namespace dex diff --git a/clangd/index/dex/Trigram.h b/clangd/index/dex/Trigram.h index a01c1063d..d0fb474ff 100644 --- a/clangd/index/dex/Trigram.h +++ b/clangd/index/dex/Trigram.h @@ -33,26 +33,21 @@ namespace clangd { namespace dex { /// Returns list of unique fuzzy-search trigrams from unqualified symbol. +/// The trigrams give the 3-character query substrings this symbol can match. /// -/// First, given Identifier (unqualified symbol name) is segmented using -/// FuzzyMatch API and lowercased. After segmentation, the following technique -/// is applied for generating trigrams: for each letter or digit in the input -/// string the algorithms looks for the possible next and skip-1-next characters -/// which can be jumped to during fuzzy matching. Each combination of such three -/// characters is inserted into the result. -/// +/// The symbol's name is broken into segments, e.g. "FooBar" has two segments. /// Trigrams can start at any character in the input. Then we can choose to move -/// to the next character, move to the start of the next segment, or skip over a -/// segment. +/// to the next character, move to the start of the next segment, or stop. /// -/// This also generates incomplete trigrams for short query scenarios: -/// * Empty trigram: "$$$". -/// * Unigram: the first character of the identifier. -/// * Bigrams: a 2-char prefix of the identifier and a bigram of the first two -/// HEAD characters (if they exist). -// -/// Note: the returned list of trigrams does not have duplicates, if any trigram -/// belongs to more than one class it is only inserted once. +/// Short trigrams (length 1-2) are used for short queries. These are: +/// - prefixes of the identifier, of length 1 and 2 +/// - the first character + next head character +/// +/// For "FooBar" we get the following trigrams: +/// {f, fo, fb, foo, fob, fba, oob, oba, bar}. +/// +/// Trigrams are lowercase, as trigram matching is case-insensitive. +/// Trigrams in the returned list are deduplicated. std::vector generateIdentifierTrigrams(llvm::StringRef Identifier); /// Returns list of unique fuzzy-search trigrams given a query. diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index c36573c1a..29319303e 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -367,53 +367,54 @@ trigramsAre(std::initializer_list Trigrams) { TEST(DexTrigrams, IdentifierTrigrams) { EXPECT_THAT(generateIdentifierTrigrams("X86"), - trigramsAre({"x86", "x$$", "x8$"})); + trigramsAre({"x86", "x", "x8"})); - EXPECT_THAT(generateIdentifierTrigrams("nl"), trigramsAre({"nl$", "n$$"})); + EXPECT_THAT(generateIdentifierTrigrams("nl"), trigramsAre({"nl", "n"})); - EXPECT_THAT(generateIdentifierTrigrams("n"), trigramsAre({"n$$"})); + EXPECT_THAT(generateIdentifierTrigrams("n"), trigramsAre({"n"})); EXPECT_THAT(generateIdentifierTrigrams("clangd"), - trigramsAre({"c$$", "cl$", "cla", "lan", "ang", "ngd"})); + trigramsAre({"c", "cl", "cla", "lan", "ang", "ngd"})); EXPECT_THAT(generateIdentifierTrigrams("abc_def"), - trigramsAre({"a$$", "abc", "abd", "ade", "bcd", "bde", "cde", - "def", "ab$", "ad$"})); + trigramsAre({"a", "ab", "ad", "abc", "abd", "ade", "bcd", "bde", + "cde", "def"})); EXPECT_THAT(generateIdentifierTrigrams("a_b_c_d_e_"), - trigramsAre({"a$$", "a_$", "a_b", "abc", "abd", "acd", "ace", - "bcd", "bce", "bde", "cde", "ab$"})); + trigramsAre({"a", "a_", "ab", "abc", "abd", "acd", "ace", "bcd", + "bce", "bde", "cde"})); EXPECT_THAT(generateIdentifierTrigrams("unique_ptr"), - trigramsAre({"u$$", "uni", "unp", "upt", "niq", "nip", "npt", - "iqu", "iqp", "ipt", "que", "qup", "qpt", "uep", - "ept", "ptr", "un$", "up$"})); + trigramsAre({"u", "un", "up", "uni", "unp", "upt", "niq", "nip", + "npt", "iqu", "iqp", "ipt", "que", "qup", "qpt", + "uep", "ept", "ptr"})); EXPECT_THAT( generateIdentifierTrigrams("TUDecl"), - trigramsAre({"t$$", "tud", "tde", "ude", "dec", "ecl", "tu$", "td$"})); + trigramsAre({"t", "tu", "td", "tud", "tde", "ude", "dec", "ecl"})); EXPECT_THAT(generateIdentifierTrigrams("IsOK"), - trigramsAre({"i$$", "iso", "iok", "sok", "is$", "io$"})); + trigramsAre({"i", "is", "io", "iso", "iok", "sok"})); + auto X = generateIdentifierTrigrams("abc_defGhij__klm"); EXPECT_THAT( generateIdentifierTrigrams("abc_defGhij__klm"), - trigramsAre({"a$$", "abc", "abd", "abg", "ade", "adg", "adk", "agh", - "agk", "bcd", "bcg", "bde", "bdg", "bdk", "bgh", "bgk", - "cde", "cdg", "cdk", "cgh", "cgk", "def", "deg", "dek", - "dgh", "dgk", "dkl", "efg", "efk", "egh", "egk", "ekl", - "fgh", "fgk", "fkl", "ghi", "ghk", "gkl", "hij", "hik", - "hkl", "ijk", "ikl", "jkl", "klm", "ab$", "ad$"})); + trigramsAre({"a", "ab", "ad", "abc", "abd", "abg", "ade", "adg", + "adk", "agh", "agk", "bcd", "bcg", "bde", "bdg", "bdk", + "bgh", "bgk", "cde", "cdg", "cdk", "cgh", "cgk", "def", + "deg", "dek", "dgh", "dgk", "dkl", "efg", "efk", "egh", + "egk", "ekl", "fgh", "fgk", "fkl", "ghi", "ghk", "gkl", + "hij", "hik", "hkl", "ijk", "ikl", "jkl", "klm"})); } TEST(DexTrigrams, QueryTrigrams) { - EXPECT_THAT(generateQueryTrigrams("c"), trigramsAre({"c$$"})); - EXPECT_THAT(generateQueryTrigrams("cl"), trigramsAre({"cl$"})); + EXPECT_THAT(generateQueryTrigrams("c"), trigramsAre({"c"})); + EXPECT_THAT(generateQueryTrigrams("cl"), trigramsAre({"cl"})); EXPECT_THAT(generateQueryTrigrams("cla"), trigramsAre({"cla"})); - EXPECT_THAT(generateQueryTrigrams("_"), trigramsAre({"_$$"})); - EXPECT_THAT(generateQueryTrigrams("__"), trigramsAre({"__$"})); - EXPECT_THAT(generateQueryTrigrams("___"), trigramsAre({"___"})); + EXPECT_THAT(generateQueryTrigrams("_"), trigramsAre({"_"})); + EXPECT_THAT(generateQueryTrigrams("__"), trigramsAre({"__"})); + EXPECT_THAT(generateQueryTrigrams("___"), trigramsAre({})); EXPECT_THAT(generateQueryTrigrams("X86"), trigramsAre({"x86"})); @@ -529,6 +530,31 @@ TEST(DexTest, FuzzyMatch) { UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); } +// TODO(sammccall): enable after D52796 bugfix. +#if 0 +TEST(DexTest, ShortQuery) { + auto I = + Dex::build(generateSymbols({"OneTwoThreeFour"}), RefSlab(), URISchemes); + FuzzyFindRequest Req; + bool Incomplete; + + EXPECT_THAT(match(*I, Req, &Incomplete), ElementsAre("OneTwoThreeFour")); + EXPECT_FALSE(Incomplete) << "Empty string is not a short query"; + + Req.Query = "t"; + EXPECT_THAT(match(*I, Req, &Incomplete), ElementsAre()); + EXPECT_TRUE(Incomplete) << "Short queries have different semantics"; + + Req.Query = "tt"; + EXPECT_THAT(match(*I, Req, &Incomplete), ElementsAre()); + EXPECT_TRUE(Incomplete) << "Short queries have different semantics"; + + Req.Query = "ttf"; + EXPECT_THAT(match(*I, Req, &Incomplete), ElementsAre("OneTwoThreeFour")); + EXPECT_FALSE(Incomplete) << "3-char string is not a short query"; +} +#endif + TEST(DexTest, MatchQualifiedNamesWithoutSpecificScope) { auto I = Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab(), URISchemes); From f6fccaef72e9eeca6b3630c255ceb256810c245c Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 4 Oct 2018 14:08:11 +0000 Subject: [PATCH 303/686] [clangd] Remove one-segment-skipping from Dex trigrams. Summary: Currently queries like "ab" can match identifiers like a_yellow_bee. The value of allowing this for exactly one segment but no more seems dubious. It costs ~3% of overall ram (~9% of posting list ram) and some quality. Reviewers: ilya-biryukov, ioeric Subscribers: MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52885 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343777 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Trigram.cpp | 6 ++---- clangd/index/dex/Trigram.h | 2 +- unittests/clangd/DexTests.cpp | 12 ++++-------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/clangd/index/dex/Trigram.cpp b/clangd/index/dex/Trigram.cpp index eb5473ddd..641c899ab 100644 --- a/clangd/index/dex/Trigram.cpp +++ b/clangd/index/dex/Trigram.cpp @@ -36,17 +36,15 @@ std::vector generateIdentifierTrigrams(llvm::StringRef Identifier) { // // * Next Tail - next character from the same segment // * Next Head - front character of the next segment - // * Skip-1-Next Head - front character of the skip-1-next segment // // Next stores tuples of three indices in the presented order, if a variant is // not available then 0 is stored. std::vector> Next(LowercaseIdentifier.size()); - unsigned NextTail = 0, NextHead = 0, NextNextHead = 0; + unsigned NextTail = 0, NextHead = 0; for (int I = LowercaseIdentifier.size() - 1; I >= 0; --I) { - Next[I] = {{NextTail, NextHead, NextNextHead}}; + Next[I] = {{NextTail, NextHead}}; NextTail = Roles[I] == Tail ? I : 0; if (Roles[I] == Head) { - NextNextHead = NextHead; NextHead = I; } } diff --git a/clangd/index/dex/Trigram.h b/clangd/index/dex/Trigram.h index d0fb474ff..adce9f423 100644 --- a/clangd/index/dex/Trigram.h +++ b/clangd/index/dex/Trigram.h @@ -37,7 +37,7 @@ namespace dex { /// /// The symbol's name is broken into segments, e.g. "FooBar" has two segments. /// Trigrams can start at any character in the input. Then we can choose to move -/// to the next character, move to the start of the next segment, or stop. +/// to the next character or to the start of the next segment. /// /// Short trigrams (length 1-2) are used for short queries. These are: /// - prefixes of the identifier, of length 1 and 2 diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 29319303e..d7223ab19 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -381,8 +381,7 @@ TEST(DexTrigrams, IdentifierTrigrams) { "cde", "def"})); EXPECT_THAT(generateIdentifierTrigrams("a_b_c_d_e_"), - trigramsAre({"a", "a_", "ab", "abc", "abd", "acd", "ace", "bcd", - "bce", "bde", "cde"})); + trigramsAre({"a", "a_", "ab", "abc", "bcd", "cde"})); EXPECT_THAT(generateIdentifierTrigrams("unique_ptr"), trigramsAre({"u", "un", "up", "uni", "unp", "upt", "niq", "nip", @@ -396,14 +395,11 @@ TEST(DexTrigrams, IdentifierTrigrams) { EXPECT_THAT(generateIdentifierTrigrams("IsOK"), trigramsAre({"i", "is", "io", "iso", "iok", "sok"})); - auto X = generateIdentifierTrigrams("abc_defGhij__klm"); EXPECT_THAT( generateIdentifierTrigrams("abc_defGhij__klm"), - trigramsAre({"a", "ab", "ad", "abc", "abd", "abg", "ade", "adg", - "adk", "agh", "agk", "bcd", "bcg", "bde", "bdg", "bdk", - "bgh", "bgk", "cde", "cdg", "cdk", "cgh", "cgk", "def", - "deg", "dek", "dgh", "dgk", "dkl", "efg", "efk", "egh", - "egk", "ekl", "fgh", "fgk", "fkl", "ghi", "ghk", "gkl", + trigramsAre({"a", "ab", "ad", "abc", "abd", "ade", "adg", "bcd", + "bde", "bdg", "cde", "cdg", "def", "deg", "dgh", "dgk", + "efg", "egh", "egk", "fgh", "fgk", "ghi", "ghk", "gkl", "hij", "hik", "hkl", "ijk", "ikl", "jkl", "klm"})); } From 61c90c17020a3c903e0be84d830abd513cf5c614 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 4 Oct 2018 14:09:55 +0000 Subject: [PATCH 304/686] [clangd] clangd-indexer gathers refs and stores them in index files. Reviewers: ioeric Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52531 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343778 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/IndexAction.cpp | 19 +++-- clangd/index/IndexAction.h | 5 +- clangd/index/Serialization.cpp | 64 ++++++++++++++++- clangd/index/Serialization.h | 11 +-- clangd/index/YAMLSerialization.cpp | 94 +++++++++++++++++++++++-- clangd/indexer/IndexerMain.cpp | 14 +++- unittests/clangd/SerializationTests.cpp | 46 +++++++++++- 7 files changed, 232 insertions(+), 21 deletions(-) diff --git a/clangd/index/IndexAction.cpp b/clangd/index/IndexAction.cpp index 9bc7e06ae..68f58336c 100644 --- a/clangd/index/IndexAction.cpp +++ b/clangd/index/IndexAction.cpp @@ -13,10 +13,11 @@ class IndexAction : public WrapperFrontendAction { IndexAction(std::shared_ptr C, std::unique_ptr Includes, const index::IndexingOptions &Opts, - std::function &SymbolsCallback) + std::function SymbolsCallback, + std::function RefsCallback) : WrapperFrontendAction(index::createIndexingAction(C, Opts, nullptr)), - SymbolsCallback(SymbolsCallback), Collector(C), - Includes(std::move(Includes)), + SymbolsCallback(SymbolsCallback), RefsCallback(RefsCallback), + Collector(C), Includes(std::move(Includes)), PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {} std::unique_ptr CreateASTConsumer(CompilerInstance &CI, @@ -41,10 +42,13 @@ class IndexAction : public WrapperFrontendAction { return; } SymbolsCallback(Collector->takeSymbols()); + if (RefsCallback != nullptr) + RefsCallback(Collector->takeRefs()); } private: std::function SymbolsCallback; + std::function RefsCallback; std::shared_ptr Collector; std::unique_ptr Includes; std::unique_ptr PragmaHandler; @@ -54,20 +58,23 @@ class IndexAction : public WrapperFrontendAction { std::unique_ptr createStaticIndexingAction(SymbolCollector::Options Opts, - std::function SymbolsCallback) { + std::function SymbolsCallback, + std::function RefsCallback) { index::IndexingOptions IndexOpts; IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All; Opts.CollectIncludePath = true; Opts.CountReferences = true; Opts.Origin = SymbolOrigin::Static; + if (RefsCallback != nullptr) + Opts.RefFilter = RefKind::All; auto Includes = llvm::make_unique(); addSystemHeadersMapping(Includes.get()); Opts.Includes = Includes.get(); return llvm::make_unique( std::make_shared(std::move(Opts)), std::move(Includes), - IndexOpts, SymbolsCallback); -} + IndexOpts, SymbolsCallback, RefsCallback); +}; } // namespace clangd } // namespace clang diff --git a/clangd/index/IndexAction.h b/clangd/index/IndexAction.h index b51bfd252..2330afc41 100644 --- a/clangd/index/IndexAction.h +++ b/clangd/index/IndexAction.h @@ -21,10 +21,13 @@ namespace clangd { // Only a subset of SymbolCollector::Options are respected: // - include paths are always collected, and canonicalized appropriately // - references are always counted +// - main-file refs are collected (if RefsCallback is non-null) // - the symbol origin is always Static +// FIXME: refs from headers should also be collected. std::unique_ptr createStaticIndexingAction(SymbolCollector::Options Opts, - std::function SymbolsCallback); + std::function SymbolsCallback, + std::function RefsCallback); } // namespace clangd } // namespace clang diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index 2ebd2041f..8784d73ee 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -298,17 +298,47 @@ Symbol readSymbol(Reader &Data, ArrayRef Strings) { return Sym; } +// REFS ENCODING +// A refs section has data grouped by Symbol. Each symbol has: +// - SymbolID: 20 bytes +// - NumRefs: varint +// - Ref[NumRefs] +// Fields of Ref are encoded in turn, see implementation. + +void writeRefs(const SymbolID &ID, ArrayRef Refs, + const StringTableOut &Strings, raw_ostream &OS) { + OS << ID.raw(); + writeVar(Refs.size(), OS); + for (const auto &Ref : Refs) { + OS.write(static_cast(Ref.Kind)); + writeLocation(Ref.Location, Strings, OS); + } +} + +std::pair> readRefs(Reader &Data, + ArrayRef Strings) { + std::pair> Result; + Result.first = Data.consumeID(); + Result.second.resize(Data.consumeVar()); + for (auto &Ref : Result.second) { + Ref.Kind = static_cast(Data.consume8()); + Ref.Location = readLocation(Data, Strings); + } + return Result; +} + // FILE ENCODING // A file is a RIFF chunk with type 'CdIx'. // It contains the sections: // - meta: version number // - stri: string table // - symb: symbols +// - refs: references to symbols // The current versioning scheme is simple - non-current versions are rejected. // If you make a breaking change, bump this version number to invalidate stored // data. Later we may want to support some backward compatibility. -constexpr static uint32_t Version = 4; +constexpr static uint32_t Version = 5; Expected readRIFF(StringRef Data) { auto RIFF = riff::readFile(Data); @@ -342,6 +372,18 @@ Expected readRIFF(StringRef Data) { return makeError("malformed or truncated symbol"); Result.Symbols = std::move(Symbols).build(); } + if (Chunks.count("refs")) { + Reader RefsReader(Chunks.lookup("refs")); + RefSlab::Builder Refs; + while (!RefsReader.eof()) { + auto RefsBundle = readRefs(RefsReader, Strings->Strings); + for (const auto &Ref : RefsBundle.second) // FIXME: bulk insert? + Refs.insert(RefsBundle.first, Ref); + } + if (RefsReader.err()) + return makeError("malformed or truncated refs"); + Result.Refs = std::move(Refs).build(); + } return std::move(Result); } @@ -363,6 +405,14 @@ void writeRIFF(const IndexFileOut &Data, raw_ostream &OS) { Symbols.emplace_back(Sym); visitStrings(Symbols.back(), [&](StringRef &S) { Strings.intern(S); }); } + std::vector>> Refs; + if (Data.Refs) { + for (const auto &Sym : *Data.Refs) { + Refs.emplace_back(Sym); + for (auto &Ref : Refs.back().second) + Strings.intern(Ref.Location.FileURI); + } + } std::string StringSection; { @@ -379,6 +429,16 @@ void writeRIFF(const IndexFileOut &Data, raw_ostream &OS) { } RIFF.Chunks.push_back({riff::fourCC("symb"), SymbolSection}); + std::string RefsSection; + if (Data.Refs) { + { + raw_string_ostream RefsOS(RefsSection); + for (const auto &Sym : Refs) + writeRefs(Sym.first, Sym.second, Strings, RefsOS); + } + RIFF.Chunks.push_back({riff::fourCC("refs"), RefsSection}); + } + OS << RIFF; } @@ -428,6 +488,8 @@ std::unique_ptr loadIndex(llvm::StringRef SymbolFilename, if (auto I = readIndexFile(Buffer->get()->getBuffer())) { if (I->Symbols) Symbols = std::move(*I->Symbols); + if (I->Refs) + Refs = std::move(*I->Refs); } else { llvm::errs() << "Bad Index: " << llvm::toString(I.takeError()) << "\n"; return nullptr; diff --git a/clangd/index/Serialization.h b/clangd/index/Serialization.h index 3cb86dbd2..65b53ee34 100644 --- a/clangd/index/Serialization.h +++ b/clangd/index/Serialization.h @@ -38,26 +38,29 @@ enum class IndexFileFormat { // Holds the contents of an index file that was read. struct IndexFileIn { llvm::Optional Symbols; + llvm::Optional Refs; }; -// Parse an index file. The input must be a RIFF container chunk. +// Parse an index file. The input must be a RIFF or YAML file. llvm::Expected readIndexFile(llvm::StringRef); // Specifies the contents of an index file to be written. struct IndexFileOut { - const SymbolSlab *Symbols; - // TODO: Support serializing symbol occurrences. + const SymbolSlab *Symbols = nullptr; + const RefSlab *Refs = nullptr; // TODO: Support serializing Dex posting lists. IndexFileFormat Format = IndexFileFormat::RIFF; IndexFileOut() = default; IndexFileOut(const IndexFileIn &I) - : Symbols(I.Symbols ? I.Symbols.getPointer() : nullptr) {} + : Symbols(I.Symbols ? I.Symbols.getPointer() : nullptr), + Refs(I.Refs ? I.Refs.getPointer() : nullptr) {} }; // Serializes an index file. llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IndexFileOut &O); // Convert a single symbol to YAML, a nice debug representation. std::string toYAML(const Symbol &); +std::string toYAML(const std::pair> &); // Build an in-memory static index from an index file. // The size should be relatively small, so data can be managed in memory. diff --git a/clangd/index/YAMLSerialization.cpp b/clangd/index/YAMLSerialization.cpp index 73df61878..a426ccee9 100644 --- a/clangd/index/YAMLSerialization.cpp +++ b/clangd/index/YAMLSerialization.cpp @@ -6,6 +6,12 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// +// +// A YAML index file is a sequence of tagged entries. +// Each entry either encodes a Symbol or the list of references to a symbol +// (a "ref bundle"). +// +//===----------------------------------------------------------------------===// #include "Index.h" #include "Serialization.h" @@ -20,10 +26,22 @@ #include LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Symbol::IncludeHeaderWithReferences) +LLVM_YAML_IS_SEQUENCE_VECTOR(clang::clangd::Ref) +namespace { +using RefBundle = + std::pair>; +// This is a pale imitation of std::variant +struct VariantEntry { + llvm::Optional Symbol; + llvm::Optional Refs; +}; +} // namespace namespace llvm { namespace yaml { +using clang::clangd::Ref; +using clang::clangd::RefKind; using clang::clangd::Symbol; using clang::clangd::SymbolID; using clang::clangd::SymbolLocation; @@ -179,6 +197,46 @@ template <> struct ScalarEnumerationTraits { } }; +template <> struct MappingTraits { + static void mapping(IO &IO, RefBundle &Refs) { + MappingNormalization NSymbolID(IO, + Refs.first); + IO.mapRequired("ID", NSymbolID->HexString); + IO.mapRequired("References", Refs.second); + } +}; + +struct NormalizedRefKind { + NormalizedRefKind(IO &) {} + NormalizedRefKind(IO &, RefKind O) { Kind = static_cast(O); } + + RefKind denormalize(IO &) { return static_cast(Kind); } + + uint8_t Kind = 0; +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, Ref &R) { + MappingNormalization NKind(IO, R.Kind); + IO.mapRequired("Kind", NKind->Kind); + IO.mapRequired("Location", R.Location); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, VariantEntry &Variant) { + if (IO.mapTag("!Symbol", Variant.Symbol.hasValue())) { + if (!IO.outputting()) + Variant.Symbol.emplace(); + MappingTraits::mapping(IO, *Variant.Symbol); + } else if (IO.mapTag("!Refs", Variant.Refs.hasValue())) { + if (!IO.outputting()) + Variant.Refs.emplace(); + MappingTraits::mapping(IO, *Variant.Refs); + } + } +}; + } // namespace yaml } // namespace llvm @@ -187,23 +245,38 @@ namespace clangd { void writeYAML(const IndexFileOut &O, raw_ostream &OS) { llvm::yaml::Output Yout(OS); - for (Symbol Sym : *O.Symbols) // copy: Yout<< requires mutability. - Yout << Sym; + for (const auto &Sym : *O.Symbols) { + VariantEntry Entry; + Entry.Symbol = Sym; + Yout << Entry; + } + if (O.Refs) + for (auto &Sym : *O.Refs) { + VariantEntry Entry; + Entry.Refs = Sym; + Yout << Entry; + } } Expected readYAML(StringRef Data) { SymbolSlab::Builder Symbols; + RefSlab::Builder Refs; llvm::yaml::Input Yin(Data); do { - Symbol S; - Yin >> S; + VariantEntry Variant; + Yin >> Variant; if (Yin.error()) return llvm::errorCodeToError(Yin.error()); - Symbols.insert(S); + if (Variant.Symbol) + Symbols.insert(*Variant.Symbol); + if (Variant.Refs) + for (const auto &Ref : Variant.Refs->second) + Refs.insert(Variant.Refs->first, Ref); } while (Yin.nextDocument()); IndexFileIn Result; Result.Symbols.emplace(std::move(Symbols).build()); + Result.Refs.emplace(std::move(Refs).build()); return std::move(Result); } @@ -218,5 +291,16 @@ std::string toYAML(const Symbol &S) { return Buf; } +std::string toYAML(const std::pair> &Data) { + RefBundle Refs = {Data.first, Data.second}; + std::string Buf; + { + llvm::raw_string_ostream OS(Buf); + llvm::yaml::Output Yout(OS); + Yout << Refs; + } + return Buf; +} + } // namespace clangd } // namespace clang diff --git a/clangd/indexer/IndexerMain.cpp b/clangd/indexer/IndexerMain.cpp index 8db755959..10490c06e 100644 --- a/clangd/indexer/IndexerMain.cpp +++ b/clangd/indexer/IndexerMain.cpp @@ -67,18 +67,30 @@ class IndexActionFactory : public tooling::FrontendActionFactory { else Symbols.insert(Sym); } + }, + [&](RefSlab S) { + std::lock_guard Lock(SymbolsMu); + for (const auto &Sym : S) { + // No need to merge as currently all Refs are from main file. + for (const auto &Ref : Sym.second) + Refs.insert(Sym.first, Ref); + } }) .release(); } // Awkward: we write the result in the destructor, because the executor // takes ownership so it's the easiest way to get our data back out. - ~IndexActionFactory() { Result.Symbols = std::move(Symbols).build(); } + ~IndexActionFactory() { + Result.Symbols = std::move(Symbols).build(); + Result.Refs = std::move(Refs).build(); + } private: IndexFileIn &Result; std::mutex SymbolsMu; SymbolSlab::Builder Symbols; + RefSlab::Builder Refs; }; } // namespace diff --git a/unittests/clangd/SerializationTests.cpp b/unittests/clangd/SerializationTests.cpp index 751883b27..720c7df55 100644 --- a/unittests/clangd/SerializationTests.cpp +++ b/unittests/clangd/SerializationTests.cpp @@ -13,6 +13,9 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +using testing::_; +using testing::AllOf; +using testing::Pair; using testing::UnorderedElementsAre; using testing::UnorderedElementsAreArray; namespace clang { @@ -21,6 +24,7 @@ namespace { const char *YAML = R"( --- +!Symbol ID: 057557CEBF6E6B2DD437FBF60CC58F352D1DF856 Name: 'Foo1' Scope: 'clang::' @@ -46,6 +50,7 @@ ReturnType: 'int' References: 3 ... --- +!Symbol ID: 057557CEBF6E6B2DD437FBF60CC58F352D1DF858 Name: 'Foo2' Scope: 'clang::' @@ -64,6 +69,18 @@ Flags: 2 Signature: '-sig' CompletionSnippetSuffix: '-snippet' ... +!Refs +ID: 057557CEBF6E6B2DD437FBF60CC58F352D1DF856 +References: + - Kind: 4 + Location: + FileURI: file:///path/foo.cc + Start: + Line: 5 + Column: 3 + End: + Line: 5 + Column: 8 )"; MATCHER_P(ID, I, "") { return arg.ID == cantFail(SymbolID::fromStr(I)); } @@ -107,6 +124,16 @@ TEST(SerializationTest, YAMLConversions) { EXPECT_EQ(Sym2.CanonicalDeclaration.FileURI, "file:///path/bar.h"); EXPECT_FALSE(Sym2.Flags & Symbol::IndexedForCodeCompletion); EXPECT_TRUE(Sym2.Flags & Symbol::Deprecated); + + ASSERT_TRUE(bool(ParsedYAML->Refs)); + EXPECT_THAT(*ParsedYAML->Refs, + UnorderedElementsAre( + Pair(cantFail(SymbolID::fromStr( + "057557CEBF6E6B2DD437FBF60CC58F352D1DF856")), + testing::SizeIs(1)))); + auto Ref1 = ParsedYAML->Refs->begin()->second.front(); + EXPECT_EQ(Ref1.Kind, RefKind::Reference); + EXPECT_EQ(Ref1.Location.FileURI, "file:///path/foo.cc"); } std::vector YAMLFromSymbols(const SymbolSlab &Slab) { @@ -115,24 +142,37 @@ std::vector YAMLFromSymbols(const SymbolSlab &Slab) { Result.push_back(toYAML(Sym)); return Result; } +std::vector YAMLFromRefs(const RefSlab &Slab) { + std::vector Result; + for (const auto &Sym : Slab) + Result.push_back(toYAML(Sym)); + return Result; +} TEST(SerializationTest, BinaryConversions) { auto In = readIndexFile(YAML); EXPECT_TRUE(bool(In)) << In.takeError(); // Write to binary format, and parse again. - IndexFileOut Out; - Out.Symbols = In->Symbols.getPointer(); + IndexFileOut Out(*In); Out.Format = IndexFileFormat::RIFF; std::string Serialized = llvm::to_string(Out); + { + std::error_code EC; + llvm::raw_fd_ostream F("/tmp/foo", EC); + F << Serialized; + } auto In2 = readIndexFile(Serialized); ASSERT_TRUE(bool(In2)) << In.takeError(); - ASSERT_TRUE(In->Symbols); + ASSERT_TRUE(In2->Symbols); + ASSERT_TRUE(In2->Refs); // Assert the YAML serializations match, for nice comparisons and diffs. EXPECT_THAT(YAMLFromSymbols(*In2->Symbols), UnorderedElementsAreArray(YAMLFromSymbols(*In->Symbols))); + EXPECT_THAT(YAMLFromRefs(*In2->Refs), + UnorderedElementsAreArray(YAMLFromRefs(*In->Refs))); } } // namespace From aec459aa20cdac302b7f08a2133561401e360238 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 4 Oct 2018 14:20:22 +0000 Subject: [PATCH 305/686] [clangd] expose MergedIndex class Summary: This allows inheriting from it, so index() can ga away and allowing TestTU::index) to be fixed. Reviewers: ioeric Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52250 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343780 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 11 +- clangd/ClangdServer.h | 4 +- clangd/index/FileIndex.cpp | 6 +- clangd/index/FileIndex.h | 8 +- clangd/index/Merge.cpp | 188 +++++++++++++--------------- clangd/index/Merge.h | 23 +++- unittests/clangd/FileIndexTests.cpp | 14 +-- unittests/clangd/IndexTests.cpp | 22 ++-- unittests/clangd/TestTU.cpp | 7 +- 9 files changed, 137 insertions(+), 146 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 01fbe34fa..96f83588e 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -117,20 +117,17 @@ ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB, : nullptr, Opts.UpdateDebounce, Opts.RetentionPolicy) { if (DynamicIdx && Opts.StaticIndex) { - MergedIndex = mergeIndex(&DynamicIdx->index(), Opts.StaticIndex); - Index = MergedIndex.get(); + MergedIdx = + llvm::make_unique(DynamicIdx.get(), Opts.StaticIndex); + Index = MergedIdx.get(); } else if (DynamicIdx) - Index = &DynamicIdx->index(); + Index = DynamicIdx.get(); else if (Opts.StaticIndex) Index = Opts.StaticIndex; else Index = nullptr; } -const SymbolIndex *ClangdServer::dynamicIndex() const { - return DynamicIdx ? &DynamicIdx->index() : nullptr; -} - void ClangdServer::setRootPath(PathRef RootPath) { auto FS = FSProvider.getFileSystem(); auto Status = FS->status(RootPath); diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h index 1706ebb0e..c66ed868a 100644 --- a/clangd/ClangdServer.h +++ b/clangd/ClangdServer.h @@ -206,7 +206,7 @@ class ClangdServer { /// Returns the active dynamic index if one was built. /// This can be useful for testing, debugging, or observing memory usage. - const SymbolIndex *dynamicIndex() const; + const SymbolIndex *dynamicIndex() const { return DynamicIdx.get(); } // Blocks the main thread until the server is idle. Only for use in tests. // Returns false if the timeout expires. @@ -244,7 +244,7 @@ class ClangdServer { // If present, an index of symbols in open files. Read via *Index. std::unique_ptr DynamicIdx; // If present, storage for the merged static/dynamic index. Read via *Index. - std::unique_ptr MergedIndex; + std::unique_ptr MergedIdx; // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex) llvm::StringMap> diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 447a96c22..8bc0cde30 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -152,10 +152,10 @@ std::unique_ptr FileSymbols::buildMemIndex() { } FileIndex::FileIndex(std::vector URISchemes) - : URISchemes(std::move(URISchemes)), + : MergedIndex(&MainFileIndex, &PreambleIndex), + URISchemes(std::move(URISchemes)), PreambleIndex(PreambleSymbols.buildMemIndex()), - MainFileIndex(MainFileSymbols.buildMemIndex()), - MergedIndex(mergeIndex(&MainFileIndex, &PreambleIndex)) {} + MainFileIndex(MainFileSymbols.buildMemIndex()) {} void FileIndex::updatePreamble(PathRef Path, ASTContext &AST, std::shared_ptr PP) { diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index 7226e1668..ea24efd1e 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -19,6 +19,7 @@ #include "ClangdUnit.h" #include "Index.h" #include "MemIndex.h" +#include "Merge.h" #include "clang/Lex/Preprocessor.h" #include @@ -59,15 +60,12 @@ class FileSymbols { /// This manages symbols from files and an in-memory index on all symbols. /// FIXME: Expose an interface to remove files that are closed. -class FileIndex { +class FileIndex : public MergedIndex { public: /// If URISchemes is empty, the default schemes in SymbolCollector will be /// used. FileIndex(std::vector URISchemes = {}); - // Presents a merged view of the supplied main-file and preamble ASTs. - const SymbolIndex &index() const { return *MergedIndex; } - /// Update preamble symbols of file \p Path with all declarations in \p AST /// and macros in \p PP. void updatePreamble(PathRef Path, ASTContext &AST, @@ -102,8 +100,6 @@ class FileIndex { // (Note that symbols *only* in the main file are not indexed). FileSymbols MainFileSymbols; SwapIndex MainFileIndex; - - std::unique_ptr MergedIndex; // Merge preamble and main index. }; /// Retrieves symbols and refs of local top level decls in \p AST (i.e. diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index 9cca9da22..1dc2c9319 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -17,111 +17,96 @@ namespace clang { namespace clangd { -namespace { using namespace llvm; -class MergedIndex : public SymbolIndex { - public: - MergedIndex(const SymbolIndex *Dynamic, const SymbolIndex *Static) - : Dynamic(Dynamic), Static(Static) {} - - // FIXME: Deleted symbols in dirty files are still returned (from Static). - // To identify these eliminate these, we should: - // - find the generating file from each Symbol which is Static-only - // - ask Dynamic if it has that file (needs new SymbolIndex method) - // - if so, drop the Symbol. - bool fuzzyFind(const FuzzyFindRequest &Req, - function_ref Callback) const override { - // We can't step through both sources in parallel. So: - // 1) query all dynamic symbols, slurping results into a slab - // 2) query the static symbols, for each one: - // a) if it's not in the dynamic slab, yield it directly - // b) if it's in the dynamic slab, merge it and yield the result - // 3) now yield all the dynamic symbols we haven't processed. - trace::Span Tracer("MergedIndex fuzzyFind"); - bool More = false; // We'll be incomplete if either source was. - SymbolSlab::Builder DynB; - unsigned DynamicCount = 0; - unsigned StaticCount = 0; - unsigned MergedCount = 0; - More |= Dynamic->fuzzyFind(Req, [&](const Symbol &S) { - ++DynamicCount; - DynB.insert(S); - }); - SymbolSlab Dyn = std::move(DynB).build(); - - DenseSet SeenDynamicSymbols; - More |= Static->fuzzyFind(Req, [&](const Symbol &S) { - auto DynS = Dyn.find(S.ID); - ++StaticCount; - if (DynS == Dyn.end()) - return Callback(S); - ++MergedCount; - SeenDynamicSymbols.insert(S.ID); - Callback(mergeSymbol(*DynS, S)); - }); - SPAN_ATTACH(Tracer, "dynamic", DynamicCount); - SPAN_ATTACH(Tracer, "static", StaticCount); - SPAN_ATTACH(Tracer, "merged", MergedCount); - for (const Symbol &S : Dyn) - if (!SeenDynamicSymbols.count(S.ID)) - Callback(S); - return More; - } - - void - lookup(const LookupRequest &Req, - llvm::function_ref Callback) const override { - trace::Span Tracer("MergedIndex lookup"); - SymbolSlab::Builder B; - - Dynamic->lookup(Req, [&](const Symbol &S) { B.insert(S); }); +// FIXME: Deleted symbols in dirty files are still returned (from Static). +// To identify these eliminate these, we should: +// - find the generating file from each Symbol which is Static-only +// - ask Dynamic if it has that file (needs new SymbolIndex method) +// - if so, drop the Symbol. +bool MergedIndex::fuzzyFind(const FuzzyFindRequest &Req, + function_ref Callback) const { + // We can't step through both sources in parallel. So: + // 1) query all dynamic symbols, slurping results into a slab + // 2) query the static symbols, for each one: + // a) if it's not in the dynamic slab, yield it directly + // b) if it's in the dynamic slab, merge it and yield the result + // 3) now yield all the dynamic symbols we haven't processed. + trace::Span Tracer("MergedIndex fuzzyFind"); + bool More = false; // We'll be incomplete if either source was. + SymbolSlab::Builder DynB; + unsigned DynamicCount = 0; + unsigned StaticCount = 0; + unsigned MergedCount = 0; + More |= Dynamic->fuzzyFind(Req, [&](const Symbol &S) { + ++DynamicCount; + DynB.insert(S); + }); + SymbolSlab Dyn = std::move(DynB).build(); + + DenseSet SeenDynamicSymbols; + More |= Static->fuzzyFind(Req, [&](const Symbol &S) { + auto DynS = Dyn.find(S.ID); + ++StaticCount; + if (DynS == Dyn.end()) + return Callback(S); + ++MergedCount; + SeenDynamicSymbols.insert(S.ID); + Callback(mergeSymbol(*DynS, S)); + }); + SPAN_ATTACH(Tracer, "dynamic", DynamicCount); + SPAN_ATTACH(Tracer, "static", StaticCount); + SPAN_ATTACH(Tracer, "merged", MergedCount); + for (const Symbol &S : Dyn) + if (!SeenDynamicSymbols.count(S.ID)) + Callback(S); + return More; +} - auto RemainingIDs = Req.IDs; - Static->lookup(Req, [&](const Symbol &S) { - const Symbol *Sym = B.find(S.ID); - RemainingIDs.erase(S.ID); - if (!Sym) - Callback(S); - else - Callback(mergeSymbol(*Sym, S)); - }); - for (const auto &ID : RemainingIDs) - if (const Symbol *Sym = B.find(ID)) - Callback(*Sym); - } +void MergedIndex::lookup( + const LookupRequest &Req, + llvm::function_ref Callback) const { + trace::Span Tracer("MergedIndex lookup"); + SymbolSlab::Builder B; + + Dynamic->lookup(Req, [&](const Symbol &S) { B.insert(S); }); + + auto RemainingIDs = Req.IDs; + Static->lookup(Req, [&](const Symbol &S) { + const Symbol *Sym = B.find(S.ID); + RemainingIDs.erase(S.ID); + if (!Sym) + Callback(S); + else + Callback(mergeSymbol(*Sym, S)); + }); + for (const auto &ID : RemainingIDs) + if (const Symbol *Sym = B.find(ID)) + Callback(*Sym); +} - void refs(const RefsRequest &Req, - llvm::function_ref Callback) const override { - trace::Span Tracer("MergedIndex refs"); - // We don't want duplicated refs from the static/dynamic indexes, - // and we can't reliably duplicate them because offsets may differ slightly. - // We consider the dynamic index authoritative and report all its refs, - // and only report static index refs from other files. - // - // FIXME: The heuristic fails if the dynamic index contains a file, but all - // refs were removed (we will report stale ones from the static index). - // Ultimately we should explicit check which index has the file instead. - llvm::StringSet<> DynamicIndexFileURIs; - Dynamic->refs(Req, [&](const Ref &O) { - DynamicIndexFileURIs.insert(O.Location.FileURI); +void MergedIndex::refs(const RefsRequest &Req, + llvm::function_ref Callback) const { + trace::Span Tracer("MergedIndex refs"); + // We don't want duplicated refs from the static/dynamic indexes, + // and we can't reliably duplicate them because offsets may differ slightly. + // We consider the dynamic index authoritative and report all its refs, + // and only report static index refs from other files. + // + // FIXME: The heuristic fails if the dynamic index contains a file, but all + // refs were removed (we will report stale ones from the static index). + // Ultimately we should explicit check which index has the file instead. + llvm::StringSet<> DynamicIndexFileURIs; + Dynamic->refs(Req, [&](const Ref &O) { + DynamicIndexFileURIs.insert(O.Location.FileURI); + Callback(O); + }); + Static->refs(Req, [&](const Ref &O) { + if (!DynamicIndexFileURIs.count(O.Location.FileURI)) Callback(O); - }); - Static->refs(Req, [&](const Ref &O) { - if (!DynamicIndexFileURIs.count(O.Location.FileURI)) - Callback(O); - }); - } - - size_t estimateMemoryUsage() const override { - return Dynamic->estimateMemoryUsage() + Static->estimateMemoryUsage(); - } - -private: - const SymbolIndex *Dynamic, *Static; -}; -} // namespace + }); +} Symbol mergeSymbol(const Symbol &L, const Symbol &R) { assert(L.ID == R.ID); @@ -169,10 +154,5 @@ Symbol mergeSymbol(const Symbol &L, const Symbol &R) { return S; } -std::unique_ptr mergeIndex(const SymbolIndex *Dynamic, - const SymbolIndex *Static) { - return llvm::make_unique(Dynamic, Static); -} - } // namespace clangd } // namespace clang diff --git a/clangd/index/Merge.h b/clangd/index/Merge.h index ca496c2bf..7569c7a48 100644 --- a/clangd/index/Merge.h +++ b/clangd/index/Merge.h @@ -20,7 +20,7 @@ namespace clangd { // Returned symbol may contain data owned by either source. Symbol mergeSymbol(const Symbol &L, const Symbol &R); -// mergeIndex returns a composite index based on two provided Indexes: +// MergedIndex is a composite index based on two provided Indexes: // - the Dynamic index covers few files, but is relatively up-to-date. // - the Static index covers a bigger set of files, but is relatively stale. // The returned index attempts to combine results, and avoid duplicates. @@ -28,8 +28,25 @@ Symbol mergeSymbol(const Symbol &L, const Symbol &R); // FIXME: We don't have a mechanism in Index to track deleted symbols and // refs in dirty files, so the merged index may return stale symbols // and refs from Static index. -std::unique_ptr mergeIndex(const SymbolIndex *Dynamic, - const SymbolIndex *Static); +class MergedIndex : public SymbolIndex { + const SymbolIndex *Dynamic, *Static; + +public: + // The constructor does not access the symbols. + // It's safe to inherit from this class and pass pointers to derived members. + MergedIndex(const SymbolIndex *Dynamic, const SymbolIndex *Static) + : Dynamic(Dynamic), Static(Static) {} + + bool fuzzyFind(const FuzzyFindRequest &, + llvm::function_ref) const override; + void lookup(const LookupRequest &, + llvm::function_ref) const override; + void refs(const RefsRequest &, + llvm::function_ref) const override; + size_t estimateMemoryUsage() const override { + return Dynamic->estimateMemoryUsage() + Static->estimateMemoryUsage(); + } +}; } // namespace clangd } // namespace clang diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index d1858c5ff..f526ec71c 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -120,10 +120,10 @@ TEST(FileSymbolsTest, SnapshotAliveAfterRemove) { EXPECT_THAT(getRefs(*Symbols, ID), RefsAre({FileURI("f1.cc")})); } -std::vector match(const FileIndex &I, +std::vector match(const SymbolIndex &I, const FuzzyFindRequest &Req) { std::vector Matches; - I.index().fuzzyFind(Req, [&](const Symbol &Sym) { + I.fuzzyFind(Req, [&](const Symbol &Sym) { Matches.push_back((Sym.Scope + Sym.Name).str()); }); return Matches; @@ -147,7 +147,7 @@ TEST(FileIndexTest, CustomizedURIScheme) { FuzzyFindRequest Req; Req.Query = ""; bool SeenSymbol = false; - M.index().fuzzyFind(Req, [&](const Symbol &Sym) { + M.fuzzyFind(Req, [&](const Symbol &Sym) { EXPECT_EQ(Sym.CanonicalDeclaration.FileURI, "unittest:///f.h"); SeenSymbol = true; }); @@ -201,7 +201,7 @@ TEST(FileIndexTest, NoIncludeCollected) { FuzzyFindRequest Req; Req.Query = ""; bool SeenSymbol = false; - M.index().fuzzyFind(Req, [&](const Symbol &Sym) { + M.fuzzyFind(Req, [&](const Symbol &Sym) { EXPECT_TRUE(Sym.IncludeHeaders.empty()); SeenSymbol = true; }); @@ -225,7 +225,7 @@ vector make_vector(Arg A) {} Req.Query = ""; bool SeenVector = false; bool SeenMakeVector = false; - M.index().fuzzyFind(Req, [&](const Symbol &Sym) { + M.fuzzyFind(Req, [&](const Symbol &Sym) { if (Sym.Name == "vector") { EXPECT_EQ(Sym.Signature, ""); EXPECT_EQ(Sym.CompletionSnippetSuffix, "<${1:class Ty}>"); @@ -324,7 +324,7 @@ TEST(FileIndexTest, Refs) { AST = Test2.build(); Index.updateMain(Test2.Filename, AST); - EXPECT_THAT(getRefs(Index.index(), Foo.ID), + EXPECT_THAT(getRefs(Index, Foo.ID), RefsAre({AllOf(RefRange(MainCode.range("foo")), FileURI("unittest:///test.cc")), AllOf(RefRange(MainCode.range("foo")), @@ -338,7 +338,7 @@ TEST(FileIndexTest, CollectMacros) { FuzzyFindRequest Req; Req.Query = ""; bool SeenSymbol = false; - M.index().fuzzyFind(Req, [&](const Symbol &Sym) { + M.fuzzyFind(Req, [&](const Symbol &Sym) { EXPECT_EQ(Sym.Name, "CLANGD"); EXPECT_EQ(Sym.SymInfo.Kind, index::SymbolKind::Macro); SeenSymbol = true; diff --git a/unittests/clangd/IndexTests.cpp b/unittests/clangd/IndexTests.cpp index b774742c3..7279291fc 100644 --- a/unittests/clangd/IndexTests.cpp +++ b/unittests/clangd/IndexTests.cpp @@ -161,16 +161,16 @@ TEST(MemIndexTest, Lookup) { TEST(MergeIndexTest, Lookup) { auto I = MemIndex::build(generateSymbols({"ns::A", "ns::B"}), RefSlab()), J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), RefSlab()); - auto M = mergeIndex(I.get(), J.get()); - EXPECT_THAT(lookup(*M, SymbolID("ns::A")), UnorderedElementsAre("ns::A")); - EXPECT_THAT(lookup(*M, SymbolID("ns::B")), UnorderedElementsAre("ns::B")); - EXPECT_THAT(lookup(*M, SymbolID("ns::C")), UnorderedElementsAre("ns::C")); - EXPECT_THAT(lookup(*M, {SymbolID("ns::A"), SymbolID("ns::B")}), + MergedIndex M(I.get(), J.get()); + EXPECT_THAT(lookup(M, SymbolID("ns::A")), UnorderedElementsAre("ns::A")); + EXPECT_THAT(lookup(M, SymbolID("ns::B")), UnorderedElementsAre("ns::B")); + EXPECT_THAT(lookup(M, SymbolID("ns::C")), UnorderedElementsAre("ns::C")); + EXPECT_THAT(lookup(M, {SymbolID("ns::A"), SymbolID("ns::B")}), UnorderedElementsAre("ns::A", "ns::B")); - EXPECT_THAT(lookup(*M, {SymbolID("ns::A"), SymbolID("ns::C")}), + EXPECT_THAT(lookup(M, {SymbolID("ns::A"), SymbolID("ns::C")}), UnorderedElementsAre("ns::A", "ns::C")); - EXPECT_THAT(lookup(*M, SymbolID("ns::D")), UnorderedElementsAre()); - EXPECT_THAT(lookup(*M, {}), UnorderedElementsAre()); + EXPECT_THAT(lookup(M, SymbolID("ns::D")), UnorderedElementsAre()); + EXPECT_THAT(lookup(M, {}), UnorderedElementsAre()); } TEST(MergeIndexTest, FuzzyFind) { @@ -178,7 +178,7 @@ TEST(MergeIndexTest, FuzzyFind) { J = MemIndex::build(generateSymbols({"ns::B", "ns::C"}), RefSlab()); FuzzyFindRequest Req; Req.Scopes = {"ns::"}; - EXPECT_THAT(match(*mergeIndex(I.get(), J.get()), Req), + EXPECT_THAT(match(MergedIndex(I.get(), J.get()), Req), UnorderedElementsAre("ns::A", "ns::B", "ns::C")); } @@ -231,7 +231,7 @@ TEST(MergeTest, PreferSymbolWithDefn) { TEST(MergeIndexTest, Refs) { FileIndex Dyn({"unittest"}); FileIndex StaticIndex({"unittest"}); - auto MergedIndex = mergeIndex(&Dyn.index(), &StaticIndex.index()); + MergedIndex Merge(&Dyn, &StaticIndex); const char *HeaderCode = "class Foo;"; auto HeaderSymbols = TestTU::withHeaderCode("class Foo;").headerSymbols(); @@ -266,7 +266,7 @@ TEST(MergeIndexTest, Refs) { RefsRequest Request; Request.IDs = {Foo.ID}; RefSlab::Builder Results; - MergedIndex->refs(Request, [&](const Ref &O) { Results.insert(Foo.ID, O); }); + Merge.refs(Request, [&](const Ref &O) { Results.insert(Foo.ID, O); }); EXPECT_THAT( std::move(Results).build(), diff --git a/unittests/clangd/TestTU.cpp b/unittests/clangd/TestTU.cpp index 4b610c8f9..80e1ce4ff 100644 --- a/unittests/clangd/TestTU.cpp +++ b/unittests/clangd/TestTU.cpp @@ -48,11 +48,12 @@ SymbolSlab TestTU::headerSymbols() const { return indexHeaderSymbols(AST.getASTContext(), AST.getPreprocessorPtr()); } -// FIXME: This should return a FileIndex with both preamble and main index. std::unique_ptr TestTU::index() const { auto AST = build(); - auto Content = indexMainDecls(AST); - return MemIndex::build(std::move(Content.first), std::move(Content.second)); + auto Idx = llvm::make_unique(); + Idx->updatePreamble(Filename, AST.getASTContext(), AST.getPreprocessorPtr()); + Idx->updateMain(Filename, AST); + return Idx; } const Symbol &findSymbol(const SymbolSlab &Slab, llvm::StringRef QName) { From 29c9ca65c6920b4ea2cc4ed50fd1587865ed9fb1 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Thu, 4 Oct 2018 15:47:57 +0000 Subject: [PATCH 306/686] [clang-tidy] Added pointer types to clang-tidy readability-identifier-naming check. Summary: Option to check for different naming conventions on the following types: - GlobalConstantPointer - GlobalPointer - LocalConstantPointer - LocalPointer - PointerParameter - ConstantPointerParameter When not specified, the conventions for the non pointer types will be applied (GlobalConstant, GlobalVariable, LocalConstant, ...). Patch by ffigueras! Reviewers: alexfh, kbobyrev Reviewed By: alexfh Subscribers: xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D52882 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343788 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../readability/IdentifierNamingCheck.cpp | 24 ++++++++++++++ .../readability-identifier-naming.cpp | 33 +++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tidy/readability/IdentifierNamingCheck.cpp index e34fcbb3a..1d1168179 100644 --- a/clang-tidy/readability/IdentifierNamingCheck.cpp +++ b/clang-tidy/readability/IdentifierNamingCheck.cpp @@ -77,8 +77,12 @@ namespace readability { m(ClassConstant) \ m(ClassMember) \ m(GlobalConstant) \ + m(GlobalConstantPointer) \ + m(GlobalPointer) \ m(GlobalVariable) \ m(LocalConstant) \ + m(LocalConstantPointer) \ + m(LocalPointer) \ m(LocalVariable) \ m(StaticConstant) \ m(StaticVariable) \ @@ -87,6 +91,8 @@ namespace readability { m(ConstantParameter) \ m(ParameterPack) \ m(Parameter) \ + m(PointerParameter) \ + m(ConstantPointerParameter) \ m(AbstractClass) \ m(Struct) \ m(Class) \ @@ -486,6 +492,9 @@ static StyleKind findStyleKind( return SK_ConstexprVariable; if (!Type.isNull() && Type.isConstQualified()) { + if (Type.getTypePtr()->isAnyPointerType() && NamingStyles[SK_ConstantPointerParameter]) + return SK_ConstantPointerParameter; + if (NamingStyles[SK_ConstantParameter]) return SK_ConstantParameter; @@ -496,6 +505,9 @@ static StyleKind findStyleKind( if (Decl->isParameterPack() && NamingStyles[SK_ParameterPack]) return SK_ParameterPack; + if (!Type.isNull() && Type.getTypePtr()->isAnyPointerType() && NamingStyles[SK_PointerParameter]) + return SK_PointerParameter; + if (NamingStyles[SK_Parameter]) return SK_Parameter; @@ -512,12 +524,18 @@ static StyleKind findStyleKind( if (Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant]) return SK_ClassConstant; + if (Decl->isFileVarDecl() && Type.getTypePtr()->isAnyPointerType() && NamingStyles[SK_GlobalConstantPointer]) + return SK_GlobalConstantPointer; + if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant]) return SK_GlobalConstant; if (Decl->isStaticLocal() && NamingStyles[SK_StaticConstant]) return SK_StaticConstant; + if (Decl->isLocalVarDecl() && Type.getTypePtr()->isAnyPointerType() && NamingStyles[SK_LocalConstantPointer]) + return SK_LocalConstantPointer; + if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant]) return SK_LocalConstant; @@ -531,11 +549,17 @@ static StyleKind findStyleKind( if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember]) return SK_ClassMember; + if (Decl->isFileVarDecl() && Type.getTypePtr()->isAnyPointerType() && NamingStyles[SK_GlobalPointer]) + return SK_GlobalPointer; + if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable]) return SK_GlobalVariable; if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable]) return SK_StaticVariable; + + if (Decl->isLocalVarDecl() && Type.getTypePtr()->isAnyPointerType() && NamingStyles[SK_LocalPointer]) + return SK_LocalPointer; if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable]) return SK_LocalVariable; diff --git a/test/clang-tidy/readability-identifier-naming.cpp b/test/clang-tidy/readability-identifier-naming.cpp index 436e8443c..211a28bcc 100644 --- a/test/clang-tidy/readability-identifier-naming.cpp +++ b/test/clang-tidy/readability-identifier-naming.cpp @@ -67,7 +67,19 @@ // RUN: {key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE}, \ // RUN: {key: readability-identifier-naming.TypeAliasCase, value: camel_Snake_Back}, \ // RUN: {key: readability-identifier-naming.TypeAliasSuffix, value: '_t'}, \ -// RUN: {key: readability-identifier-naming.IgnoreFailedSplit, value: 0} \ +// RUN: {key: readability-identifier-naming.IgnoreFailedSplit, value: 0}, \ +// RUN: {key: readability-identifier-naming.GlobalPointerCase, value: CamelCase}, \ +// RUN: {key: readability-identifier-naming.GlobalPointerSuffix, value: '_Ptr'}, \ +// RUN: {key: readability-identifier-naming.GlobalConstantPointerCase, value: UPPER_CASE}, \ +// RUN: {key: readability-identifier-naming.GlobalConstantPointerSuffix, value: '_Ptr'}, \ +// RUN: {key: readability-identifier-naming.PointerParameterCase, value: lower_case}, \ +// RUN: {key: readability-identifier-naming.PointerParameterPrefix, value: 'p_'}, \ +// RUN: {key: readability-identifier-naming.ConstantPointerParameterCase, value: CamelCase}, \ +// RUN: {key: readability-identifier-naming.ConstantPointerParameterPrefix, value: 'cp_'}, \ +// RUN: {key: readability-identifier-naming.LocalPointerCase, value: CamelCase}, \ +// RUN: {key: readability-identifier-naming.LocalPointerPrefix, value: 'l_'}, \ +// RUN: {key: readability-identifier-naming.LocalConstantPointerCase, value: CamelCase}, \ +// RUN: {key: readability-identifier-naming.LocalConstantPointerPrefix, value: 'lc_'}, \ // RUN: ]}' -- -std=c++11 -fno-delayed-template-parsing \ // RUN: -I%S/Inputs/readability-identifier-naming \ // RUN: -isystem %S/Inputs/readability-identifier-naming/system @@ -235,8 +247,8 @@ class CMyWellNamedClass2 : public my_class { CMyWellNamedClass2() = default; CMyWellNamedClass2(CMyWellNamedClass2 const&) = default; CMyWellNamedClass2(CMyWellNamedClass2 &&) = default; - CMyWellNamedClass2(t_t a_v, void *a_p) : my_class(a_p), my_Bad_Member(a_v) {} - // CHECK-FIXES: {{^}} CMyWellNamedClass2(t_t a_v, void *a_p) : CMyClass(a_p), __my_Bad_Member(a_v) {}{{$}} + CMyWellNamedClass2(t_t a_v, void *p_p) : my_class(p_p), my_Bad_Member(a_v) {} + // CHECK-FIXES: {{^}} CMyWellNamedClass2(t_t a_v, void *p_p) : CMyClass(p_p), __my_Bad_Member(a_v) {}{{$}} CMyWellNamedClass2(t_t a_v) : my_class(), my_Bad_Member(a_v), my_Other_Bad_Member(11) {} // CHECK-FIXES: {{^}} CMyWellNamedClass2(t_t a_v) : CMyClass(), __my_Bad_Member(a_v), __my_Other_Bad_Member(11) {}{{$}} @@ -474,3 +486,18 @@ unsigned MY_GLOBAL_array[] = {1,2,3}; unsigned const MyConstGlobal_array[] = {1,2,3}; // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: invalid case style for global constant 'MyConstGlobal_array' // CHECK-FIXES: {{^}}unsigned const MY_CONST_GLOBAL_ARRAY[] = {1,2,3};{{$}} + +int * MyGlobal_Ptr;// -> ok +int * const MyConstantGlobalPointer = nullptr; +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: invalid case style for global constant pointer 'MyConstantGlobalPointer' +// CHECK-FIXES: {{^}}int * const MY_CONSTANT_GLOBAL_POINTER_Ptr = nullptr;{{$}} + +void MyPoiterFunction(int * p_normal_pointer, int * const constant_ptr){ +// CHECK-MESSAGES: :[[@LINE-1]]:59: warning: invalid case style for constant pointer parameter 'constant_ptr' +// CHECK-FIXES: {{^}}void MyPoiterFunction(int * p_normal_pointer, int * const cp_ConstantPtr){{{$}} + int * l_PointerA; + int * const pointer_b = nullptr; +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: invalid case style for local constant pointer 'pointer_b' +// CHECK-FIXES: {{^}} int * const lc_PointerB = nullptr;{{$}} +} + From 93953d46c25637eafaf8e1a8450f6274caa101eb Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Thu, 4 Oct 2018 15:49:25 +0000 Subject: [PATCH 307/686] [clang-tidy] fix PR39167, bugprone-exception-escape hangs-up Summary: The check bugprone-exception-escape should not register if -fno-exceptions is set for the compile options. Bailing out on non-cplusplus and non-exceptions language options resolves the issue. Reviewers: alexfh, aaron.ballman, baloghadamsoftware Reviewed By: alexfh Subscribers: lebedev.ri, xazax.hun, rnkovacs, cfe-commits Differential Revision: https://reviews.llvm.org/D52880 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343789 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/bugprone/ExceptionEscapeCheck.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang-tidy/bugprone/ExceptionEscapeCheck.cpp b/clang-tidy/bugprone/ExceptionEscapeCheck.cpp index a5e54c0a1..6d1f7a782 100644 --- a/clang-tidy/bugprone/ExceptionEscapeCheck.cpp +++ b/clang-tidy/bugprone/ExceptionEscapeCheck.cpp @@ -186,6 +186,9 @@ void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { } void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus || !getLangOpts().CXXExceptions) + return; + Finder->addMatcher( functionDecl(allOf(throws(unless(isIgnored(IgnoredExceptions))), anyOf(isNoThrow(), cxxDestructorDecl(), From f5ecd31649c114d12962d2778d134d7201475bfc Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Thu, 4 Oct 2018 15:55:37 +0000 Subject: [PATCH 308/686] [clang-tidy] NFC use CHECK-NOTES in tests for performance-move-constructor-init Reviewers: alexfh, aaron.ballman, hokein Reviewed By: alexfh Subscribers: lebedev.ri, xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D52691 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343791 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clang-tidy/check_clang_tidy.py | 4 +++- .../clang-tidy/performance-move-constructor-init.cpp | 12 +++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/test/clang-tidy/check_clang_tidy.py b/test/clang-tidy/check_clang_tidy.py index d8086b16e..46fe0f6c8 100755 --- a/test/clang-tidy/check_clang_tidy.py +++ b/test/clang-tidy/check_clang_tidy.py @@ -164,7 +164,9 @@ def main(): if has_check_notes: notes_file = temp_file_name + '.notes' - write_file(notes_file, clang_tidy_output) + filtered_output = [line for line in clang_tidy_output.splitlines() + if not "note: FIX-IT applied suggested changes" in line] + write_file(notes_file, '\n'.join(filtered_output)) try: subprocess.check_output( ['FileCheck', '-input-file=' + notes_file, input_file_name, diff --git a/test/clang-tidy/performance-move-constructor-init.cpp b/test/clang-tidy/performance-move-constructor-init.cpp index c9ed34698..09108fcca 100644 --- a/test/clang-tidy/performance-move-constructor-init.cpp +++ b/test/clang-tidy/performance-move-constructor-init.cpp @@ -30,9 +30,9 @@ struct B { struct D : B { D() : B() {} D(const D &RHS) : B(RHS) {} - // CHECK-MESSAGES: :[[@LINE+3]]:16: warning: move constructor initializes base class by calling a copy constructor [performance-move-constructor-init] - // CHECK-MESSAGES: 26:3: note: copy constructor being called - // CHECK-MESSAGES: 27:3: note: candidate move constructor here + // CHECK-NOTES: :[[@LINE+3]]:16: warning: move constructor initializes base class by calling a copy constructor [performance-move-constructor-init] + // CHECK-NOTES: 26:3: note: copy constructor being called + // CHECK-NOTES: 27:3: note: candidate move constructor here D(D &&RHS) : B(RHS) {} }; @@ -75,8 +75,10 @@ struct L : K { struct M { B Mem; - // CHECK-MESSAGES: :[[@LINE+1]]:16: warning: move constructor initializes class member by calling a copy constructor [performance-move-constructor-init] + // CHECK-NOTES: :[[@LINE+1]]:16: warning: move constructor initializes class member by calling a copy constructor [performance-move-constructor-init] M(M &&RHS) : Mem(RHS.Mem) {} + // CHECK-NOTES: 26:3: note: copy constructor being called + // CHECK-NOTES: 27:3: note: candidate move constructor here }; struct N { @@ -109,7 +111,7 @@ struct TriviallyCopyable { struct Positive { Positive(Movable M) : M_(M) {} - // CHECK-MESSAGES: [[@LINE-1]]:12: warning: pass by value and use std::move [modernize-pass-by-value] + // CHECK-NOTES: [[@LINE-1]]:12: warning: pass by value and use std::move [modernize-pass-by-value] // CHECK-FIXES: Positive(Movable M) : M_(std::move(M)) {} Movable M_; }; From 798322f5d6b995d786b430e95ab553a43a8aaab0 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Thu, 4 Oct 2018 15:59:30 +0000 Subject: [PATCH 309/686] [clang-tidy] NFC use CHECK-NOTES in tests for fuchsia-default-arguments Reviewers: alexfh, aaron.ballman, hokein Reviewed By: alexfh Subscribers: xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D52688 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343792 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clang-tidy/fuchsia-default-arguments.cpp | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/test/clang-tidy/fuchsia-default-arguments.cpp b/test/clang-tidy/fuchsia-default-arguments.cpp index f7b1ae695..ce745651d 100644 --- a/test/clang-tidy/fuchsia-default-arguments.cpp +++ b/test/clang-tidy/fuchsia-default-arguments.cpp @@ -1,14 +1,13 @@ // RUN: %check_clang_tidy %s fuchsia-default-arguments %t int foo(int value = 5) { return value; } -// CHECK-MESSAGES: [[@LINE-1]]:9: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] +// CHECK-NOTES: [[@LINE-1]]:9: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] // CHECK-FIXES: int foo(int value) { return value; } int f() { foo(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling a function that uses a default argument is disallowed [fuchsia-default-arguments] - // CHECK-NEXT: note: default parameter was declared here: - // CHECK-NEXT: int foo(int value = 5) { return value; } + // CHECK-NOTES: [[@LINE-1]]:3: warning: calling a function that uses a default argument is disallowed [fuchsia-default-arguments] + // CHECK-NOTES: [[@LINE-8]]:9: note: default parameter was declared here } int bar(int value) { return value; } @@ -21,7 +20,7 @@ int n() { class Baz { public: int a(int value = 5) { return value; } - // CHECK-MESSAGES: [[@LINE-1]]:9: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] + // CHECK-NOTES: [[@LINE-1]]:9: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] // CHECK-FIXES: int a(int value) { return value; } int b(int value) { return value; } @@ -30,7 +29,7 @@ class Baz { class Foo { // Fix should be suggested in declaration int a(int value = 53); - // CHECK-MESSAGES: [[@LINE-1]]:9: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] + // CHECK-NOTES: [[@LINE-1]]:9: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] // CHECK-FIXES: int a(int value); }; @@ -41,7 +40,7 @@ int Foo::a(int value) { // Elided functions void f(int = 5) {}; -// CHECK-MESSAGES: [[@LINE-1]]:8: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] +// CHECK-NOTES: [[@LINE-1]]:8: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] // CHECK-FIXES: void f(int) {}; void g(int) {}; @@ -50,12 +49,12 @@ void g(int) {}; #define D(val) = val void h(int i D(5)); -// CHECK-MESSAGES: [[@LINE-1]]:8: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] +// CHECK-NOTES: [[@LINE-1]]:8: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] // CHECK-FIXES-NOT: void h(int i); void x(int i); void x(int i = 12); -// CHECK-MESSAGES: [[@LINE-1]]:8: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] +// CHECK-NOTES: [[@LINE-1]]:8: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] // CHECK-FIXES: void x(int i); void x(int i) {} @@ -65,17 +64,17 @@ struct S { }; void S::x(int i = 12) {} -// CHECK-MESSAGES: [[@LINE-1]]:11: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] +// CHECK-NOTES: [[@LINE-1]]:11: warning: declaring a parameter with a default argument is disallowed [fuchsia-default-arguments] // CHECK-FIXES: void S::x(int i) {} int main() { S s; s.x(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling a function that uses a default argument is disallowed [fuchsia-default-arguments] - // CHECK-NEXT: note: default parameter was declared here: + // CHECK-NOTES: [[@LINE-1]]:3: warning: calling a function that uses a default argument is disallowed [fuchsia-default-arguments] + // CHECK-NOTES: [[@LINE-9]]:11: note: default parameter was declared here // CHECK-NEXT: void S::x(int i = 12) {} x(); - // CHECK-MESSAGES: [[@LINE-1]]:3: warning: calling a function that uses a default argument is disallowed [fuchsia-default-arguments] - // CHECK-NEXT: note: default parameter was declared here: + // CHECK-NOTES: [[@LINE-1]]:3: warning: calling a function that uses a default argument is disallowed [fuchsia-default-arguments] + // CHECK-NOTES: [[@LINE-19]]:8: note: default parameter was declared here // CHECK-NEXT: void x(int i = 12); } From 4a18d4258815cd9058b768ed2602b6ed1ee6ac99 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 4 Oct 2018 16:05:22 +0000 Subject: [PATCH 310/686] [clangd] Fix ambiguous constructor in DexTest git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343793 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/DexTests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index d7223ab19..02eb9a283 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -111,8 +111,8 @@ TEST(DexIterators, AndThreeLists) { TEST(DexIterators, AndEmpty) { Corpus C{10000}; - const PostingList L1({1}); - const PostingList L2({2}); + const PostingList L1{1}; + const PostingList L2{2}; // These iterators are empty, but the optimizer can't tell. auto Empty1 = C.intersect(L1.iterator(), L2.iterator()); auto Empty2 = C.intersect(L1.iterator(), L2.iterator()); From 6a6784cb6e47fb42be625c0ae01c58742d508206 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Thu, 4 Oct 2018 16:29:58 +0000 Subject: [PATCH 311/686] [clangd] fix another ambigous constructor in DexTest git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343796 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/DexTests.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 02eb9a283..017ccbfa0 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -317,9 +317,9 @@ TEST(DexIterators, Boost) { TEST(DexIterators, Optimizations) { Corpus C{5}; - const PostingList L1({1}); - const PostingList L2({2}); - const PostingList L3({3}); + const PostingList L1{1}; + const PostingList L2{2}; + const PostingList L3{3}; // empty and/or yield true/false EXPECT_EQ(llvm::to_string(*C.intersect()), "true"); From 65d307b9f523f5d69f1efb7f8676f231d5e7ec53 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Thu, 4 Oct 2018 16:39:41 +0000 Subject: [PATCH 312/686] [clang-tidy] fix failing unit tests The removal from the FIX-IT notes through the check-clang-tidy script was done incorrect. I did not detect beforehand but adjusted the script and tests accordingly git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343797 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../bugprone-argument-comment-gmock.cpp | 43 ++++++++----------- .../bugprone-argument-comment-strict.cpp | 12 ++---- test/clang-tidy/bugprone-argument-comment.cpp | 9 ++-- test/clang-tidy/check_clang_tidy.py | 2 +- test/clang-tidy/fuchsia-default-arguments.cpp | 6 +-- 5 files changed, 30 insertions(+), 42 deletions(-) diff --git a/test/clang-tidy/bugprone-argument-comment-gmock.cpp b/test/clang-tidy/bugprone-argument-comment-gmock.cpp index 8b67a915b..95b1a0713 100644 --- a/test/clang-tidy/bugprone-argument-comment-gmock.cpp +++ b/test/clang-tidy/bugprone-argument-comment-gmock.cpp @@ -84,20 +84,18 @@ void test_gmock_expectations() { MockDerived m; EXPECT_CALL(m, Method(/*param_one=*/1, /*param_tw=*/2)); // CHECK-NOTES: [[@LINE-1]]:42: warning: argument name 'param_tw' in comment does not match parameter name 'param_two' -// CHECK-NOTES: [[@LINE-2]]:42: note: FIX-IT applied suggested code changes -// CHECK-NOTES: [[@LINE-19]]:42: note: 'param_two' declared here -// CHECK-NOTES: [[@LINE-15]]:3: note: actual callee ('gmock_Method') is declared here -// CHECK-NOTES: [[@LINE-33]]:30: note: expanded from macro 'MOCK_METHOD2' -// CHECK-NOTES: [[@LINE-36]]:7: note: expanded from macro 'GMOCK_METHOD2_' +// CHECK-NOTES: [[@LINE-18]]:42: note: 'param_two' declared here +// CHECK-NOTES: [[@LINE-14]]:3: note: actual callee ('gmock_Method') is declared here +// CHECK-NOTES: [[@LINE-32]]:30: note: expanded from macro 'MOCK_METHOD2' +// CHECK-NOTES: [[@LINE-35]]:7: note: expanded from macro 'GMOCK_METHOD2_' // CHECK-NOTES: note: expanded from here // CHECK-FIXES: EXPECT_CALL(m, Method(/*param_one=*/1, /*param_two=*/2)); EXPECT_CALL(m, Method2(/*p_on=*/3, /*p_two=*/4)); // CHECK-NOTES: [[@LINE-1]]:26: warning: argument name 'p_on' in comment does not match parameter name 'p_one' -// CHECK-NOTES: [[@LINE-2]]:26: note: FIX-IT applied suggested code changes -// CHECK-NOTES: [[@LINE-27]]:28: note: 'p_one' declared here -// CHECK-NOTES: [[@LINE-23]]:3: note: actual callee ('gmock_Method2') is declared here -// CHECK-NOTES: [[@LINE-41]]:36: note: expanded from macro 'MOCK_CONST_METHOD2' -// CHECK-NOTES: [[@LINE-45]]:7: note: expanded from macro 'GMOCK_METHOD2_' +// CHECK-NOTES: [[@LINE-25]]:28: note: 'p_one' declared here +// CHECK-NOTES: [[@LINE-21]]:3: note: actual callee ('gmock_Method2') is declared here +// CHECK-NOTES: [[@LINE-39]]:36: note: expanded from macro 'MOCK_CONST_METHOD2' +// CHECK-NOTES: [[@LINE-43]]:7: note: expanded from macro 'GMOCK_METHOD2_' // CHECK-NOTES: note: expanded from here // CHECK-FIXES: EXPECT_CALL(m, Method2(/*p_one=*/3, /*p_two=*/4)); @@ -105,18 +103,16 @@ void test_gmock_expectations() { #define PARAM2 22 EXPECT_CALL(m, Method2(/*p_on1=*/PARAM1, /*p_tw2=*/PARAM2)); // CHECK-NOTES: [[@LINE-1]]:26: warning: argument name 'p_on1' in comment does not match parameter name 'p_one' -// CHECK-NOTES: [[@LINE-2]]:26: note: FIX-IT applied suggested code changes -// CHECK-NOTES: [[@LINE-39]]:28: note: 'p_one' declared here -// CHECK-NOTES: [[@LINE-35]]:3: note: actual callee ('gmock_Method2') is declared here -// CHECK-NOTES: [[@LINE-53]]:36: note: expanded from macro 'MOCK_CONST_METHOD2' -// CHECK-NOTES: [[@LINE-57]]:7: note: expanded from macro 'GMOCK_METHOD2_' +// CHECK-NOTES: [[@LINE-36]]:28: note: 'p_one' declared here +// CHECK-NOTES: [[@LINE-32]]:3: note: actual callee ('gmock_Method2') is declared here +// CHECK-NOTES: [[@LINE-50]]:36: note: expanded from macro 'MOCK_CONST_METHOD2' +// CHECK-NOTES: [[@LINE-54]]:7: note: expanded from macro 'GMOCK_METHOD2_' // CHECK-NOTES: note: expanded from here -// CHECK-NOTES: [[@LINE-8]]:44: warning: argument name 'p_tw2' in comment does not match parameter name 'p_two' -// CHECK-NOTES: [[@LINE-9]]:44: note: FIX-IT applied suggested code changes -// CHECK-NOTES: [[@LINE-46]]:39: note: 'p_two' declared here -// CHECK-NOTES: [[@LINE-42]]:3: note: actual callee ('gmock_Method2') is declared here -// CHECK-NOTES: [[@LINE-60]]:36: note: expanded from macro 'MOCK_CONST_METHOD2' -// CHECK-NOTES: [[@LINE-64]]:7: note: expanded from macro 'GMOCK_METHOD2_' +// CHECK-NOTES: [[@LINE-7]]:44: warning: argument name 'p_tw2' in comment does not match parameter name 'p_two' +// CHECK-NOTES: [[@LINE-42]]:39: note: 'p_two' declared here +// CHECK-NOTES: [[@LINE-38]]:3: note: actual callee ('gmock_Method2') is declared here +// CHECK-NOTES: [[@LINE-56]]:36: note: expanded from macro 'MOCK_CONST_METHOD2' +// CHECK-NOTES: [[@LINE-60]]:7: note: expanded from macro 'GMOCK_METHOD2_' // CHECK-NOTES: note: expanded from here // CHECK-FIXES: EXPECT_CALL(m, Method2(/*p_one=*/PARAM1, /*p_two=*/PARAM2)); @@ -128,8 +124,7 @@ void test_gmock_direct_calls() { MockDerived m; m.Method(/*param_one=*/1, /*param_tw=*/2); // CHECK-NOTES: [[@LINE-1]]:29: warning: argument name 'param_tw' in comment does not match parameter name 'param_two' -// CHECK-NOTES: [[@LINE-2]]:29: note: FIX-IT applied suggested code changes -// CHECK-NOTES: [[@LINE-63]]:42: note: 'param_two' declared here -// CHECK-NOTES: [[@LINE-59]]:16: note: actual callee ('Method') is declared here +// CHECK-NOTES: [[@LINE-58]]:42: note: 'param_two' declared here +// CHECK-NOTES: [[@LINE-54]]:16: note: actual callee ('Method') is declared here // CHECK-FIXES: m.Method(/*param_one=*/1, /*param_two=*/2); } diff --git a/test/clang-tidy/bugprone-argument-comment-strict.cpp b/test/clang-tidy/bugprone-argument-comment-strict.cpp index 68f8da364..46e346fa6 100644 --- a/test/clang-tidy/bugprone-argument-comment-strict.cpp +++ b/test/clang-tidy/bugprone-argument-comment-strict.cpp @@ -6,23 +6,19 @@ void g(int x_); void ignores_underscores() { f(/*With_Underscores=*/0); // CHECK-NOTES: [[@LINE-1]]:5: warning: argument name 'With_Underscores' in comment does not match parameter name '_with_underscores_' -// CHECK-NOTES: [[@LINE-2]]:5: note: FIX-IT applied suggested code changes -// CHECK-NOTES: [[@LINE-6]]:12: note: '_with_underscores_' declared here +// CHECK-NOTES: [[@LINE-5]]:12: note: '_with_underscores_' declared here // CHECK-FIXES: f(/*_with_underscores_=*/0); f(/*with_underscores=*/1); // CHECK-NOTES: [[@LINE-1]]:5: warning: argument name 'with_underscores' in comment does not match parameter name '_with_underscores_' -// CHECK-NOTES: [[@LINE-2]]:5: note: FIX-IT applied suggested code changes -// CHECK-NOTES: [[@LINE-12]]:12: note: '_with_underscores_' declared here +// CHECK-NOTES: [[@LINE-10]]:12: note: '_with_underscores_' declared here // CHECK-FIXES: f(/*_with_underscores_=*/1); f(/*_With_Underscores_=*/2); // CHECK-NOTES: [[@LINE-1]]:5: warning: argument name '_With_Underscores_' in comment does not match parameter name '_with_underscores_' -// CHECK-NOTES: [[@LINE-2]]:5: note: FIX-IT applied suggested code changes -// CHECK-NOTES: [[@LINE-17]]:12: note: '_with_underscores_' declared here +// CHECK-NOTES: [[@LINE-14]]:12: note: '_with_underscores_' declared here // CHECK-FIXES: f(/*_with_underscores_=*/2); g(/*X=*/3); // CHECK-NOTES: [[@LINE-1]]:5: warning: argument name 'X' in comment does not match parameter name 'x_' -// CHECK-NOTES: [[@LINE-2]]:5: note: FIX-IT applied suggested code changes -// CHECK-NOTES: [[@LINE-21]]:12: note: 'x_' declared here +// CHECK-NOTES: [[@LINE-17]]:12: note: 'x_' declared here // CHECK-FIXES: g(/*x_=*/3); } diff --git a/test/clang-tidy/bugprone-argument-comment.cpp b/test/clang-tidy/bugprone-argument-comment.cpp index 53b9726f5..08f87170c 100644 --- a/test/clang-tidy/bugprone-argument-comment.cpp +++ b/test/clang-tidy/bugprone-argument-comment.cpp @@ -47,8 +47,7 @@ void templates() { variadic(/*xxx=*/0, /*yyy=*/1); variadic2(/*zzU=*/0, /*xxx=*/1, /*yyy=*/2); // CHECK-NOTES: [[@LINE-1]]:13: warning: argument name 'zzU' in comment does not match parameter name 'zzz' - // CHECK-NOTES: [[@LINE-2]]:13: note: FIX-IT applied suggested code changes - // CHECK-NOTES: :[[@LINE-7]]:20: note: 'zzz' declared here + // CHECK-NOTES: :[[@LINE-6]]:20: note: 'zzz' declared here // CHECK-FIXES: variadic2(/*zzz=*/0, /*xxx=*/1, /*yyy=*/2); } @@ -76,16 +75,14 @@ namespace OtherEditDistanceAboveThreshold { void f5(int xxx, int yyy); void g() { f5(/*Zxx=*/0, 0); } // CHECK-NOTES: [[@LINE-1]]:15: warning: argument name 'Zxx' in comment does not match parameter name 'xxx' -// CHECK-NOTES: [[@LINE-2]]:15: note: FIX-IT applied suggested code changes -// CHECK-NOTES: [[@LINE-4]]:13: note: 'xxx' declared here +// CHECK-NOTES: [[@LINE-3]]:13: note: 'xxx' declared here // CHECK-FIXES: void g() { f5(/*xxx=*/0, 0); } struct C2 { C2(int xxx, int yyy); }; C2 c2(/*Zxx=*/0, 0); // CHECK-NOTES: [[@LINE-1]]:7: warning: argument name 'Zxx' in comment does not match parameter name 'xxx' -// CHECK-NOTES: [[@LINE-2]]:7: note: FIX-IT applied suggested code changes -// CHECK-NOTES: [[@LINE-5]]:10: note: 'xxx' declared here +// CHECK-NOTES: [[@LINE-4]]:10: note: 'xxx' declared here // CHECK-FIXES: C2 c2(/*xxx=*/0, 0); } diff --git a/test/clang-tidy/check_clang_tidy.py b/test/clang-tidy/check_clang_tidy.py index 46fe0f6c8..d1cefba3c 100755 --- a/test/clang-tidy/check_clang_tidy.py +++ b/test/clang-tidy/check_clang_tidy.py @@ -165,7 +165,7 @@ def main(): if has_check_notes: notes_file = temp_file_name + '.notes' filtered_output = [line for line in clang_tidy_output.splitlines() - if not "note: FIX-IT applied suggested changes" in line] + if not "note: FIX-IT applied" in line] write_file(notes_file, '\n'.join(filtered_output)) try: subprocess.check_output( diff --git a/test/clang-tidy/fuchsia-default-arguments.cpp b/test/clang-tidy/fuchsia-default-arguments.cpp index ce745651d..23f269ad1 100644 --- a/test/clang-tidy/fuchsia-default-arguments.cpp +++ b/test/clang-tidy/fuchsia-default-arguments.cpp @@ -7,7 +7,7 @@ int foo(int value = 5) { return value; } int f() { foo(); // CHECK-NOTES: [[@LINE-1]]:3: warning: calling a function that uses a default argument is disallowed [fuchsia-default-arguments] - // CHECK-NOTES: [[@LINE-8]]:9: note: default parameter was declared here + // CHECK-NOTES: [[@LINE-7]]:9: note: default parameter was declared here } int bar(int value) { return value; } @@ -71,10 +71,10 @@ int main() { S s; s.x(); // CHECK-NOTES: [[@LINE-1]]:3: warning: calling a function that uses a default argument is disallowed [fuchsia-default-arguments] - // CHECK-NOTES: [[@LINE-9]]:11: note: default parameter was declared here + // CHECK-NOTES: [[@LINE-8]]:11: note: default parameter was declared here // CHECK-NEXT: void S::x(int i = 12) {} x(); // CHECK-NOTES: [[@LINE-1]]:3: warning: calling a function that uses a default argument is disallowed [fuchsia-default-arguments] - // CHECK-NOTES: [[@LINE-19]]:8: note: default parameter was declared here + // CHECK-NOTES: [[@LINE-18]]:8: note: default parameter was declared here // CHECK-NEXT: void x(int i = 12); } From 959ecc344364ce3447e161a15576e466de1123d8 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 4 Oct 2018 17:15:41 +0000 Subject: [PATCH 313/686] [clangd] Add std::move for converting-return to satisfy older compilers git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343800 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/TestTU.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/clangd/TestTU.cpp b/unittests/clangd/TestTU.cpp index 80e1ce4ff..f63b738c9 100644 --- a/unittests/clangd/TestTU.cpp +++ b/unittests/clangd/TestTU.cpp @@ -53,7 +53,7 @@ std::unique_ptr TestTU::index() const { auto Idx = llvm::make_unique(); Idx->updatePreamble(Filename, AST.getASTContext(), AST.getPreprocessorPtr()); Idx->updateMain(Filename, AST); - return Idx; + return std::move(Idx); } const Symbol &findSymbol(const SymbolSlab &Slab, llvm::StringRef QName) { From 95baa85493a799f4f953133f3b4dc93cd5d9c7d9 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 4 Oct 2018 17:18:49 +0000 Subject: [PATCH 314/686] [clangd] Dex: FALSE iterator, peephole optimizations, fix AND bug Summary: The FALSE iterator will be used in a followup patch to fix a logic bug in Dex (currently, tokens that don't have posting lists in the index are simply dropped from the query, changing semantics). It can usually be optimized away, so added the following opmitizations: - simplify booleans inside AND/OR - replace effectively-empty AND/OR with booleans - flatten nested AND/ORs While working on this, found a bug in the AND iterator: its constructor sync() assumes that ReachedEnd is set if applicable, but the constructor never sets it. This crashes if a non-first iterator is nonempty. Reviewers: ilya-biryukov Subscribers: ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52789 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343801 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Iterator.h | 1 + 1 file changed, 1 insertion(+) diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 149fd43ad..25922fba9 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -106,6 +106,7 @@ class Iterator { Iterator(Kind MyKind = Kind::Other) : MyKind(MyKind) {} private: + Kind MyKind; virtual llvm::raw_ostream &dump(llvm::raw_ostream &OS) const = 0; Kind MyKind; }; From 87ff46c90c7ec8204cbf9756cf675c0906e984ac Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 4 Oct 2018 17:18:55 +0000 Subject: [PATCH 315/686] [clangd] Simplify Dex query tree logic and fix missing-posting-list bug Summary: The bug being fixed: when a posting list doesn't exist in the index, it was previously just dropped from the query rather than being treated as empty. Now that we have the FALSE iterator, we can use it instead. The query tree logic previously had a bunch of special cases to detect whether subtrees are empty. Now we just naively build the whole tree, and rely on the query optimizations to drop the trivial parts. Finally, there was a bug in trigram generation: the empty query would generate a single trigram "$$$" instead of no trigrams. This had no effect (there was no posting list, so the other bug cancelled it out). But we now have to fix this bug too. Reviewers: ilya-biryukov Subscribers: ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52796 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343802 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/Dex.cpp | 67 +++++++++++++---------------------- clangd/index/dex/Dex.h | 1 + clangd/index/dex/Iterator.h | 1 - clangd/index/dex/Trigram.cpp | 2 ++ unittests/clangd/DexTests.cpp | 13 +++++-- 5 files changed, 38 insertions(+), 46 deletions(-) diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index 59cd8fc43..cb080b30e 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -62,7 +62,7 @@ std::vector generateSearchTokens(const Symbol &Sym) { } // Constructs BOOST iterators for Path Proximities. -std::vector> createFileProximityIterators( +std::unique_ptr createFileProximityIterator( llvm::ArrayRef ProximityPaths, llvm::ArrayRef URISchemes, const llvm::DenseMap &InvertedIndex, @@ -105,7 +105,8 @@ std::vector> createFileProximityIterators( It->second.iterator(&It->first), PathProximitySignals.evaluate())); } } - return BoostingIterators; + BoostingIterators.push_back(Corpus.all()); + return Corpus.unionOf(std::move(BoostingIterators)); } } // namespace @@ -147,6 +148,12 @@ void Dex::buildIndex() { {TokenToPostingList.first, PostingList(TokenToPostingList.second)}); } +std::unique_ptr Dex::iterator(const Token &Tok) const { + auto It = InvertedIndex.find(Tok); + return It == InvertedIndex.end() ? Corpus.none() + : It->second.iterator(&It->first); +} + /// Constructs iterators over tokens extracted from the query and exhausts it /// while applying Callback to each symbol in the order of decreasing quality /// of the matched symbols. @@ -160,64 +167,40 @@ bool Dex::fuzzyFind(const FuzzyFindRequest &Req, // Prevent clients from postfiltering them for longer queries. bool More = !Req.Query.empty() && Req.Query.size() < 3; - std::vector> TopLevelChildren; + std::vector> Criteria; const auto TrigramTokens = generateQueryTrigrams(Req.Query); // Generate query trigrams and construct AND iterator over all query // trigrams. std::vector> TrigramIterators; - for (const auto &Trigram : TrigramTokens) { - const auto It = InvertedIndex.find(Trigram); - if (It != InvertedIndex.end()) - TrigramIterators.push_back(It->second.iterator(&It->first)); - } - if (!TrigramIterators.empty()) - TopLevelChildren.push_back(Corpus.intersect(move(TrigramIterators))); + for (const auto &Trigram : TrigramTokens) + TrigramIterators.push_back(iterator(Trigram)); + Criteria.push_back(Corpus.intersect(move(TrigramIterators))); // Generate scope tokens for search query. std::vector> ScopeIterators; - for (const auto &Scope : Req.Scopes) { - Token Tok(Token::Kind::Scope, Scope); - const auto It = InvertedIndex.find(Tok); - if (It != InvertedIndex.end()) - ScopeIterators.push_back(It->second.iterator(&It->first)); - } - if (Req.AnyScope) + for (const auto &Scope : Req.Scopes) + ScopeIterators.push_back(iterator(Token(Token::Kind::Scope, Scope))); + if (Req.AnyScope || /*legacy*/ Req.Scopes.empty()) ScopeIterators.push_back( Corpus.boost(Corpus.all(), ScopeIterators.empty() ? 1.0 : 0.2)); + Criteria.push_back(Corpus.unionOf(move(ScopeIterators))); - // Add OR iterator for scopes if there are any Scope Iterators. - if (!ScopeIterators.empty()) - TopLevelChildren.push_back(Corpus.unionOf(move(ScopeIterators))); - - // Add proximity paths boosting. - auto BoostingIterators = createFileProximityIterators( - Req.ProximityPaths, URISchemes, InvertedIndex, Corpus); - // Boosting iterators do not actually filter symbols. In order to preserve - // the validity of resulting query, TRUE iterator should be added along - // BOOSTs. - if (!BoostingIterators.empty()) { - BoostingIterators.push_back(Corpus.all()); - TopLevelChildren.push_back(Corpus.unionOf(move(BoostingIterators))); - } + // Add proximity paths boosting (all symbols, some boosted). + Criteria.push_back(createFileProximityIterator(Req.ProximityPaths, URISchemes, + InvertedIndex, Corpus)); if (Req.RestrictForCodeCompletion) - TopLevelChildren.push_back( - InvertedIndex.find(RestrictedForCodeCompletion) - ->second.iterator(&RestrictedForCodeCompletion)); + Criteria.push_back(iterator(RestrictedForCodeCompletion)); // Use TRUE iterator if both trigrams and scopes from the query are not // present in the symbol index. - auto QueryIterator = TopLevelChildren.empty() - ? Corpus.all() - : Corpus.intersect(move(TopLevelChildren)); + auto Root = Corpus.intersect(move(Criteria)); // Retrieve more items than it was requested: some of the items with high // final score might not be retrieved otherwise. - // FIXME(kbobyrev): Pre-scoring retrieval threshold should be adjusted as - // using 100x of the requested number might not be good in practice, e.g. - // when the requested number of items is small. - auto Root = Req.Limit ? Corpus.limit(move(QueryIterator), *Req.Limit * 100) - : move(QueryIterator); + // FIXME(kbobyrev): Tune this ratio. + if (Req.Limit) + Root = Corpus.limit(move(Root), *Req.Limit * 100); SPAN_ATTACH(Tracer, "query", llvm::to_string(*Root)); vlog("Dex query tree: {0}", *Root); diff --git a/clangd/index/dex/Dex.h b/clangd/index/dex/Dex.h index d89e6e15b..8364cb5d1 100644 --- a/clangd/index/dex/Dex.h +++ b/clangd/index/dex/Dex.h @@ -87,6 +87,7 @@ class Dex : public SymbolIndex { private: void buildIndex(); + std::unique_ptr iterator(const Token &Tok) const; /// Stores symbols sorted in the descending order of symbol quality.. std::vector Symbols; diff --git a/clangd/index/dex/Iterator.h b/clangd/index/dex/Iterator.h index 25922fba9..149fd43ad 100644 --- a/clangd/index/dex/Iterator.h +++ b/clangd/index/dex/Iterator.h @@ -106,7 +106,6 @@ class Iterator { Iterator(Kind MyKind = Kind::Other) : MyKind(MyKind) {} private: - Kind MyKind; virtual llvm::raw_ostream &dump(llvm::raw_ostream &OS) const = 0; Kind MyKind; }; diff --git a/clangd/index/dex/Trigram.cpp b/clangd/index/dex/Trigram.cpp index 641c899ab..ec6ba682b 100644 --- a/clangd/index/dex/Trigram.cpp +++ b/clangd/index/dex/Trigram.cpp @@ -87,6 +87,8 @@ std::vector generateIdentifierTrigrams(llvm::StringRef Identifier) { } std::vector generateQueryTrigrams(llvm::StringRef Query) { + if (Query.empty()) + return {}; std::string LowercaseQuery = Query.lower(); if (Query.size() < 3) // short-query trigrams only return {Token(Token::Kind::Trigram, LowercaseQuery)}; diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 017ccbfa0..66951f590 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -408,6 +408,7 @@ TEST(DexTrigrams, QueryTrigrams) { EXPECT_THAT(generateQueryTrigrams("cl"), trigramsAre({"cl"})); EXPECT_THAT(generateQueryTrigrams("cla"), trigramsAre({"cla"})); + EXPECT_THAT(generateQueryTrigrams(""), trigramsAre({})); EXPECT_THAT(generateQueryTrigrams("_"), trigramsAre({"_"})); EXPECT_THAT(generateQueryTrigrams("__"), trigramsAre({"__"})); EXPECT_THAT(generateQueryTrigrams("___"), trigramsAre({})); @@ -526,8 +527,6 @@ TEST(DexTest, FuzzyMatch) { UnorderedElementsAre("LaughingOutLoud", "LittleOldLady")); } -// TODO(sammccall): enable after D52796 bugfix. -#if 0 TEST(DexTest, ShortQuery) { auto I = Dex::build(generateSymbols({"OneTwoThreeFour"}), RefSlab(), URISchemes); @@ -549,7 +548,6 @@ TEST(DexTest, ShortQuery) { EXPECT_THAT(match(*I, Req, &Incomplete), ElementsAre("OneTwoThreeFour")); EXPECT_FALSE(Incomplete) << "3-char string is not a short query"; } -#endif TEST(DexTest, MatchQualifiedNamesWithoutSpecificScope) { auto I = Dex::build(generateSymbols({"a::y1", "b::y2", "y3"}), RefSlab(), @@ -614,6 +612,15 @@ TEST(DexTest, IgnoreCases) { EXPECT_THAT(match(*I, Req), UnorderedElementsAre("ns::ABC", "ns::abc")); } +TEST(DexTest, UnknownPostingList) { + // Regression test: we used to ignore unknown scopes and accept any symbol. + auto I = Dex::build(generateSymbols({"ns::ABC", "ns::abc"}), RefSlab(), + URISchemes); + FuzzyFindRequest Req; + Req.Scopes = {"ns2::"}; + EXPECT_THAT(match(*I, Req), UnorderedElementsAre()); +} + TEST(DexTest, Lookup) { auto I = Dex::build(generateSymbols({"ns::abc", "ns::xyz"}), RefSlab(), URISchemes); EXPECT_THAT(lookup(*I, SymbolID("ns::abc")), UnorderedElementsAre("ns::abc")); From 09aac645711c551627efe35bc9f76a6d49c4f8af Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Thu, 4 Oct 2018 21:34:13 +0000 Subject: [PATCH 316/686] [clang-doc] Clean up Markdown output Make the output for the MDGenerator cleaner and more readable. Differential Revision: https://reviews.llvm.org/D52754 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343818 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/MDGenerator.cpp | 25 +++++++++++++++---------- test/clang-doc/md-comment.cpp | 3 ++- test/clang-doc/md-linkage.cpp | 30 ++++++++++++++++++++---------- test/clang-doc/md-module.cpp | 6 ++++-- test/clang-doc/md-namespace.cpp | 6 ++++-- test/clang-doc/md-record.cpp | 12 ++++++++---- 6 files changed, 53 insertions(+), 29 deletions(-) diff --git a/clang-doc/MDGenerator.cpp b/clang-doc/MDGenerator.cpp index 2f523e439..5deb510e6 100644 --- a/clang-doc/MDGenerator.cpp +++ b/clang-doc/MDGenerator.cpp @@ -74,18 +74,18 @@ std::string genReferenceList(const llvm::SmallVectorImpl &Refs) { return Stream.str(); } -void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n"; } +void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n\n"; } -void writeNewLine(raw_ostream &OS) { OS << "\n"; } +void writeNewLine(raw_ostream &OS) { OS << "\n\n"; } void writeHeader(const Twine &Text, unsigned int Num, raw_ostream &OS) { - OS << std::string(Num, '#') + " " + Text << "\n"; + OS << std::string(Num, '#') + " " + Text << "\n\n"; } void writeFileDefinition(const Location &L, raw_ostream &OS) { OS << genItalic("Defined at line " + std::to_string(L.LineNumber) + " of " + L.Filename) - << "\n"; + << "\n\n"; } void writeDescription(const CommentInfo &I, raw_ostream &OS) { @@ -104,10 +104,10 @@ void writeDescription(const CommentInfo &I, raw_ostream &OS) { OS << genEmphasis(I.Name) << " " << I.Text; } else if (I.Kind == "ParamCommandComment") { std::string Direction = I.Explicit ? (" " + I.Direction).str() : ""; - OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n"; + OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n\n"; } else if (I.Kind == "TParamCommandComment") { std::string Direction = I.Explicit ? (" " + I.Direction).str() : ""; - OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n"; + OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n\n"; } else if (I.Kind == "VerbatimBlockComment") { for (const auto &Child : I.Children) writeDescription(*Child, OS); @@ -132,7 +132,7 @@ void writeDescription(const CommentInfo &I, raw_ostream &OS) { } else if (I.Kind == "TextComment") { OS << I.Text; } else { - OS << "Unknown comment kind: " << I.Kind << ".\n"; + OS << "Unknown comment kind: " << I.Kind << ".\n\n"; } } @@ -166,11 +166,16 @@ void genMarkdown(const FunctionInfo &I, llvm::raw_ostream &OS) { Stream << N.Type.Name + " " + N.Name; First = false; } + writeHeader(I.Name, 3, OS); std::string Access = getAccess(I.Access); if (Access != "") - writeHeader(genItalic(Access) + " " + I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")", 3, OS); - else - writeHeader(I.ReturnType.Type.Name + " " + I.Name + "(" + Stream.str() + ")", 3, OS); + writeLine(genItalic(Access + " " + I.ReturnType.Type.Name + " " + I.Name + + "(" + Stream.str() + ")"), + OS); + else + writeLine(genItalic(I.ReturnType.Type.Name + " " + I.Name + "(" + + Stream.str() + ")"), + OS); if (I.DefLoc) writeFileDefinition(I.DefLoc.getValue(), OS); diff --git a/test/clang-doc/md-comment.cpp b/test/clang-doc/md-comment.cpp index ae14eaac2..8e8f97a97 100644 --- a/test/clang-doc/md-comment.cpp +++ b/test/clang-doc/md-comment.cpp @@ -33,7 +33,8 @@ void F(int I, int J) {} // RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-0 // CHECK-0: # Global Namespace // CHECK-0: ## Functions -// CHECK-0: ### void F(int I, int J) +// CHECK-0: ### F +// CHECK-0: *void F(int I, int J)* // CHECK-0: *Defined at line 28 of test* // CHECK-0: **brief** Brief description. // CHECK-0: Extended description that continues onto the next line. diff --git a/test/clang-doc/md-linkage.cpp b/test/clang-doc/md-linkage.cpp index 942ff3533..ffc6a3d78 100644 --- a/test/clang-doc/md-linkage.cpp +++ b/test/clang-doc/md-linkage.cpp @@ -104,23 +104,31 @@ inline void anonInlineFunction(); // CHECK-0: int publicField // CHECK-0: protected int protectedField // CHECK-0: ## Functions -// CHECK-0: ### void publicMethod() -// CHECK-0: ### void protectedMethod() +// CHECK-0: ### publicMethod +// CHECK-0: *void publicMethod()* +// CHECK-0: ### protectedMethod +// CHECK-0: *void protectedMethod()* // RUN: cat %t/docs/./named.md | FileCheck %s --check-prefix CHECK-1 // CHECK-1: # namespace named // CHECK-1: ## Functions -// CHECK-1: ### void namedFunction() -// CHECK-1: ### void namedInlineFunction() +// CHECK-1: ### namedFunction +// CHECK-1: *void namedFunction()* +// CHECK-1: ### namedInlineFunction +// CHECK-1: *void namedInlineFunction()* // RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-2 // CHECK-2: # Global Namespace // CHECK-2: ## Functions -// CHECK-2: ### void function(int x) -// CHECK-2: ### int inlinedFunction(int x) -// CHECK-2: ### int functionWithInnerClass(int x) +// CHECK-2: ### function +// CHECK-2: *void function(int x)* +// CHECK-2: ### inlinedFunction +// CHECK-2: *int inlinedFunction(int x)* +// CHECK-2: ### functionWithInnerClass +// CHECK-2: *int functionWithInnerClass(int x)* // CHECK-2: *Defined at line 14 of test* -// CHECK-2: ### int inlinedFunctionWithInnerClass(int x) +// CHECK-2: ### inlinedFunctionWithInnerClass +// CHECK-2: *int inlinedFunctionWithInnerClass(int x)* // CHECK-2: *Defined at line 23 of test* // RUN: cat %t/docs/named/NamedClass.md | FileCheck %s --check-prefix CHECK-3 @@ -130,5 +138,7 @@ inline void anonInlineFunction(); // CHECK-3: int namedPublicField // CHECK-3: protected int namedProtectedField // CHECK-3: ## Functions -// CHECK-3: ### void namedPublicMethod() -// CHECK-3: ### void namedProtectedMethod() +// CHECK-3: ### namedPublicMethod +// CHECK-3: *void namedPublicMethod()* +// CHECK-3: ### namedProtectedMethod +// CHECK-3: *void namedProtectedMethod()* diff --git a/test/clang-doc/md-module.cpp b/test/clang-doc/md-module.cpp index 936f621bc..33de5aef5 100644 --- a/test/clang-doc/md-module.cpp +++ b/test/clang-doc/md-module.cpp @@ -20,5 +20,7 @@ export double exportedModuleFunction(double y, int z); // ExternalLinkage // RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-0 // CHECK-0: # Global Namespace // CHECK-0: ## Functions -// CHECK-0: ### int moduleFunction(int x) -// CHECK-0: ### double exportedModuleFunction(double y, int z) +// CHECK-0: ### moduleFunction +// CHECK-0: *int moduleFunction(int x)* +// CHECK-0: ### exportedModuleFunction +// CHECK-0: *double exportedModuleFunction(double y, int z)* diff --git a/test/clang-doc/md-namespace.cpp b/test/clang-doc/md-namespace.cpp index 6db3fffd2..e2fddf4eb 100644 --- a/test/clang-doc/md-namespace.cpp +++ b/test/clang-doc/md-namespace.cpp @@ -31,13 +31,15 @@ E func(int i) { return X; } // RUN: cat %t/docs/./A.md | FileCheck %s --check-prefix CHECK-0 // CHECK-0: # namespace A // CHECK-0: ## Functions -// CHECK-0: ### void f() +// CHECK-0: ### f +// CHECK-0: *void f()* // CHECK-0: *Defined at line 17 of test* // RUN: cat %t/docs/A/B.md | FileCheck %s --check-prefix CHECK-1 // CHECK-1: # namespace B // CHECK-1: ## Functions -// CHECK-1: ### enum A::B::E func(int i) +// CHECK-1: ### func +// CHECK-1: *enum A::B::E func(int i)* // CHECK-1: *Defined at line 23 of test* // CHECK-1: ## Enums // CHECK-1: | enum E | diff --git a/test/clang-doc/md-record.cpp b/test/clang-doc/md-record.cpp index 0d3e0ccc2..93fbe1607 100644 --- a/test/clang-doc/md-record.cpp +++ b/test/clang-doc/md-record.cpp @@ -56,7 +56,8 @@ class G; // RUN: cat %t/docs/./GlobalNamespace.md | FileCheck %s --check-prefix CHECK-2 // CHECK-2: # Global Namespace // CHECK-2: ## Functions -// CHECK-2: ### void H() +// CHECK-2: ### H +// CHECK-2: *void H()* // CHECK-2: *Defined at line 11 of test* // CHECK-2: ## Enums // CHECK-2: | enum B | @@ -74,11 +75,14 @@ class G; // CHECK-3: # class E // CHECK-3: *Defined at line 25 of test* // CHECK-3: ## Functions -// CHECK-3: ### void E() +// CHECK-3: ### E +// CHECK-3: *void E()* // CHECK-3: *Defined at line 27 of test* -// CHECK-3: ### void ~E() +// CHECK-3: ### ~E +// CHECK-3: *void ~E()* // CHECK-3: *Defined at line 28 of test* -// CHECK-3: ### void ProtectedMethod() +// CHECK-3: ### ProtectedMethod +// CHECK-3: *void ProtectedMethod()* // CHECK-3: *Defined at line 34 of test* // RUN: cat %t/docs/./C.md | FileCheck %s --check-prefix CHECK-4 From c5807e88cfb70bccf3a5df4cec55ec127918598d Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Fri, 5 Oct 2018 09:05:28 +0000 Subject: [PATCH 317/686] [clangd] Make binary index format the default, remove dead flag. Reviewers: hokein Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52872 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343841 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/indexer/IndexerMain.cpp | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/clangd/indexer/IndexerMain.cpp b/clangd/indexer/IndexerMain.cpp index 10490c06e..f6a939d65 100644 --- a/clangd/indexer/IndexerMain.cpp +++ b/clangd/indexer/IndexerMain.cpp @@ -7,8 +7,7 @@ // //===----------------------------------------------------------------------===// // -// GlobalSymbolBuilder is a tool to extract symbols from a whole project. -// This tool is **experimental** only. Don't use it in production code. +// clangd-indexer is a tool to gather index data (symbols, xrefs) from source. // //===----------------------------------------------------------------------===// @@ -21,7 +20,6 @@ #include "clang/Tooling/Execution.h" #include "clang/Tooling/Tooling.h" #include "llvm/Support/CommandLine.h" -#include "llvm/Support/Path.h" #include "llvm/Support/Signals.h" using namespace llvm; @@ -32,22 +30,13 @@ namespace clang { namespace clangd { namespace { -static llvm::cl::opt AssumedHeaderDir( - "assume-header-dir", - llvm::cl::desc("The index includes header that a symbol is defined in. " - "If the absolute path cannot be determined (e.g. an " - "in-memory VFS) then the relative path is resolved against " - "this directory, which must be absolute. If this flag is " - "not given, such headers will have relative paths."), - llvm::cl::init("")); - static llvm::cl::opt Format("format", llvm::cl::desc("Format of the index to be written"), llvm::cl::values(clEnumValN(IndexFileFormat::YAML, "yaml", "human-readable YAML format"), clEnumValN(IndexFileFormat::RIFF, "binary", "binary RIFF format")), - llvm::cl::init(IndexFileFormat::YAML)); + llvm::cl::init(IndexFileFormat::RIFF)); class IndexActionFactory : public tooling::FrontendActionFactory { public: @@ -55,7 +44,6 @@ class IndexActionFactory : public tooling::FrontendActionFactory { clang::FrontendAction *create() override { SymbolCollector::Options Opts; - Opts.FallbackDir = AssumedHeaderDir; return createStaticIndexingAction( Opts, [&](SymbolSlab S) { @@ -102,15 +90,14 @@ int main(int argc, const char **argv) { const char *Overview = R"( Creates an index of symbol information etc in a whole project. - This is **experimental** and not production-ready! Example usage for a project using CMake compile commands: - $ clangd-indexer --executor=all-TUs compile_commands.json > index.yaml + $ clangd-indexer --executor=all-TUs compile_commands.json > clangd.dex Example usage for file sequence index without flags: - $ clangd-indexer File1.cpp File2.cpp ... FileN.cpp > index.yaml + $ clangd-indexer File1.cpp File2.cpp ... FileN.cpp > clangd.dex Note: only symbols from header files will be indexed. )"; @@ -123,12 +110,6 @@ int main(int argc, const char **argv) { return 1; } - if (!clang::clangd::AssumedHeaderDir.empty() && - !llvm::sys::path::is_absolute(clang::clangd::AssumedHeaderDir)) { - llvm::errs() << "--assume-header-dir must be an absolute path.\n"; - return 1; - } - // Collect symbols found in each translation unit, merging as we go. clang::clangd::IndexFileIn Data; auto Err = Executor->get()->execute( From 34b30d6eb68ef71d53debc296ae7fe1beb867071 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Fri, 5 Oct 2018 12:08:06 +0000 Subject: [PATCH 318/686] [clangd] Fix a subtle case for GetBeginningOfIdentifier. Calling getMacroArgExpansionLocation too early was causing Lexer::getRawToken to do the wrong thing - lexing the macro name instead of the arg contents. Differential Revision: https://reviews.llvm.org/D52928 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343844 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdUnit.cpp | 8 ++++---- unittests/clangd/ClangdUnitTests.cpp | 25 +++++++++++++++++++++---- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/clangd/ClangdUnit.cpp b/clangd/ClangdUnit.cpp index 2ad6dc69c..5717bc8a5 100644 --- a/clangd/ClangdUnit.cpp +++ b/clangd/ClangdUnit.cpp @@ -390,7 +390,6 @@ SourceLocation clangd::getBeginningOfIdentifier(ParsedAST &Unit, log("getBeginningOfIdentifier: {0}", Offset.takeError()); return SourceLocation(); } - SourceLocation InputLoc = SourceMgr.getComposedLoc(FID, *Offset); // GetBeginningOfToken(pos) is almost what we want, but does the wrong thing // if the cursor is at the end of the identifier. @@ -401,15 +400,16 @@ SourceLocation clangd::getBeginningOfIdentifier(ParsedAST &Unit, // 3) anywhere outside an identifier, we'll get some non-identifier thing. // We can't actually distinguish cases 1 and 3, but returning the original // location is correct for both! + SourceLocation InputLoc = SourceMgr.getComposedLoc(FID, *Offset); if (*Offset == 0) // Case 1 or 3. return SourceMgr.getMacroArgExpandedLocation(InputLoc); - SourceLocation Before = - SourceMgr.getMacroArgExpandedLocation(InputLoc.getLocWithOffset(-1)); + SourceLocation Before = SourceMgr.getComposedLoc(FID, *Offset - 1); + Before = Lexer::GetBeginningOfToken(Before, SourceMgr, AST.getLangOpts()); Token Tok; if (Before.isValid() && !Lexer::getRawToken(Before, Tok, SourceMgr, AST.getLangOpts(), false) && Tok.is(tok::raw_identifier)) - return Before; // Case 2. + return SourceMgr.getMacroArgExpandedLocation(Before); // Case 2. return SourceMgr.getMacroArgExpandedLocation(InputLoc); // Case 1 or 3. } diff --git a/unittests/clangd/ClangdUnitTests.cpp b/unittests/clangd/ClangdUnitTests.cpp index 48d936632..51cfcfe48 100644 --- a/unittests/clangd/ClangdUnitTests.cpp +++ b/unittests/clangd/ClangdUnitTests.cpp @@ -205,8 +205,13 @@ main.cpp:2:3: error: something terrible happened)"); } TEST(ClangdUnitTest, GetBeginningOfIdentifier) { + std::string Preamble = R"cpp( +struct Bar { int func(); }; +#define MACRO(X) void f() { X; } +Bar* bar; + )cpp"; // First ^ is the expected beginning, last is the search position. - for (const char *Text : { + for (std::string Text : std::vector{ "int ^f^oo();", // inside identifier "int ^foo();", // beginning of identifier "int ^foo^();", // end of identifier @@ -214,14 +219,26 @@ TEST(ClangdUnitTest, GetBeginningOfIdentifier) { "^int foo();", // beginning of file (can't back up) "int ^f0^0();", // after a digit (lexing at N-1 is wrong) "int ^λλ^λ();", // UTF-8 handled properly when backing up + + // identifier in macro arg + "MACRO(bar->^func())", // beginning of identifier + "MACRO(bar->^fun^c())", // inside identifier + "MACRO(bar->^func^())", // end of identifier + "MACRO(^bar->func())", // begin identifier + "MACRO(^bar^->func())", // end identifier + "^MACRO(bar->func())", // beginning of macro name + "^MAC^RO(bar->func())", // inside macro name + "^MACRO^(bar->func())", // end of macro name }) { - Annotations TestCase(Text); + std::string WithPreamble = Preamble + Text; + Annotations TestCase(WithPreamble); auto AST = TestTU::withCode(TestCase.code()).build(); const auto &SourceMgr = AST.getASTContext().getSourceManager(); SourceLocation Actual = getBeginningOfIdentifier( AST, TestCase.points().back(), SourceMgr.getMainFileID()); - Position ActualPos = - offsetToPosition(TestCase.code(), SourceMgr.getFileOffset(Actual)); + Position ActualPos = offsetToPosition( + TestCase.code(), + SourceMgr.getFileOffset(SourceMgr.getSpellingLoc(Actual))); EXPECT_EQ(TestCase.points().front(), ActualPos) << Text; } } From 00567aa973e39e29bb95216dcb04653e5ef7ce89 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Fri, 5 Oct 2018 12:22:40 +0000 Subject: [PATCH 319/686] [clangd] Remove debugging output in test git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343845 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/SerializationTests.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/unittests/clangd/SerializationTests.cpp b/unittests/clangd/SerializationTests.cpp index 720c7df55..11b26eb99 100644 --- a/unittests/clangd/SerializationTests.cpp +++ b/unittests/clangd/SerializationTests.cpp @@ -157,11 +157,6 @@ TEST(SerializationTest, BinaryConversions) { IndexFileOut Out(*In); Out.Format = IndexFileFormat::RIFF; std::string Serialized = llvm::to_string(Out); - { - std::error_code EC; - llvm::raw_fd_ostream F("/tmp/foo", EC); - F << Serialized; - } auto In2 = readIndexFile(Serialized); ASSERT_TRUE(bool(In2)) << In.takeError(); From 80fa4a223fc0432d8129c466b8ba4ac51df2e640 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Fri, 5 Oct 2018 13:36:00 +0000 Subject: [PATCH 320/686] [clang-tidy] Replace deprecated std::ios_base aliases This check warns the uses of the deprecated member types of std::ios_base and replaces those that have a non-deprecated equivalent. Patch by andobence! Reviewd by: alexfh Revision ID: https://reviews.llvm.org/D51332 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343848 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/modernize/CMakeLists.txt | 1 + .../DeprecatedIosBaseAliasesCheck.cpp | 80 ++++++ .../modernize/DeprecatedIosBaseAliasesCheck.h | 36 +++ clang-tidy/modernize/ModernizeTidyModule.cpp | 3 + docs/ReleaseNotes.rst | 6 + docs/clang-tidy/checks/list.rst | 1 + .../modernize-deprecated-ios-base-aliases.rst | 17 ++ .../modernize-deprecated-ios-base-aliases.cpp | 239 ++++++++++++++++++ 8 files changed, 383 insertions(+) create mode 100644 clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp create mode 100644 clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.h create mode 100644 docs/clang-tidy/checks/modernize-deprecated-ios-base-aliases.rst create mode 100644 test/clang-tidy/modernize-deprecated-ios-base-aliases.cpp diff --git a/clang-tidy/modernize/CMakeLists.txt b/clang-tidy/modernize/CMakeLists.txt index 06ea18d67..1c8dc4627 100644 --- a/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tidy/modernize/CMakeLists.txt @@ -4,6 +4,7 @@ add_clang_library(clangTidyModernizeModule AvoidBindCheck.cpp ConcatNestedNamespacesCheck.cpp DeprecatedHeadersCheck.cpp + DeprecatedIosBaseAliasesCheck.cpp LoopConvertCheck.cpp LoopConvertUtils.cpp MakeSharedCheck.cpp diff --git a/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp b/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp new file mode 100644 index 000000000..2e9dad99e --- /dev/null +++ b/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp @@ -0,0 +1,80 @@ +//===--- DeprecatedIosBaseAliasesCheck.cpp - clang-tidy--------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DeprecatedIosBaseAliasesCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +static const std::array DeprecatedTypes = { + "::std::ios_base::io_state", "::std::ios_base::open_mode", + "::std::ios_base::seek_dir", "::std::ios_base::streamoff", + "::std::ios_base::streampos", +}; + +static const llvm::StringMap ReplacementTypes = { + {"io_state", "iostate"}, + {"open_mode", "openmode"}, + {"seek_dir", "seekdir"}}; + +void DeprecatedIosBaseAliasesCheck::registerMatchers(MatchFinder *Finder) { + // Only register the matchers for C++; the functionality currently does not + // provide any benefit to other languages, despite being benign. + if (!getLangOpts().CPlusPlus) + return; + + auto IoStateDecl = typedefDecl(hasAnyName(DeprecatedTypes)).bind("TypeDecl"); + auto IoStateType = + qualType(hasDeclaration(IoStateDecl), unless(elaboratedType())); + + Finder->addMatcher(typeLoc(loc(IoStateType)).bind("TypeLoc"), this); +} + +void DeprecatedIosBaseAliasesCheck::check( + const MatchFinder::MatchResult &Result) { + SourceManager &SM = *Result.SourceManager; + + const auto *Typedef = Result.Nodes.getNodeAs("TypeDecl"); + StringRef TypeName = Typedef->getName(); + bool HasReplacement = ReplacementTypes.count(TypeName); + + const auto *TL = Result.Nodes.getNodeAs("TypeLoc"); + SourceLocation IoStateLoc = TL->getBeginLoc(); + + // Do not generate fixits for matches depending on template arguments and + // macro expansions. + bool Fix = HasReplacement && !TL->getType()->isDependentType(); + if (IoStateLoc.isMacroID()) { + IoStateLoc = SM.getSpellingLoc(IoStateLoc); + Fix = false; + } + + SourceLocation EndLoc = IoStateLoc.getLocWithOffset(TypeName.size() - 1); + + if (HasReplacement) { + auto FixName = ReplacementTypes.lookup(TypeName); + auto Builder = diag(IoStateLoc, "'std::ios_base::%0' is deprecated; use " + "'std::ios_base::%1' instead") + << TypeName << FixName; + + if (Fix) + Builder << FixItHint::CreateReplacement(SourceRange(IoStateLoc, EndLoc), + FixName); + } else + diag(IoStateLoc, "'std::ios_base::%0' is deprecated") << TypeName; +} + +} // namespace modernize +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.h b/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.h new file mode 100644 index 000000000..bbccd4131 --- /dev/null +++ b/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.h @@ -0,0 +1,36 @@ +//===--- DeprecatedIosBaseAliasesCheck.h - clang-tidy------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_DEPRECATEDIOSBASEALIASESCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_DEPRECATEDIOSBASEALIASESCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// This check warns the uses of the deprecated member types of ``std::ios_base`` +/// and replaces those that have a non-deprecated equivalent. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-deprecated-ios-base-aliases.html +class DeprecatedIosBaseAliasesCheck : public ClangTidyCheck { +public: + DeprecatedIosBaseAliasesCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_DEPRECATEDIOSBASEALIASESCHECK_H diff --git a/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tidy/modernize/ModernizeTidyModule.cpp index fa0a49071..8f2f52b1c 100644 --- a/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -13,6 +13,7 @@ #include "AvoidBindCheck.h" #include "ConcatNestedNamespacesCheck.h" #include "DeprecatedHeadersCheck.h" +#include "DeprecatedIosBaseAliasesCheck.h" #include "LoopConvertCheck.h" #include "MakeSharedCheck.h" #include "MakeUniqueCheck.h" @@ -51,6 +52,8 @@ class ModernizeModule : public ClangTidyModule { "modernize-concat-nested-namespaces"); CheckFactories.registerCheck( "modernize-deprecated-headers"); + CheckFactories.registerCheck( + "modernize-deprecated-ios-base-aliases"); CheckFactories.registerCheck("modernize-loop-convert"); CheckFactories.registerCheck("modernize-make-shared"); CheckFactories.registerCheck("modernize-make-unique"); diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index 386c74e7a..9da5ce6e9 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -99,6 +99,12 @@ Improvements to clang-tidy Checks for uses of nested namespaces in the form of ``namespace a { namespace b { ... }}`` and offers change to syntax introduced in C++17 standard: ``namespace a::b { ... }``. + +- New :doc:`modernize-deprecated-ios-base-aliases + ` check. + + This check warns the uses of the deprecated member types of ``std::ios_base`` + and replaces those that have a non-deprecated equivalent. - New :doc:`readability-magic-numbers ` check. diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index 4180385fe..cc966a9b0 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -174,6 +174,7 @@ Clang-Tidy Checks modernize-avoid-bind modernize-concat-nested-namespaces modernize-deprecated-headers + modernize-deprecated-ios-base-aliases modernize-loop-convert modernize-make-shared modernize-make-unique diff --git a/docs/clang-tidy/checks/modernize-deprecated-ios-base-aliases.rst b/docs/clang-tidy/checks/modernize-deprecated-ios-base-aliases.rst new file mode 100644 index 000000000..9460cab35 --- /dev/null +++ b/docs/clang-tidy/checks/modernize-deprecated-ios-base-aliases.rst @@ -0,0 +1,17 @@ +.. title:: clang-tidy - modernize-deprecated-ios-base-aliases + +modernize-deprecated-ios-base-aliases +===================================== + +This check warns the uses of the deprecated member types of ``std::ios_base`` +and replaces those that have a non-deprecated equivalent. + +=================================== =========================== +Deprecated member type Replacement +=================================== =========================== +``std::ios_base::io_state`` ``std::ios_base::iostate`` +``std::ios_base::open_mode`` ``std::ios_base::openmode`` +``std::ios_base::seek_dir`` ``std::ios_base::seekdir`` +``std::ios_base::streamoff`` +``std::ios_base::streampos`` +=================================== =========================== diff --git a/test/clang-tidy/modernize-deprecated-ios-base-aliases.cpp b/test/clang-tidy/modernize-deprecated-ios-base-aliases.cpp new file mode 100644 index 000000000..680cb9ec4 --- /dev/null +++ b/test/clang-tidy/modernize-deprecated-ios-base-aliases.cpp @@ -0,0 +1,239 @@ +// RUN: %check_clang_tidy %s modernize-deprecated-ios-base-aliases %t + +namespace std { +class ios_base { +public: + typedef int io_state; + typedef int open_mode; + typedef int seek_dir; + + typedef int streampos; + typedef int streamoff; +}; + +template +class basic_ios : public ios_base { +}; +} // namespace std + +// Test function return values (declaration) +std::ios_base::io_state f_5(); +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'std::ios_base::io_state' is deprecated; use 'std::ios_base::iostate' instead [modernize-deprecated-ios-base-aliases] +// CHECK-FIXES: std::ios_base::iostate f_5(); + +// Test function parameters. +void f_6(std::ios_base::open_mode); +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 'std::ios_base::open_mode' is deprecated +// CHECK-FIXES: void f_6(std::ios_base::openmode); +void f_7(const std::ios_base::seek_dir &); +// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 'std::ios_base::seek_dir' is deprecated +// CHECK-FIXES: void f_7(const std::ios_base::seekdir &); + +// Test on record type fields. +struct A { + std::ios_base::io_state field; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: std::ios_base::iostate field; + + typedef std::ios_base::io_state int_ptr_type; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: typedef std::ios_base::iostate int_ptr_type; +}; + +struct B : public std::ios_base { + io_state a; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: iostate a; +}; + +struct C : public std::basic_ios { + io_state a; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: iostate a; +}; + +void f_1() { + std::ios_base::io_state a; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: std::ios_base::iostate a; + + // Check that spaces aren't modified unnecessarily. + std :: ios_base :: io_state b; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: std :: ios_base :: iostate b; + + // Test construction from a temporary. + std::ios_base::io_state c = std::ios_base::io_state{}; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'std::ios_base::io_state' is deprecated + // CHECK-MESSAGES: :[[@LINE-2]]:46: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: std::ios_base::iostate c = std::ios_base::iostate{}; + + typedef std::ios_base::io_state alias1; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: typedef std::ios_base::iostate alias1; + alias1 d(a); + + using alias2 = std::ios_base::io_state; + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: using alias2 = std::ios_base::iostate; + alias2 e; + + // Test pointers. + std::ios_base::io_state *f; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: std::ios_base::iostate *f; + + // Test 'static' declarations. + static std::ios_base::io_state g; + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: static std::ios_base::iostate g; + + // Test with cv-qualifiers. + const std::ios_base::io_state h(0); + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: const std::ios_base::iostate h(0); + volatile std::ios_base::io_state i; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: volatile std::ios_base::iostate i; + const volatile std::ios_base::io_state j(0); + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: const volatile std::ios_base::iostate j(0); + + // Test auto and initializer-list. + auto k = std::ios_base::io_state{}; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: auto k = std::ios_base::iostate{}; + + std::ios_base::io_state l{std::ios_base::io_state()}; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'std::ios_base::io_state' is deprecated + // CHECK-MESSAGES: :[[@LINE-2]]:44: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: std::ios_base::iostate l{std::ios_base::iostate()}; + + // Test temporaries. + std::ios_base::io_state(); + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: std::ios_base::iostate(); + + // Test inherited type usage + std::basic_ios::io_state m; + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: std::basic_ios::iostate m; + + std::ios_base::streampos n; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'std::ios_base::streampos' is deprecated [modernize-deprecated-ios-base-aliases] + + std::ios_base::streamoff o; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'std::ios_base::streamoff' is deprecated [modernize-deprecated-ios-base-aliases] +} + +// Test without the nested name specifiers. +void f_2() { + using namespace std; + + ios_base::io_state a; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: ios_base::iostate a; +} + +// Test messing-up with macros. +void f_4() { +#define MACRO_1 std::ios_base::io_state + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: 'std::ios_base::io_state' is deprecated + MACRO_1 a; + +#define MACRO_2 io_state + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 'std::ios_base::io_state' is deprecated + std::ios_base::MACRO_2 b; + +#define MACRO_3 std::ios_base + MACRO_3::io_state c; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 'std::ios_base::io_state' is deprecated + +#define MACRO_4(type) type::io_state + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: 'std::ios_base::io_state' is deprecated + MACRO_4(std::ios_base) d; + +#undef MACRO_1 +#undef MACRO_2 +#undef MACRO_3 +#undef MACRO_4 +} + +// Test function return values (definition). +std::ios_base::io_state f_5() +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'std::ios_base::io_state' is deprecated +// CHECK-FIXES: std::ios_base::iostate f_5() +{ + // Test constructor. + return std::ios_base::io_state(0); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: return std::ios_base::iostate(0); +} + +// Test that other aliases with same name aren't replaced +struct my_ios_base { + typedef int io_state; +}; + +namespace ns_1 { +struct my_ios_base2 { + typedef int io_state; +}; +} // namespace ns_1 + +void f_8() { + my_ios_base::io_state a; + + ns_1::my_ios_base2::io_state b; +} + +// Test templates +template +void f_9() { + typename std::basic_ios::io_state p; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 'std::ios_base::io_state' is deprecated + typename std::ios_base::io_state q; + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: typename std::ios_base::iostate q; +} + +template +void f_10(T arg) { + T x(arg); +} + +template +void f_11() { + typename T::io_state x{}; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'std::ios_base::io_state' is deprecated +} + +template +struct D : std::ios_base { + io_state a; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: iostate a; + + typename std::basic_ios::io_state b; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 'std::ios_base::io_state' is deprecated +}; + +template +struct E { + T t; +}; + +void f_12() { + f_9(); + + f_10(0); + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: f_10(0); + + f_11(); + D d; + + E e; + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 'std::ios_base::io_state' is deprecated + // CHECK-FIXES: E e; +} From 4e1ab6ce0c9fe2b50ab7ae30460964ba2e0fd1be Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Fri, 5 Oct 2018 14:03:04 +0000 Subject: [PATCH 321/686] [clangd] Remove last usage of ast matchers from SymbolCollector. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343849 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/SymbolCollector.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp index 35f3edb42..1a9432cd8 100644 --- a/clangd/index/SymbolCollector.cpp +++ b/clangd/index/SymbolCollector.cpp @@ -19,7 +19,6 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" #include "clang/Index/IndexSymbol.h" @@ -229,10 +228,10 @@ getTokenLocation(SourceLocation TokLoc, const SourceManager &SM, // the first seen declaration as canonical declaration is not a good enough // heuristic. bool isPreferredDeclaration(const NamedDecl &ND, index::SymbolRoleSet Roles) { - using namespace clang::ast_matchers; + const auto& SM = ND.getASTContext().getSourceManager(); return (Roles & static_cast(index::SymbolRole::Definition)) && llvm::isa(&ND) && - match(decl(isExpansionInMainFile()), ND, ND.getASTContext()).empty(); + !SM.isWrittenInMainFile(SM.getExpansionLoc(ND.getLocation())); } RefKind toRefKind(index::SymbolRoleSet Roles) { @@ -260,7 +259,6 @@ void SymbolCollector::initialize(ASTContext &Ctx) { bool SymbolCollector::shouldCollectSymbol(const NamedDecl &ND, ASTContext &ASTCtx, const Options &Opts) { - using namespace clang::ast_matchers; if (ND.isImplicit()) return false; // Skip anonymous declarations, e.g (anonymous enum/class/struct). From 6bb33f45717cc46ae84ae41b922a534a435437bb Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Fri, 5 Oct 2018 14:15:19 +0000 Subject: [PATCH 322/686] [clang-tidy] NFC refactor lexer-utils to be usable without ASTContext Summary: This patch is a small refactoring necessary for 'readability-isolate-declaration' and does not introduce functional changes. It allows to use the utility functions without a full `ASTContext` and requires only the `SourceManager` and the `LangOpts`. Reviewers: alexfh, aaron.ballman, hokein Reviewed By: alexfh Subscribers: nemanjai, xazax.hun, kbarton, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D52684 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343850 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/bugprone/ArgumentCommentCheck.cpp | 5 +++-- clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp | 3 ++- .../cppcoreguidelines/ProTypeMemberInitCheck.cpp | 6 ++++-- clang-tidy/utils/FixItHintUtils.cpp | 3 ++- clang-tidy/utils/LexerUtils.cpp | 14 +++++--------- clang-tidy/utils/LexerUtils.h | 4 ++-- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/clang-tidy/bugprone/ArgumentCommentCheck.cpp b/clang-tidy/bugprone/ArgumentCommentCheck.cpp index 068f2c57d..62f30f79b 100644 --- a/clang-tidy/bugprone/ArgumentCommentCheck.cpp +++ b/clang-tidy/bugprone/ArgumentCommentCheck.cpp @@ -91,8 +91,9 @@ static std::vector> getCommentsBeforeLoc(ASTContext *Ctx, SourceLocation Loc) { std::vector> Comments; while (Loc.isValid()) { - clang::Token Tok = - utils::lexer::getPreviousToken(*Ctx, Loc, /*SkipComments=*/false); + clang::Token Tok = utils::lexer::getPreviousToken( + Loc, Ctx->getSourceManager(), Ctx->getLangOpts(), + /*SkipComments=*/false); if (Tok.isNot(tok::comment)) break; Loc = Tok.getLocation(); diff --git a/clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp b/clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp index ae6f2ee64..f92fc37f1 100644 --- a/clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp +++ b/clang-tidy/bugprone/SuspiciousSemicolonCheck.cpp @@ -40,7 +40,8 @@ void SuspiciousSemicolonCheck::check(const MatchFinder::MatchResult &Result) { return; ASTContext &Ctxt = *Result.Context; - auto Token = utils::lexer::getPreviousToken(Ctxt, LocStart); + auto Token = utils::lexer::getPreviousToken(LocStart, Ctxt.getSourceManager(), + Ctxt.getLangOpts()); auto &SM = *Result.SourceManager; unsigned SemicolonLine = SM.getSpellingLineNumber(LocStart); diff --git a/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp b/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp index 142ac2ed7..82f50a1a8 100644 --- a/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp +++ b/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp @@ -120,12 +120,14 @@ struct IntializerInsertion { switch (Placement) { case InitializerPlacement::New: Location = utils::lexer::getPreviousToken( - Context, Constructor.getBody()->getBeginLoc()) + Constructor.getBody()->getBeginLoc(), + Context.getSourceManager(), Context.getLangOpts()) .getLocation(); break; case InitializerPlacement::Before: Location = utils::lexer::getPreviousToken( - Context, Where->getSourceRange().getBegin()) + Where->getSourceRange().getBegin(), + Context.getSourceManager(), Context.getLangOpts()) .getLocation(); break; case InitializerPlacement::After: diff --git a/clang-tidy/utils/FixItHintUtils.cpp b/clang-tidy/utils/FixItHintUtils.cpp index 0627e5193..9da93653d 100644 --- a/clang-tidy/utils/FixItHintUtils.cpp +++ b/clang-tidy/utils/FixItHintUtils.cpp @@ -18,7 +18,8 @@ namespace fixit { FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context) { SourceLocation AmpLocation = Var.getLocation(); - auto Token = utils::lexer::getPreviousToken(Context, AmpLocation); + auto Token = utils::lexer::getPreviousToken( + AmpLocation, Context.getSourceManager(), Context.getLangOpts()); if (!Token.is(tok::unknown)) AmpLocation = Lexer::getLocForEndOfToken(Token.getLocation(), 0, Context.getSourceManager(), diff --git a/clang-tidy/utils/LexerUtils.cpp b/clang-tidy/utils/LexerUtils.cpp index d02721913..1b52347e4 100644 --- a/clang-tidy/utils/LexerUtils.cpp +++ b/clang-tidy/utils/LexerUtils.cpp @@ -14,19 +14,15 @@ namespace tidy { namespace utils { namespace lexer { -Token getPreviousToken(const ASTContext &Context, SourceLocation Location, - bool SkipComments) { - const auto &SourceManager = Context.getSourceManager(); +Token getPreviousToken(SourceLocation Location, const SourceManager &SM, + const LangOptions &LangOpts, bool SkipComments) { Token Token; Token.setKind(tok::unknown); Location = Location.getLocWithOffset(-1); - auto StartOfFile = - SourceManager.getLocForStartOfFile(SourceManager.getFileID(Location)); + auto StartOfFile = SM.getLocForStartOfFile(SM.getFileID(Location)); while (Location != StartOfFile) { - Location = Lexer::GetBeginningOfToken(Location, SourceManager, - Context.getLangOpts()); - if (!Lexer::getRawToken(Location, Token, SourceManager, - Context.getLangOpts()) && + Location = Lexer::GetBeginningOfToken(Location, SM, LangOpts); + if (!Lexer::getRawToken(Location, Token, SM, LangOpts) && (!SkipComments || !Token.is(tok::comment))) { break; } diff --git a/clang-tidy/utils/LexerUtils.h b/clang-tidy/utils/LexerUtils.h index f7bcd6f63..0eb5e56ef 100644 --- a/clang-tidy/utils/LexerUtils.h +++ b/clang-tidy/utils/LexerUtils.h @@ -19,8 +19,8 @@ namespace utils { namespace lexer { /// Returns previous token or ``tok::unknown`` if not found. -Token getPreviousToken(const ASTContext &Context, SourceLocation Location, - bool SkipComments = true); +Token getPreviousToken(SourceLocation Location, const SourceManager &SM, + const LangOptions &LangOpts, bool SkipComments = true); } // namespace lexer } // namespace utils From 689c7d528abab029f44c0ebfff2914e6354cfb5d Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sat, 6 Oct 2018 07:00:50 +0000 Subject: [PATCH 323/686] [clangd] Remove unused headers from CodeComplete.cpp queue is not used after index-provided completions' merge with those from Sema USRGeneration.h is not used after introduction of getSymbolID git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343912 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CodeComplete.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 9e2f162b4..9e80afbc5 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -40,11 +40,9 @@ #include "clang/Format/Format.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" -#include "clang/Index/USRGeneration.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Sema/Sema.h" -#include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" @@ -54,7 +52,6 @@ #include "llvm/Support/ScopedPrinter.h" #include #include -#include // We log detailed candidate here if you run with -debug-only=codecomplete. #define DEBUG_TYPE "CodeComplete" From 1bc8aec89bda33d0df8374c89f8c2b78008923f7 Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Sat, 6 Oct 2018 11:46:27 +0000 Subject: [PATCH 324/686] Fix -Wmissing-braces warning. NFCI. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343916 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp b/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp index 2e9dad99e..cfedff814 100644 --- a/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp +++ b/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp @@ -18,9 +18,9 @@ namespace tidy { namespace modernize { static const std::array DeprecatedTypes = { - "::std::ios_base::io_state", "::std::ios_base::open_mode", - "::std::ios_base::seek_dir", "::std::ios_base::streamoff", - "::std::ios_base::streampos", + {"::std::ios_base::io_state"}, {"::std::ios_base::open_mode"}, + {"::std::ios_base::seek_dir"}, {"::std::ios_base::streamoff"}, + {"::std::ios_base::streampos"}, }; static const llvm::StringMap ReplacementTypes = { From ffb1f50bc16186bdfc2f6efeea11a62396fe8ead Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Sat, 6 Oct 2018 11:59:31 +0000 Subject: [PATCH 325/686] Revert rL343916: Fix -Wmissing-braces warning. NFCI. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343917 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp b/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp index cfedff814..2e9dad99e 100644 --- a/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp +++ b/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp @@ -18,9 +18,9 @@ namespace tidy { namespace modernize { static const std::array DeprecatedTypes = { - {"::std::ios_base::io_state"}, {"::std::ios_base::open_mode"}, - {"::std::ios_base::seek_dir"}, {"::std::ios_base::streamoff"}, - {"::std::ios_base::streampos"}, + "::std::ios_base::io_state", "::std::ios_base::open_mode", + "::std::ios_base::seek_dir", "::std::ios_base::streamoff", + "::std::ios_base::streampos", }; static const llvm::StringMap ReplacementTypes = { From 5ac9b612b9c1d964fa5994e9d94e7fb791512251 Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Sun, 7 Oct 2018 14:49:41 +0000 Subject: [PATCH 326/686] [clangd] NFC: Migrate to LLVM STLExtras API where possible This patch improves readability by migrating `std::function(ForwardIt start, ForwardIt end, ...)` to LLVM's STLExtras range-based equivalent `llvm::function(RangeT &&Range, ...)`. Similar change in Clang: D52576. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D52650 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343937 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 3 +-- clangd/ClangdServer.cpp | 14 ++++++-------- clangd/CodeComplete.cpp | 26 ++++++++++++-------------- clangd/TUScheduler.cpp | 3 +-- clangd/XRefs.cpp | 18 ++++++++---------- clangd/index/CanonicalIncludes.cpp | 11 +++++------ clangd/index/FileIndex.cpp | 4 ++-- clangd/index/Index.cpp | 14 ++++++-------- clangd/index/Serialization.cpp | 2 +- clangd/index/SymbolCollector.cpp | 3 +-- clangd/index/dex/Dex.cpp | 3 +-- clangd/index/dex/Iterator.cpp | 9 ++++----- 12 files changed, 48 insertions(+), 62 deletions(-) diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index c6ce3ef9b..ca898a153 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -549,8 +549,7 @@ void ClangdLSPServer::onDiagnosticsReady(PathRef File, DiagnosticsJSON.push_back(std::move(LSPDiag)); auto &FixItsForDiagnostic = LocalFixIts[Diag]; - std::copy(Fixes.begin(), Fixes.end(), - std::back_inserter(FixItsForDiagnostic)); + llvm::copy(Fixes, std::back_inserter(FixItsForDiagnostic)); }); } diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index 96f83588e..f0b94cb95 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -361,17 +361,15 @@ llvm::Optional ClangdServer::switchSourceHeader(PathRef Path) { // Lookup in a list of known extensions. auto SourceIter = - std::find_if(std::begin(SourceExtensions), std::end(SourceExtensions), - [&PathExt](PathRef SourceExt) { - return SourceExt.equals_lower(PathExt); - }); + llvm::find_if(SourceExtensions, [&PathExt](PathRef SourceExt) { + return SourceExt.equals_lower(PathExt); + }); bool IsSource = SourceIter != std::end(SourceExtensions); auto HeaderIter = - std::find_if(std::begin(HeaderExtensions), std::end(HeaderExtensions), - [&PathExt](PathRef HeaderExt) { - return HeaderExt.equals_lower(PathExt); - }); + llvm::find_if(HeaderExtensions, [&PathExt](PathRef HeaderExt) { + return HeaderExt.equals_lower(PathExt); + }); bool IsHeader = HeaderIter != std::end(HeaderExtensions); diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 9e80afbc5..9c634f5e8 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -306,11 +306,10 @@ struct CodeCompletionBuilder { Completion.FixIts.push_back( toTextEdit(FixIt, ASTCtx.getSourceManager(), ASTCtx.getLangOpts())); } - std::sort(Completion.FixIts.begin(), Completion.FixIts.end(), - [](const TextEdit &X, const TextEdit &Y) { - return std::tie(X.range.start.line, X.range.start.character) < - std::tie(Y.range.start.line, Y.range.start.character); - }); + llvm::sort(Completion.FixIts, [](const TextEdit &X, const TextEdit &Y) { + return std::tie(X.range.start.line, X.range.start.character) < + std::tie(Y.range.start.line, Y.range.start.character); + }); Completion.Deprecated |= (C.SemaResult->Availability == CXAvailability_Deprecated); } @@ -861,8 +860,8 @@ class SignatureHelpCollector final : public CodeCompleteConsumer { IndexRequest.IDs.size(), FetchedDocs.size()); } - std::sort( - ScoredSignatures.begin(), ScoredSignatures.end(), + llvm::sort( + ScoredSignatures, [](const ScoredSignature &L, const ScoredSignature &R) { // Ordering follows: // - Less number of parameters is better. @@ -1164,13 +1163,12 @@ llvm::SmallVector getRankedIncludes(const Symbol &Sym) { auto Includes = Sym.IncludeHeaders; // Sort in descending order by reference count and header length. - std::sort(Includes.begin(), Includes.end(), - [](const Symbol::IncludeHeaderWithReferences &LHS, - const Symbol::IncludeHeaderWithReferences &RHS) { - if (LHS.References == RHS.References) - return LHS.IncludeHeader.size() < RHS.IncludeHeader.size(); - return LHS.References > RHS.References; - }); + llvm::sort(Includes, [](const Symbol::IncludeHeaderWithReferences &LHS, + const Symbol::IncludeHeaderWithReferences &RHS) { + if (LHS.References == RHS.References) + return LHS.IncludeHeader.size() < RHS.IncludeHeader.size(); + return LHS.References > RHS.References; + }); llvm::SmallVector Headers; for (const auto &Include : Includes) Headers.push_back(Include.IncludeHeader); diff --git a/clangd/TUScheduler.cpp b/clangd/TUScheduler.cpp index 6ac21341f..9a5095fd5 100644 --- a/clangd/TUScheduler.cpp +++ b/clangd/TUScheduler.cpp @@ -128,8 +128,7 @@ class TUScheduler::ASTCache { using KVPair = std::pair>; std::vector::iterator findByKey(Key K) { - return std::find_if(LRU.begin(), LRU.end(), - [K](const KVPair &P) { return P.first == K; }); + return llvm::find_if(LRU, [K](const KVPair &P) { return P.first == K; }); } std::mutex Mut; diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index 69c6aad21..7d9635e9c 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -104,21 +104,19 @@ class DeclarationAndMacrosFinder : public index::IndexDataConsumer { } // Sort results. Declarations being referenced explicitly come first. - std::sort(Result.begin(), Result.end(), - [](const DeclInfo &L, const DeclInfo &R) { - if (L.IsReferencedExplicitly != R.IsReferencedExplicitly) - return L.IsReferencedExplicitly > R.IsReferencedExplicitly; - return L.D->getBeginLoc() < R.D->getBeginLoc(); - }); + llvm::sort(Result, [](const DeclInfo &L, const DeclInfo &R) { + if (L.IsReferencedExplicitly != R.IsReferencedExplicitly) + return L.IsReferencedExplicitly > R.IsReferencedExplicitly; + return L.D->getBeginLoc() < R.D->getBeginLoc(); + }); return Result; } std::vector takeMacroInfos() { // Don't keep the same Macro info multiple times. - std::sort(MacroInfos.begin(), MacroInfos.end(), - [](const MacroDecl &Left, const MacroDecl &Right) { - return Left.Info < Right.Info; - }); + llvm::sort(MacroInfos, [](const MacroDecl &Left, const MacroDecl &Right) { + return Left.Info < Right.Info; + }); auto Last = std::unique(MacroInfos.begin(), MacroInfos.end(), [](const MacroDecl &Left, const MacroDecl &Right) { diff --git a/clangd/index/CanonicalIncludes.cpp b/clangd/index/CanonicalIncludes.cpp index 757c8ff2f..62dff1348 100644 --- a/clangd/index/CanonicalIncludes.cpp +++ b/clangd/index/CanonicalIncludes.cpp @@ -46,12 +46,11 @@ CanonicalIncludes::mapHeader(llvm::ArrayRef Headers, return SE->second; // Find the first header such that the extension is not '.inc', and isn't a // recognized non-header file - auto I = - std::find_if(Headers.begin(), Headers.end(), [](llvm::StringRef Include) { - // Skip .inc file whose including header file should - // be #included instead. - return !Include.endswith(".inc"); - }); + auto I = llvm::find_if(Headers, [](llvm::StringRef Include) { + // Skip .inc file whose including header file should + // be #included instead. + return !Include.endswith(".inc"); + }); if (I == Headers.end()) return Headers[0]; // Fallback to the declaring header. StringRef Header = *I; diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 8bc0cde30..59bc21ff1 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -128,8 +128,8 @@ std::unique_ptr FileSymbols::buildMemIndex() { for (auto &Sym : MergedRefs) { auto &SymRefs = Sym.second; // Sorting isn't required, but yields more stable results over rebuilds. - std::sort(SymRefs.begin(), SymRefs.end()); - std::copy(SymRefs.begin(), SymRefs.end(), back_inserter(RefsStorage)); + llvm::sort(SymRefs); + llvm::copy(SymRefs, back_inserter(RefsStorage)); AllRefs.try_emplace( Sym.first, ArrayRef(&RefsStorage[RefsStorage.size() - SymRefs.size()], diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index dec95fcf9..604927880 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -84,10 +84,8 @@ float quality(const Symbol &S) { } SymbolSlab::const_iterator SymbolSlab::find(const SymbolID &ID) const { - auto It = std::lower_bound(Symbols.begin(), Symbols.end(), ID, - [](const Symbol &S, const SymbolID &I) { - return S.ID < I; - }); + auto It = llvm::lower_bound( + Symbols, ID, [](const Symbol &S, const SymbolID &I) { return S.ID < I; }); if (It != Symbols.end() && It->ID == ID) return It; return Symbols.end(); @@ -112,8 +110,8 @@ void SymbolSlab::Builder::insert(const Symbol &S) { SymbolSlab SymbolSlab::Builder::build() && { Symbols = {Symbols.begin(), Symbols.end()}; // Force shrink-to-fit. // Sort symbols so the slab can binary search over them. - std::sort(Symbols.begin(), Symbols.end(), - [](const Symbol &L, const Symbol &R) { return L.ID < R.ID; }); + llvm::sort(Symbols, + [](const Symbol &L, const Symbol &R) { return L.ID < R.ID; }); // We may have unused strings from overwritten symbols. Build a new arena. BumpPtrAllocator NewArena; llvm::UniqueStringSaver Strings(NewArena); @@ -155,8 +153,8 @@ RefSlab RefSlab::Builder::build() && { Result.reserve(Refs.size()); for (auto &Sym : Refs) { auto &SymRefs = Sym.second; - std::sort(SymRefs.begin(), SymRefs.end()); - // TODO: do we really need to dedup? + llvm::sort(SymRefs); + // FIXME: do we really need to dedup? SymRefs.erase(std::unique(SymRefs.begin(), SymRefs.end()), SymRefs.end()); auto *Array = Arena.Allocate(SymRefs.size()); diff --git a/clangd/index/Serialization.cpp b/clangd/index/Serialization.cpp index 8784d73ee..e7d49da50 100644 --- a/clangd/index/Serialization.cpp +++ b/clangd/index/Serialization.cpp @@ -158,7 +158,7 @@ class StringTableOut { // Finalize the table and write it to OS. No more strings may be added. void finalize(raw_ostream &OS) { Sorted = {Unique.begin(), Unique.end()}; - std::sort(Sorted.begin(), Sorted.end()); + llvm::sort(Sorted); for (unsigned I = 0; I < Sorted.size(); ++I) Index.try_emplace({Sorted[I].data(), Sorted[I].size()}, I); diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp index 1a9432cd8..26941d2dc 100644 --- a/clangd/index/SymbolCollector.cpp +++ b/clangd/index/SymbolCollector.cpp @@ -129,8 +129,7 @@ bool isPrivateProtoDecl(const NamedDecl &ND) { // will include OUTER_INNER and exclude some_enum_constant. // FIXME: the heuristic relies on naming style (i.e. no underscore in // user-defined names) and can be improved. - return (ND.getKind() != Decl::EnumConstant) || - std::any_of(Name.begin(), Name.end(), islower); + return (ND.getKind() != Decl::EnumConstant) || llvm::any_of(Name, islower); } // We only collect #include paths for symbols that are suitable for global code diff --git a/clangd/index/dex/Dex.cpp b/clangd/index/dex/Dex.cpp index cb080b30e..1c6663b94 100644 --- a/clangd/index/dex/Dex.cpp +++ b/clangd/index/dex/Dex.cpp @@ -123,8 +123,7 @@ void Dex::buildIndex() { // Symbols are sorted by symbol qualities so that items in the posting lists // are stored in the descending order of symbol quality. - std::sort(begin(ScoredSymbols), end(ScoredSymbols), - std::greater>()); + llvm::sort(ScoredSymbols, std::greater>()); // SymbolQuality was empty up until now. SymbolQuality.resize(Symbols.size()); diff --git a/clangd/index/dex/Iterator.cpp b/clangd/index/dex/Iterator.cpp index 5155827f6..d1ad72f55 100644 --- a/clangd/index/dex/Iterator.cpp +++ b/clangd/index/dex/Iterator.cpp @@ -40,11 +40,10 @@ class AndIterator : public Iterator { // highest element starting from the front. When child iterators in the // beginning have smaller estimated size, the sync() will have less restarts // and become more effective. - std::sort(begin(Children), end(Children), - [](const std::unique_ptr &LHS, - const std::unique_ptr &RHS) { - return LHS->estimateSize() < RHS->estimateSize(); - }); + llvm::sort(Children, [](const std::unique_ptr &LHS, + const std::unique_ptr &RHS) { + return LHS->estimateSize() < RHS->estimateSize(); + }); } bool reachedEnd() const override { return ReachedEnd; } From bff7489a41db6a297cf04e60ed81fdbcec8c0378 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sun, 7 Oct 2018 17:21:08 +0000 Subject: [PATCH 327/686] [clangd] Migrate to LLVM STLExtras range API git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343946 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/FileDistance.cpp | 4 ++-- clangd/XRefs.cpp | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/clangd/FileDistance.cpp b/clangd/FileDistance.cpp index e24d030dd..e530ad523 100644 --- a/clangd/FileDistance.cpp +++ b/clangd/FileDistance.cpp @@ -72,8 +72,8 @@ FileDistance::FileDistance(StringMap Sources, Rest = parent_path(Rest, sys::path::Style::posix); auto NextHash = hash_value(Rest); auto &Down = DownEdges[NextHash]; - if (std::find(Down.begin(), Down.end(), Hash) == Down.end()) - DownEdges[NextHash].push_back(Hash); + if (!llvm::is_contained(Down, Hash)) + Down.push_back(Hash); // We can't just break after MaxUpTraversals, must still set DownEdges. if (I > S.getValue().MaxUpTraversals) { if (Cache.find(Hash) != Cache.end()) diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index 7d9635e9c..4c7182206 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -372,11 +372,10 @@ class ReferenceFinder : public index::IndexDataConsumer { } std::vector take() && { - std::sort(References.begin(), References.end(), - [](const Reference &L, const Reference &R) { - return std::tie(L.Loc, L.CanonicalTarget, L.Role) < - std::tie(R.Loc, R.CanonicalTarget, R.Role); - }); + llvm::sort(References, [](const Reference &L, const Reference &R) { + return std::tie(L.Loc, L.CanonicalTarget, L.Role) < + std::tie(R.Loc, R.CanonicalTarget, R.Role); + }); // We sometimes see duplicates when parts of the AST get traversed twice. References.erase( std::unique(References.begin(), References.end(), From 413382cfe284fb435d52c191a9cbe79174611c35 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Mon, 8 Oct 2018 10:44:54 +0000 Subject: [PATCH 328/686] [clangd] Update the out-of-date yaml-symbol-file flag in clangd. Summary: The flag is stale due to the recent changes of clangd indexer, this patch renames the flag to "index-file". Reviewers: sammccall Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52976 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343963 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/tool/ClangdMain.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index aebff5189..a7a9fddcc 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -158,11 +158,11 @@ static llvm::cl::opt HeaderInsertionDecorators( "an include line will be inserted or not."), llvm::cl::init(true)); -static llvm::cl::opt YamlSymbolFile( - "yaml-symbol-file", +static llvm::cl::opt IndexFile( + "index-file", llvm::cl::desc( - "YAML-format global symbol file to build the static index. Clangd will " - "use the static index for global code completion.\n" + "Index file to build the static index. The file must have been created " + "by a compatible clangd-index.\n" "WARNING: This option is experimental only, and will be removed " "eventually. Don't rely on it."), llvm::cl::init(""), llvm::cl::Hidden); @@ -288,12 +288,12 @@ int main(int argc, char *argv[]) { Opts.BuildDynamicSymbolIndex = EnableIndex; std::unique_ptr StaticIdx; std::future AsyncIndexLoad; // Block exit while loading the index. - if (EnableIndex && !YamlSymbolFile.empty()) { + if (EnableIndex && !IndexFile.empty()) { // Load the index asynchronously. Meanwhile SwapIndex returns no results. SwapIndex *Placeholder; StaticIdx.reset(Placeholder = new SwapIndex(llvm::make_unique())); AsyncIndexLoad = runAsync([Placeholder, &Opts] { - if (auto Idx = loadIndex(YamlSymbolFile, Opts.URISchemes, UseDex)) + if (auto Idx = loadIndex(IndexFile, Opts.URISchemes, UseDex)) Placeholder->reset(std::move(Idx)); }); if (RunSynchronously) From 0becf8e0b9d79077e86c11431be325dafd13a62d Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Mon, 8 Oct 2018 17:22:50 +0000 Subject: [PATCH 329/686] [clang-move] Dump whether a declaration is templated. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@343982 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-move/ClangMove.cpp | 14 ++++---- clang-move/ClangMove.h | 28 +++++++++------ clang-move/tool/ClangMoveMain.cpp | 6 ++-- unittests/clang-move/ClangMoveTests.cpp | 46 ++++++++++++++----------- 4 files changed, 55 insertions(+), 39 deletions(-) diff --git a/clang-move/ClangMove.cpp b/clang-move/ClangMove.cpp index f20cf1ec4..0f104e499 100644 --- a/clang-move/ClangMove.cpp +++ b/clang-move/ClangMove.cpp @@ -899,21 +899,21 @@ void ClangMoveTool::onEndOfTranslationUnit() { assert(Reporter); for (const auto *Decl : UnremovedDeclsInOldHeader) { auto Kind = Decl->getKind(); + bool Templated = Decl->isTemplated(); const std::string QualifiedName = Decl->getQualifiedNameAsString(); if (Kind == Decl::Kind::Var) - Reporter->reportDeclaration(QualifiedName, "Variable"); + Reporter->reportDeclaration(QualifiedName, "Variable", Templated); else if (Kind == Decl::Kind::Function || Kind == Decl::Kind::FunctionTemplate) - Reporter->reportDeclaration(QualifiedName, "Function"); + Reporter->reportDeclaration(QualifiedName, "Function", Templated); else if (Kind == Decl::Kind::ClassTemplate || Kind == Decl::Kind::CXXRecord) - Reporter->reportDeclaration(QualifiedName, "Class"); + Reporter->reportDeclaration(QualifiedName, "Class", Templated); else if (Kind == Decl::Kind::Enum) - Reporter->reportDeclaration(QualifiedName, "Enum"); - else if (Kind == Decl::Kind::Typedef || - Kind == Decl::Kind::TypeAlias || + Reporter->reportDeclaration(QualifiedName, "Enum", Templated); + else if (Kind == Decl::Kind::Typedef || Kind == Decl::Kind::TypeAlias || Kind == Decl::Kind::TypeAliasTemplate) - Reporter->reportDeclaration(QualifiedName, "TypeAlias"); + Reporter->reportDeclaration(QualifiedName, "TypeAlias", Templated); } return; } diff --git a/clang-move/ClangMove.h b/clang-move/ClangMove.h index 945c6db0a..94172181e 100644 --- a/clang-move/ClangMove.h +++ b/clang-move/ClangMove.h @@ -17,6 +17,7 @@ #include "clang/Tooling/Tooling.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" #include #include #include @@ -31,23 +32,30 @@ class DeclarationReporter { DeclarationReporter() = default; ~DeclarationReporter() = default; - void reportDeclaration(llvm::StringRef DeclarationName, - llvm::StringRef Type) { - DeclarationList.emplace_back(DeclarationName, Type); + void reportDeclaration(llvm::StringRef DeclarationName, llvm::StringRef Type, + bool Templated) { + DeclarationList.emplace_back(DeclarationName, Type, Templated); }; - // A pair. - // The DeclarationName is a fully qualified name for the declaration, like - // A::B::Foo. The DeclarationKind is a string represents the kind of the - // declaration, currently only "Function" and "Class" are supported. - typedef std::pair DeclarationPair; + struct Declaration { + Declaration(llvm::StringRef QName, llvm::StringRef Kind, bool Templated) + : QualifiedName(QName), Kind(Kind), Templated(Templated) {} + + friend bool operator==(const Declaration &LHS, const Declaration &RHS) { + return std::tie(LHS.QualifiedName, LHS.Kind, LHS.Templated) == + std::tie(RHS.QualifiedName, RHS.Kind, RHS.Templated); + } + std::string QualifiedName; // E.g. A::B::Foo. + std::string Kind; // E.g. Function, Class + bool Templated = false; // Whether the declaration is templated. + }; - const std::vector getDeclarationList() const { + const std::vector getDeclarationList() const { return DeclarationList; } private: - std::vector DeclarationList; + std::vector DeclarationList; }; // Specify declarations being moved. It contains all information of the moved diff --git a/clang-move/tool/ClangMoveMain.cpp b/clang-move/tool/ClangMoveMain.cpp index e5fee2635..b882b6ec2 100644 --- a/clang-move/tool/ClangMoveMain.cpp +++ b/clang-move/tool/ClangMoveMain.cpp @@ -138,8 +138,10 @@ int main(int argc, const char **argv) { const auto &Declarations = Reporter.getDeclarationList(); for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) { llvm::outs() << " {\n"; - llvm::outs() << " \"DeclarationName\": \"" << I->first << "\",\n"; - llvm::outs() << " \"DeclarationType\": \"" << I->second << "\"\n"; + llvm::outs() << " \"DeclarationName\": \"" << I->QualifiedName << "\",\n"; + llvm::outs() << " \"DeclarationType\": \"" << I->Kind << "\"\n"; + llvm::outs() << " \"Templated\": " << (I->Templated ? "true" : "false") + << "\n"; llvm::outs() << " }"; // Don't print trailing "," at the end of last element. if (I != std::prev(E)) diff --git a/unittests/clang-move/ClangMoveTests.cpp b/unittests/clang-move/ClangMoveTests.cpp index 45e3f6823..97c7ce075 100644 --- a/unittests/clang-move/ClangMoveTests.cpp +++ b/unittests/clang-move/ClangMoveTests.cpp @@ -16,6 +16,7 @@ #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/StringRef.h" +#include "gmock/gmock-matchers.h" #include "gtest/gtest.h" #include #include @@ -581,7 +582,8 @@ TEST(ClangMove, DumpDecls) { "namespace a {\n" "class Move1 {};\n" "void f1() {}\n" - "void f2();\n" + "template " + "void f2(T t);\n" "} // namespace a\n" "\n" "class ForwardClass;\n" @@ -594,6 +596,8 @@ TEST(ClangMove, DumpDecls) { "typedef int Int2;\n" "typedef A A_d;" "using Int = int;\n" + "template \n" + "using AA = A;\n" "extern int kGlobalInt;\n" "extern const char* const kGlobalStr;\n" "} // namespace b\n" @@ -608,26 +612,28 @@ TEST(ClangMove, DumpDecls) { Spec.NewHeader = "new_foo.h"; Spec.NewCC = "new_foo.cc"; DeclarationReporter Reporter; - std::set ExpectedDeclarations = { - {"A", "Class"}, - {"B", "Class"}, - {"a::Move1", "Class"}, - {"a::f1", "Function"}, - {"a::f2", "Function"}, - {"a::b::Move1", "Class"}, - {"a::b::f", "Function"}, - {"a::b::E1", "Enum"}, - {"a::b::E2", "Enum"}, - {"a::b::Int2", "TypeAlias"}, - {"a::b::A_d", "TypeAlias"}, - {"a::b::Int", "TypeAlias"}, - {"a::b::kGlobalInt", "Variable"}, - {"a::b::kGlobalStr", "Variable"}}; + std::vector ExpectedDeclarations = { + {"A", "Class", true}, + {"B", "Class", false}, + {"a::Move1", "Class", false}, + {"a::f1", "Function", false}, + {"a::f2", "Function", true}, + {"a::b::Move1", "Class", false}, + {"a::b::f", "Function", false}, + {"a::b::E1", "Enum", false}, + {"a::b::E2", "Enum", false}, + {"a::b::Int2", "TypeAlias", false}, + {"a::b::A_d", "TypeAlias", false}, + {"a::b::Int", "TypeAlias", false}, + {"a::b::AA", "TypeAlias", true}, + {"a::b::kGlobalInt", "Variable", false}, + {"a::b::kGlobalStr", "Variable", false}}; runClangMoveOnCode(Spec, TestHeader, TestCode, &Reporter); - std::set Results; - for (const auto& DelPair : Reporter.getDeclarationList()) - Results.insert(DelPair); - EXPECT_EQ(ExpectedDeclarations, Results); + std::vector Results; + for (const auto &DelPair : Reporter.getDeclarationList()) + Results.push_back(DelPair); + EXPECT_THAT(ExpectedDeclarations, + testing::UnorderedElementsAreArray(Results)); } } // namespace From 518d2ad09714a37426d8225c86ee53fae21c798e Mon Sep 17 00:00:00 2001 From: Zinovy Nis Date: Tue, 9 Oct 2018 05:40:03 +0000 Subject: [PATCH 330/686] [clang-tidy] The patch extends the existing command line option -check-suffix (with alias -check-suffixes) to accept multiple comma-separated FileCheck prefixes. Usage: // RUN: %check_clang_tidy -check-suffix=USING-C,USING-D %s misc-unused-using-decls %t -- -- ... or for the same: // RUN: %check_clang_tidy -check-suffixes=USING-C,USING-D %s misc-unused-using-decls %t -- -- ... Differential Revision: https://reviews.llvm.org/D52971 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344015 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clang-tidy/check_clang_tidy.cpp | 23 ++++++--- test/clang-tidy/check_clang_tidy.py | 77 +++++++++++++++++++--------- 2 files changed, 71 insertions(+), 29 deletions(-) diff --git a/test/clang-tidy/check_clang_tidy.cpp b/test/clang-tidy/check_clang_tidy.cpp index 5c1bf92ee..0e4c45d39 100644 --- a/test/clang-tidy/check_clang_tidy.cpp +++ b/test/clang-tidy/check_clang_tidy.cpp @@ -1,21 +1,32 @@ // RUN: %check_clang_tidy -check-suffix=USING-A %s misc-unused-using-decls %t -- -- -DUSING_A // RUN: %check_clang_tidy -check-suffix=USING-B %s misc-unused-using-decls %t -- -- -DUSING_B +// RUN: %check_clang_tidy -check-suffix=USING-C,USING-D %s misc-unused-using-decls %t -- -- -DUSING_C_D +// RUN: %check_clang_tidy -check-suffixes=USING-C,USING-D %s misc-unused-using-decls %t -- -- -DUSING_C_D // RUN: %check_clang_tidy %s misc-unused-using-decls %t -namespace a {class A {}; class B {}; class C {}; } +namespace a {class A {}; class B {}; class C {}; class D {}; class E {};} namespace b { #if defined(USING_A) using a::A; #elif defined(USING_B) using a::B; -#else +#elif defined(USING_C_D) using a::C; +using a::D; +#else +using a::E; #endif } namespace c {} -// CHECK-MESSAGES-USING-A: :[[@LINE-8]]:10: warning: using decl 'A' {{.*}} -// CHECK-MESSAGES-USING-B: :[[@LINE-7]]:10: warning: using decl 'B' {{.*}} -// CHECK-MESSAGES: :[[@LINE-6]]:10: warning: using decl 'C' {{.*}} +// CHECK-MESSAGES-USING-A: warning: using decl 'A' {{.*}} +// CHECK-MESSAGES-USING-B: warning: using decl 'B' {{.*}} +// CHECK-MESSAGES-USING-C: warning: using decl 'C' {{.*}} +// CHECK-MESSAGES-USING-D: warning: using decl 'D' {{.*}} +// CHECK-MESSAGES: warning: using decl 'E' {{.*}} // CHECK-FIXES-USING-A-NOT: using a::A;$ // CHECK-FIXES-USING-B-NOT: using a::B;$ -// CHECK-FIXES-NOT: using a::C;$ +// CHECK-FIXES-USING-C-NOT: using a::C;$ +// CHECK-FIXES-USING-C-NOT: using a::D;$ +// CHECK-FIXES-USING-D-NOT: using a::C;$ +// CHECK-FIXES-USING-D-NOT: using a::D;$ +// CHECK-FIXES-NOT: using a::E;$ diff --git a/test/clang-tidy/check_clang_tidy.py b/test/clang-tidy/check_clang_tidy.py index d1cefba3c..9d2b5f789 100755 --- a/test/clang-tidy/check_clang_tidy.py +++ b/test/clang-tidy/check_clang_tidy.py @@ -18,7 +18,8 @@ Usage: check_clang_tidy.py [-resource-dir=] \ [-assume-filename=] \ - [-check-suffix=] \ + [-check-suffix=] \ + [-check-suffixes=] \ \ -- [optional clang-tidy arguments] @@ -38,15 +39,20 @@ def write_file(file_name, text): f.write(text) f.truncate() +def csv(string): + return string.split(',') + def main(): parser = argparse.ArgumentParser() parser.add_argument('-expect-clang-tidy-error', action='store_true') parser.add_argument('-resource-dir') parser.add_argument('-assume-filename') - parser.add_argument('-check-suffix', default='') parser.add_argument('input_file_name') parser.add_argument('check_name') parser.add_argument('temp_file_name') + parser.add_argument('-check-suffix', '-check-suffixes', + default=[], type=csv, + help="comma-separated list of FileCheck suffixes") args, extra_args = parser.parse_known_args() @@ -72,14 +78,6 @@ def main(): clang_tidy_extra_args.extend( ['-fobjc-abi-version=2', '-fobjc-arc']) - if args.check_suffix and not re.match('^[A-Z0-9\-]+$', args.check_suffix): - sys.exit('Only A..Z, 0..9 and "-" are allowed in check suffix, but "%s" was given' % (args.check_suffix)) - - file_check_suffix = ('-' + args.check_suffix) if args.check_suffix else '' - check_fixes_prefix = 'CHECK-FIXES' + file_check_suffix - check_messages_prefix = 'CHECK-MESSAGES' + file_check_suffix - check_notes_prefix = 'CHECK-NOTES' + file_check_suffix - # Tests should not rely on STL being available, and instead provide mock # implementations of relevant APIs. clang_tidy_extra_args.append('-nostdinc++') @@ -90,16 +88,48 @@ def main(): with open(input_file_name, 'r') as input_file: input_text = input_file.read() - has_check_fixes = check_fixes_prefix in input_text - has_check_messages = check_messages_prefix in input_text - has_check_notes = check_notes_prefix in input_text - - if not has_check_fixes and not has_check_messages and not has_check_notes: - sys.exit('%s, %s or %s not found in the input' % (check_fixes_prefix, - check_messages_prefix, check_notes_prefix) ) - - if has_check_notes and has_check_messages: - sys.exit('Please use either CHECK-NOTES or CHECK-MESSAGES but not both') + check_fixes_prefixes = [] + check_messages_prefixes = [] + check_notes_prefixes = [] + + has_check_fixes = False + has_check_messages = False + has_check_notes = False + + if any(args.check_suffix): + for check in args.check_suffix: + if not re.match('^[A-Z0-9\-]+$', check): + sys.exit('Only A..Z, 0..9 and "-" are ' + + 'allowed in check suffixes list, but "%s" was given' % (check)) + + file_check_suffix = '-' + check + check_fixes_prefix = 'CHECK-FIXES' + file_check_suffix + check_messages_prefix = 'CHECK-MESSAGES' + file_check_suffix + check_notes_prefix = 'CHECK-NOTES' + file_check_suffix + + has_check_fix = check_fixes_prefix in input_text + has_check_message = check_messages_prefix in input_text + has_check_note = check_notes_prefix in input_text + + if has_check_note and has_check_message: + sys.exit('Please use either %s or %s but not both' % + (check_notes_prefix, check_messages_prefix)) + + if not has_check_fix and not has_check_message and not has_check_note: + sys.exit('%s, %s or %s not found in the input' % + (check_fixes_prefix, check_messages_prefix, check_notes_prefix)) + + has_check_fixes = has_check_fixes or has_check_fix + has_check_messages = has_check_messages or has_check_message + has_check_notes = has_check_notes or has_check_note + + check_fixes_prefixes.append(check_fixes_prefix) + check_messages_prefixes.append(check_messages_prefix) + check_notes_prefixes.append(check_notes_prefix) + else: + check_fixes_prefixes = ['CHECK-FIXES'] + check_messages_prefixes = ['CHECK-MESSAGES'] + check_notes_prefixes = ['CHECK-NOTES'] # Remove the contents of the CHECK lines to avoid CHECKs matching on # themselves. We need to keep the comments to preserve line numbers while @@ -143,7 +173,8 @@ def main(): try: subprocess.check_output( ['FileCheck', '-input-file=' + temp_file_name, input_file_name, - '-check-prefix=' + check_fixes_prefix, '-strict-whitespace'], + '-check-prefixes=' + ','.join(check_fixes_prefixes), + '-strict-whitespace'], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: print('FileCheck failed:\n' + e.output.decode()) @@ -155,7 +186,7 @@ def main(): try: subprocess.check_output( ['FileCheck', '-input-file=' + messages_file, input_file_name, - '-check-prefix=' + check_messages_prefix, + '-check-prefixes=' + ','.join(check_messages_prefixes), '-implicit-check-not={{warning|error}}:'], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: @@ -170,7 +201,7 @@ def main(): try: subprocess.check_output( ['FileCheck', '-input-file=' + notes_file, input_file_name, - '-check-prefix=' + check_notes_prefix, + '-check-prefixes=' + ','.join(check_notes_prefixes), '-implicit-check-not={{note|warning|error}}:'], stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: From 25356dd99be875396d025113d2fa7590a04a27b9 Mon Sep 17 00:00:00 2001 From: Zinovy Nis Date: Tue, 9 Oct 2018 05:48:57 +0000 Subject: [PATCH 331/686] [clang-tidy][docs] Update docs for `--check-suffixes` Differential Revision: https://reviews.llvm.org/D52971 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344016 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/clang-tidy/index.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/clang-tidy/index.rst b/docs/clang-tidy/index.rst index ab165d8c5..06963ae5f 100644 --- a/docs/clang-tidy/index.rst +++ b/docs/clang-tidy/index.rst @@ -680,9 +680,10 @@ source code is at `test/clang-tidy/google-readability-casting.cpp`_): // CHECK-FIXES: int b = a; } -To check more than one scenario in the same test file use -``-check-suffix=SUFFIX-NAME`` on ``check_clang_tidy.py`` command line. -With ``-check-suffix=SUFFIX-NAME`` you need to replace your ``CHECK-*`` +To check more than one scenario in the same test file use +``-check-suffix=SUFFIX-NAME`` on ``check_clang_tidy.py`` command line or +``-check-suffixes=SUFFIX-NAME-1,SUFFIX-NAME-2,...``. +With ``-check-suffix[es]=SUFFIX-NAME`` you need to replace your ``CHECK-*`` directives with ``CHECK-MESSAGES-SUFFIX-NAME`` and ``CHECK-FIXES-SUFFIX-NAME``. Here's an example: From 3d6c08d8c2f09f488ddc648a8ad245aba1f2eccc Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 9 Oct 2018 08:27:31 +0000 Subject: [PATCH 332/686] [clangd] Avoid cache main file status in preamble. Summary: Main file can certainly change when reusing preamble. Reviewers: sammccall Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D52991 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344024 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdUnit.cpp | 5 ++++- clangd/FS.cpp | 9 +++++++++ clangd/FS.h | 14 ++++++++++---- unittests/clangd/FSTests.cpp | 6 +++++- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/clangd/ClangdUnit.cpp b/clangd/ClangdUnit.cpp index 5717bc8a5..4cc6ef642 100644 --- a/clangd/ClangdUnit.cpp +++ b/clangd/ClangdUnit.cpp @@ -29,6 +29,7 @@ #include "clang/Serialization/ASTWriter.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/raw_ostream.h" @@ -336,7 +337,9 @@ std::shared_ptr clangd::buildPreamble( // dirs. } - auto StatCache = llvm::make_unique(); + llvm::SmallString<32> AbsFileName(FileName); + Inputs.FS->makeAbsolute(AbsFileName); + auto StatCache = llvm::make_unique(AbsFileName); auto BuiltPreamble = PrecompiledPreamble::Build( CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine, StatCache->getProducingFS(Inputs.FS), PCHs, StoreInMemory, diff --git a/clangd/FS.cpp b/clangd/FS.cpp index 3fe0c8e56..fa94b9672 100644 --- a/clangd/FS.cpp +++ b/clangd/FS.cpp @@ -10,14 +10,23 @@ #include "FS.h" #include "clang/Basic/VirtualFileSystem.h" #include "llvm/ADT/None.h" +#include "llvm/Support/Path.h" namespace clang { namespace clangd { +PreambleFileStatusCache::PreambleFileStatusCache(llvm::StringRef MainFilePath) + : MainFilePath(MainFilePath) { + assert(llvm::sys::path::is_absolute(MainFilePath)); +} + void PreambleFileStatusCache::update(const vfs::FileSystem &FS, vfs::Status S) { SmallString<32> PathStore(S.getName()); if (FS.makeAbsolute(PathStore)) return; + // Do not cache status for the main file. + if (PathStore == MainFilePath) + return; // Stores the latest status in cache as it can change in a preamble build. StatCache.insert({PathStore, std::move(S)}); } diff --git a/clangd/FS.h b/clangd/FS.h index 5f0fe62c6..33a8ac93e 100644 --- a/clangd/FS.h +++ b/clangd/FS.h @@ -17,10 +17,10 @@ namespace clang { namespace clangd { /// Records status information for files open()ed or stat()ed during preamble -/// build, so we can avoid stat()s on the underlying FS when reusing the -/// preamble. For example, code completion can re-stat files when getting FileID -/// for source locations stored in preamble (e.g. checking whether a location is -/// in the main file). +/// build (except for the main file), so we can avoid stat()s on the underlying +/// FS when reusing the preamble. For example, code completion can re-stat files +/// when getting FileID for source locations stored in preamble (e.g. checking +/// whether a location is in the main file). /// /// The cache is keyed by absolute path of file name in cached status, as this /// is what preamble stores. @@ -35,7 +35,12 @@ namespace clangd { /// Note that the cache is only valid when reusing preamble. class PreambleFileStatusCache { public: + /// \p MainFilePath is the absolute path of the main source file this preamble + /// corresponds to. The stat for the main file will not be cached. + PreambleFileStatusCache(llvm::StringRef MainFilePath); + void update(const vfs::FileSystem &FS, vfs::Status S); + /// \p Path is a path stored in preamble. llvm::Optional lookup(llvm::StringRef Path) const; @@ -56,6 +61,7 @@ class PreambleFileStatusCache { getConsumingFS(IntrusiveRefCntPtr FS) const; private: + std::string MainFilePath; llvm::StringMap StatCache; }; diff --git a/unittests/clangd/FSTests.cpp b/unittests/clangd/FSTests.cpp index 3b0284260..64e6f8a0e 100644 --- a/unittests/clangd/FSTests.cpp +++ b/unittests/clangd/FSTests.cpp @@ -20,16 +20,20 @@ TEST(FSTests, PreambleStatusCache) { llvm::StringMap Files; Files["x"] = ""; Files["y"] = ""; + Files["main"] = ""; auto FS = buildTestFS(Files); FS->setCurrentWorkingDirectory(testRoot()); - PreambleFileStatusCache StatCache; + PreambleFileStatusCache StatCache(testPath("main")); auto ProduceFS = StatCache.getProducingFS(FS); EXPECT_TRUE(ProduceFS->openFileForRead("x")); EXPECT_TRUE(ProduceFS->status("y")); + EXPECT_TRUE(ProduceFS->status("main")); EXPECT_TRUE(StatCache.lookup(testPath("x")).hasValue()); EXPECT_TRUE(StatCache.lookup(testPath("y")).hasValue()); + // Main file is not cached. + EXPECT_FALSE(StatCache.lookup(testPath("main")).hasValue()); vfs::Status S("fake", llvm::sys::fs::UniqueID(0, 0), std::chrono::system_clock::now(), 0, 0, 1024, From 4ad9374b527be8c2a8c7e9b31868a78998196a90 Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Tue, 9 Oct 2018 08:41:12 +0000 Subject: [PATCH 333/686] [clangd] Revert back to previous heuristic for diagnostic range extraction. Summary: Also add a few new test cases and a special case into handling of empty fixit ranges that collides with location of a diag. Reviewers: sammccall, ilya-biryukov Reviewed By: sammccall Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D52889 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344025 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/Diagnostics.cpp | 8 ++------ unittests/clangd/ClangdUnitTests.cpp | 11 +++++++++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/clangd/Diagnostics.cpp b/clangd/Diagnostics.cpp index a2f86eb94..b8ce2e0ed 100644 --- a/clangd/Diagnostics.cpp +++ b/clangd/Diagnostics.cpp @@ -51,16 +51,12 @@ bool locationInRange(SourceLocation L, CharSourceRange R, Range diagnosticRange(const clang::Diagnostic &D, const LangOptions &L) { auto &M = D.getSourceManager(); auto Loc = M.getFileLoc(D.getLocation()); - // Accept the first range that contains the location. - llvm::Optional FallbackRange; for (const auto &CR : D.getRanges()) { auto R = Lexer::makeFileCharRange(CR, M, L); if (locationInRange(Loc, R, M)) return halfOpenToRange(M, R); - // If there are no ranges that contain the location report the first range. - if (!FallbackRange) - FallbackRange = halfOpenToRange(M, R); } + llvm::Optional FallbackRange; // The range may be given as a fixit hint instead. for (const auto &F : D.getFixItHints()) { auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L); @@ -69,7 +65,7 @@ Range diagnosticRange(const clang::Diagnostic &D, const LangOptions &L) { // If there's a fixit that performs insertion, it has zero-width. Therefore // it can't contain the location of the diag, but it might be possible that // this should be reported as range. For example missing semicolon. - if (!FallbackRange && R.getBegin() == R.getEnd()) + if (R.getBegin() == R.getEnd() && Loc == R.getBegin()) FallbackRange = halfOpenToRange(M, R); } if (FallbackRange) diff --git a/unittests/clangd/ClangdUnitTests.cpp b/unittests/clangd/ClangdUnitTests.cpp index 51cfcfe48..fe3fc2006 100644 --- a/unittests/clangd/ClangdUnitTests.cpp +++ b/unittests/clangd/ClangdUnitTests.cpp @@ -75,13 +75,17 @@ Position pos(int line, int character) { TEST(DiagnosticsTest, DiagnosticRanges) { // Check we report correct ranges, including various edge-cases. Annotations Test(R"cpp( + namespace test{}; void $decl[[foo]](); int main() { $typo[[go\ o]](); foo()$semicolon[[]]//with comments $unk[[unknown]](); - double bar = $type[["foo"]]; + double $type[[bar]] = "foo"; + struct Foo { int x; }; Foo a; + a.$nomember[[y]]; + test::$nomembernamespace[[test]]; } )cpp"); EXPECT_THAT( @@ -103,7 +107,10 @@ o]](); Diag(Test.range("unk"), "use of undeclared identifier 'unknown'"), Diag(Test.range("type"), "cannot initialize a variable of type 'double' with an lvalue " - "of type 'const char [4]'"))); + "of type 'const char [4]'"), + Diag(Test.range("nomember"), "no member named 'y' in 'Foo'"), + Diag(Test.range("nomembernamespace"), + "no member named 'test' in namespace 'test'"))); } TEST(DiagnosticsTest, FlagsMatter) { From 4a978e1f13a7c6b9cd83a6838f4b25c8982e210c Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 9 Oct 2018 10:02:02 +0000 Subject: [PATCH 334/686] [clangd] Fix nondeterministic test git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344030 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/DexTests.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 66951f590..130659247 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -23,6 +23,7 @@ #include #include +using ::testing::AnyOf; using ::testing::ElementsAre; using ::testing::UnorderedElementsAre; using namespace llvm; @@ -257,7 +258,9 @@ TEST(DexIterators, StringRepresentation) { EXPECT_EQ(llvm::to_string(*I2), "T=L2"); auto Tree = C.limit(C.intersect(move(I1), move(I2)), 10); - EXPECT_EQ(llvm::to_string(*Tree), "(LIMIT 10 (& [1 3 5] T=L2))"); + // AND reorders its children, we don't care which order it prints. + EXPECT_THAT(llvm::to_string(*Tree), AnyOf("(LIMIT 10 (& [1 3 5] T=L2))", + "(LIMIT 10 (& T=L2 [1 3 5]))")); } TEST(DexIterators, Limit) { From 8f25351a954380f1c5fb44de722e33ec728f3eae Mon Sep 17 00:00:00 2001 From: Kadir Cetinkaya Date: Tue, 9 Oct 2018 10:29:54 +0000 Subject: [PATCH 335/686] [clangd] Mark colon as a safe character when percent-encoding. Summary: Also change output of percent-encoding to use upper-case letters. Reviewers: sammccall Reviewed By: sammccall Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D53016 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344033 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/URI.cpp | 4 +++- unittests/clangd/URITests.cpp | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/clangd/URI.cpp b/clangd/URI.cpp index 0eab8f5f6..7e75296d3 100644 --- a/clangd/URI.cpp +++ b/clangd/URI.cpp @@ -91,6 +91,8 @@ bool shouldEscape(unsigned char C) { case '.': case '~': case '/': // '/' is only reserved when parsing. + // ':' is only reserved for relative URI paths, which clangd doesn't produce. + case ':': return false; } return true; @@ -105,7 +107,7 @@ std::string percentEncode(llvm::StringRef Content) { llvm::raw_string_ostream OS(Result); for (unsigned char C : Content) if (shouldEscape(C)) - OS << '%' << llvm::format_hex_no_prefix(C, 2); + OS << '%' << llvm::format_hex_no_prefix(C, 2, /*Upper = */true); else OS << C; diff --git a/unittests/clangd/URITests.cpp b/unittests/clangd/URITests.cpp index fab144086..c9c571e48 100644 --- a/unittests/clangd/URITests.cpp +++ b/unittests/clangd/URITests.cpp @@ -44,8 +44,9 @@ URI parseOrDie(llvm::StringRef Uri) { TEST(PercentEncodingTest, Encode) { EXPECT_EQ(URI("x", /*Authority=*/"", "a/b/c").toString(), "x:a/b/c"); - EXPECT_EQ(URI("x", /*Authority=*/"", "a!b;c~").toString(), "x:a%21b%3bc~"); + EXPECT_EQ(URI("x", /*Authority=*/"", "a!b;c~").toString(), "x:a%21b%3Bc~"); EXPECT_EQ(URI("x", /*Authority=*/"", "a123b").toString(), "x:a123b"); + EXPECT_EQ(URI("x", /*Authority=*/"", "a:b;c").toString(), "x:a:b%3Bc"); } TEST(PercentEncodingTest, Decode) { @@ -56,6 +57,7 @@ TEST(PercentEncodingTest, Decode) { EXPECT_EQ(parseOrDie("s%2b://%3a/%3").body(), "/%3"); EXPECT_EQ(parseOrDie("x:a%21b%3ac~").body(), "a!b:c~"); + EXPECT_EQ(parseOrDie("x:a:b%3bc").body(), "a:b;c"); } std::string resolveOrDie(const URI &U, llvm::StringRef HintPath = "") { @@ -67,10 +69,10 @@ std::string resolveOrDie(const URI &U, llvm::StringRef HintPath = "") { TEST(URITest, Create) { #ifdef _WIN32 - EXPECT_THAT(createOrDie("c:\\x\\y\\z"), "file:///c%3a/x/y/z"); + EXPECT_THAT(createOrDie("c:\\x\\y\\z"), "file:///c:/x/y/z"); #else EXPECT_THAT(createOrDie("/x/y/z"), "file:///x/y/z"); - EXPECT_THAT(createOrDie("/(x)/y/\\ z"), "file:///%28x%29/y/%5c%20z"); + EXPECT_THAT(createOrDie("/(x)/y/\\ z"), "file:///%28x%29/y/%5C%20z"); #endif } @@ -138,6 +140,7 @@ TEST(URITest, ParseFailed) { TEST(URITest, Resolve) { #ifdef _WIN32 EXPECT_THAT(resolveOrDie(parseOrDie("file:///c%3a/x/y/z")), "c:\\x\\y\\z"); + EXPECT_THAT(resolveOrDie(parseOrDie("file:///c:/x/y/z")), "c:\\x\\y\\z"); #else EXPECT_EQ(resolveOrDie(parseOrDie("file:/a/b/c")), "/a/b/c"); EXPECT_EQ(resolveOrDie(parseOrDie("file://auth/a/b/c")), "/a/b/c"); From a221ab8a2715ffb09350e4078f5f195774657cba Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Tue, 9 Oct 2018 13:24:50 +0000 Subject: [PATCH 336/686] [clangd] fix miscompiling lower_bound call git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344044 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Index.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clangd/index/Index.cpp b/clangd/index/Index.cpp index 604927880..058be6ef2 100644 --- a/clangd/index/Index.cpp +++ b/clangd/index/Index.cpp @@ -84,8 +84,9 @@ float quality(const Symbol &S) { } SymbolSlab::const_iterator SymbolSlab::find(const SymbolID &ID) const { - auto It = llvm::lower_bound( - Symbols, ID, [](const Symbol &S, const SymbolID &I) { return S.ID < I; }); + auto It = std::lower_bound( + Symbols.begin(), Symbols.end(), ID, + [](const Symbol &S, const SymbolID &I) { return S.ID < I; }); if (It != Symbols.end() && It->ID == ID) return It; return Symbols.end(); From d4b116e4896c6e723a0b6e7a55c4b769edd5ac4e Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Tue, 9 Oct 2018 13:29:31 +0000 Subject: [PATCH 337/686] [clang-tidy] NFC fix warnings from missing braces The std::array create multiple StringRef but did not wrap them in braces. Some compilers warned for that. Adding the braces is not possible and result in a compilation error. This commit changes the array to vector which works without warning. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344046 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../modernize/DeprecatedIosBaseAliasesCheck.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp b/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp index 2e9dad99e..c9f0649a4 100644 --- a/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp +++ b/clang-tidy/modernize/DeprecatedIosBaseAliasesCheck.cpp @@ -17,11 +17,12 @@ namespace clang { namespace tidy { namespace modernize { -static const std::array DeprecatedTypes = { - "::std::ios_base::io_state", "::std::ios_base::open_mode", - "::std::ios_base::seek_dir", "::std::ios_base::streamoff", - "::std::ios_base::streampos", -}; +static const llvm::SmallVector DeprecatedTypes = { + {"::std::ios_base::io_state"}, + {"::std::ios_base::open_mode"}, + {"::std::ios_base::seek_dir"}, + {"::std::ios_base::streamoff"}, + {"::std::ios_base::streampos"}}; static const llvm::StringMap ReplacementTypes = { {"io_state", "iostate"}, From 64ff25767feed21a779d6ab56631c09dd766dc72 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Tue, 9 Oct 2018 15:16:14 +0000 Subject: [PATCH 338/686] [clangd] Fix an accident change in r342999. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344054 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/YAMLSerialization.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/index/YAMLSerialization.cpp b/clangd/index/YAMLSerialization.cpp index a426ccee9..06c4ef78d 100644 --- a/clangd/index/YAMLSerialization.cpp +++ b/clangd/index/YAMLSerialization.cpp @@ -286,7 +286,7 @@ std::string toYAML(const Symbol &S) { llvm::raw_string_ostream OS(Buf); llvm::yaml::Output Yout(OS); Symbol Sym = S; // copy: Yout<< requires mutability. - OS << Sym; + Yout << Sym; } return Buf; } From a88e7c4a20e7cd80baa1b9c7308debf04690ec13 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 9 Oct 2018 15:17:16 +0000 Subject: [PATCH 339/686] [clang-move] Fix broken json output. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344055 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-move/tool/ClangMoveMain.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/clang-move/tool/ClangMoveMain.cpp b/clang-move/tool/ClangMoveMain.cpp index b882b6ec2..f50e973a5 100644 --- a/clang-move/tool/ClangMoveMain.cpp +++ b/clang-move/tool/ClangMoveMain.cpp @@ -128,7 +128,7 @@ int main(int argc, const char **argv) { InitialDirectory.str(), Style, DumpDecls}; move::DeclarationReporter Reporter; move::ClangMoveActionFactory Factory(&Context, &Reporter); - + int CodeStatus = Tool.run(&Factory); if (CodeStatus) return CodeStatus; @@ -138,8 +138,9 @@ int main(int argc, const char **argv) { const auto &Declarations = Reporter.getDeclarationList(); for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) { llvm::outs() << " {\n"; - llvm::outs() << " \"DeclarationName\": \"" << I->QualifiedName << "\",\n"; - llvm::outs() << " \"DeclarationType\": \"" << I->Kind << "\"\n"; + llvm::outs() << " \"DeclarationName\": \"" << I->QualifiedName + << "\",\n"; + llvm::outs() << " \"DeclarationType\": \"" << I->Kind << "\",\n"; llvm::outs() << " \"Templated\": " << (I->Templated ? "true" : "false") << "\n"; llvm::outs() << " }"; From 35fc0545247c7a3b28632b4523f1923e87cafa9e Mon Sep 17 00:00:00 2001 From: Alexander Kornienko Date: Tue, 9 Oct 2018 15:58:18 +0000 Subject: [PATCH 340/686] [clang-tidy] Fix handling of parens around new expressions in make_ checks. Summary: Extra parentheses around a new expression result in incorrect code after applying fixes. Reviewers: hokein Reviewed By: hokein Subscribers: xazax.hun, cfe-commits Differential Revision: https://reviews.llvm.org/D52989 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344058 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/modernize/MakeSmartPtrCheck.cpp | 46 ++++++++++++++-------- clang-tidy/modernize/MakeSmartPtrCheck.h | 14 +++---- test/clang-tidy/modernize-make-shared.cpp | 12 ++++++ test/clang-tidy/modernize-make-unique.cpp | 13 ++++++ 4 files changed, 61 insertions(+), 24 deletions(-) diff --git a/clang-tidy/modernize/MakeSmartPtrCheck.cpp b/clang-tidy/modernize/MakeSmartPtrCheck.cpp index 97580fb78..df5e7735b 100644 --- a/clang-tidy/modernize/MakeSmartPtrCheck.cpp +++ b/clang-tidy/modernize/MakeSmartPtrCheck.cpp @@ -21,6 +21,9 @@ namespace modernize { namespace { constexpr char StdMemoryHeader[] = "memory"; +constexpr char ConstructorCall[] = "constructorCall"; +constexpr char ResetCall[] = "resetCall"; +constexpr char NewExpression[] = "newExpression"; std::string GetNewExprName(const CXXNewExpr *NewExpr, const SourceManager &SM, @@ -30,7 +33,7 @@ std::string GetNewExprName(const CXXNewExpr *NewExpr, NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()), SM, Lang); if (NewExpr->isArray()) { - return WrittenName.str() + "[]"; + return (WrittenName + "[]").str(); } return WrittenName.str(); } @@ -38,9 +41,6 @@ std::string GetNewExprName(const CXXNewExpr *NewExpr, } // namespace const char MakeSmartPtrCheck::PointerType[] = "pointerType"; -const char MakeSmartPtrCheck::ConstructorCall[] = "constructorCall"; -const char MakeSmartPtrCheck::ResetCall[] = "resetCall"; -const char MakeSmartPtrCheck::NewExpression[] = "newExpression"; MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext* Context, @@ -68,8 +68,8 @@ bool MakeSmartPtrCheck::isLanguageVersionSupported( void MakeSmartPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) { if (isLanguageVersionSupported(getLangOpts())) { - Inserter.reset(new utils::IncludeInserter( - Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle)); + Inserter = llvm::make_unique( + Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle); Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks()); } } @@ -122,12 +122,12 @@ void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) { return; if (Construct) - checkConstruct(SM, Construct, Type, New); + checkConstruct(SM, Result.Context, Construct, Type, New); else if (Reset) - checkReset(SM, Reset, New); + checkReset(SM, Result.Context, Reset, New); } -void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, +void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *Ctx, const CXXConstructExpr *Construct, const QualType *Type, const CXXNewExpr *New) { @@ -154,7 +154,7 @@ void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, return; } - if (!replaceNew(Diag, New, SM)) { + if (!replaceNew(Diag, New, SM, Ctx)) { return; } @@ -193,7 +193,7 @@ void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, insertHeader(Diag, SM.getFileID(ConstructCallStart)); } -void MakeSmartPtrCheck::checkReset(SourceManager &SM, +void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *Ctx, const CXXMemberCallExpr *Reset, const CXXNewExpr *New) { const auto *Expr = cast(Reset->getCallee()); @@ -224,7 +224,7 @@ void MakeSmartPtrCheck::checkReset(SourceManager &SM, return; } - if (!replaceNew(Diag, New, SM)) { + if (!replaceNew(Diag, New, SM, Ctx)) { return; } @@ -241,10 +241,24 @@ void MakeSmartPtrCheck::checkReset(SourceManager &SM, } bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag, - const CXXNewExpr *New, - SourceManager& SM) { - SourceLocation NewStart = New->getSourceRange().getBegin(); - SourceLocation NewEnd = New->getSourceRange().getEnd(); + const CXXNewExpr *New, SourceManager &SM, + ASTContext *Ctx) { + auto SkipParensParents = [&](const Expr *E) { + for (const Expr *OldE = nullptr; E != OldE;) { + OldE = E; + for (const auto &Node : Ctx->getParents(*E)) { + if (const Expr *Parent = Node.get()) { + E = Parent; + break; + } + } + } + return E; + }; + + SourceRange NewRange = SkipParensParents(New)->getSourceRange(); + SourceLocation NewStart = NewRange.getBegin(); + SourceLocation NewEnd = NewRange.getEnd(); // Skip when the source location of the new expression is invalid. if (NewStart.isInvalid() || NewEnd.isInvalid()) diff --git a/clang-tidy/modernize/MakeSmartPtrCheck.h b/clang-tidy/modernize/MakeSmartPtrCheck.h index 6622482a2..02428d7a6 100644 --- a/clang-tidy/modernize/MakeSmartPtrCheck.h +++ b/clang-tidy/modernize/MakeSmartPtrCheck.h @@ -44,9 +44,6 @@ class MakeSmartPtrCheck : public ClangTidyCheck { virtual bool isLanguageVersionSupported(const LangOptions &LangOpts) const; static const char PointerType[]; - static const char ConstructorCall[]; - static const char ResetCall[]; - static const char NewExpression[]; private: std::unique_ptr Inserter; @@ -55,14 +52,15 @@ class MakeSmartPtrCheck : public ClangTidyCheck { const std::string MakeSmartPtrFunctionName; const bool IgnoreMacros; - void checkConstruct(SourceManager &SM, const CXXConstructExpr *Construct, - const QualType *Type, const CXXNewExpr *New); - void checkReset(SourceManager &SM, const CXXMemberCallExpr *Member, - const CXXNewExpr *New); + void checkConstruct(SourceManager &SM, ASTContext *Ctx, + const CXXConstructExpr *Construct, const QualType *Type, + const CXXNewExpr *New); + void checkReset(SourceManager &SM, ASTContext *Ctx, + const CXXMemberCallExpr *Member, const CXXNewExpr *New); /// Returns true when the fixes for replacing CXXNewExpr are generated. bool replaceNew(DiagnosticBuilder &Diag, const CXXNewExpr *New, - SourceManager &SM); + SourceManager &SM, ASTContext *Ctx); void insertHeader(DiagnosticBuilder &Diag, FileID FD); }; diff --git a/test/clang-tidy/modernize-make-shared.cpp b/test/clang-tidy/modernize-make-shared.cpp index ed4da998e..49012dc18 100644 --- a/test/clang-tidy/modernize-make-shared.cpp +++ b/test/clang-tidy/modernize-make-shared.cpp @@ -70,6 +70,18 @@ void basic() { // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_shared instead // CHECK-FIXES: auto P3 = std::make_shared(); + std::shared_ptr P4 = std::shared_ptr((new int)); + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_shared instead [modernize-make-shared] + // CHECK-FIXES: std::shared_ptr P4 = std::make_shared(); + + P4.reset((((new int())))); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_shared instead [modernize-make-shared] + // CHECK-FIXES: P4 = std::make_shared(); + + P4 = std::shared_ptr(((new int))); + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_shared instead [modernize-make-shared] + // CHECK-FIXES: P4 = std::make_shared(); + { // No std. using namespace std; diff --git a/test/clang-tidy/modernize-make-unique.cpp b/test/clang-tidy/modernize-make-unique.cpp index 968520344..d2a73f3f3 100644 --- a/test/clang-tidy/modernize-make-unique.cpp +++ b/test/clang-tidy/modernize-make-unique.cpp @@ -110,6 +110,19 @@ void basic() { // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_unique instead // CHECK-FIXES: auto P3 = std::make_unique(); + std::unique_ptr P4 = std::unique_ptr((new int)); + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_unique instead [modernize-make-unique] + // CHECK-FIXES: std::unique_ptr P4 = std::make_unique(); + P4.reset((new int)); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_unique instead [modernize-make-unique] + // CHECK-FIXES: P4 = std::make_unique(); + std::unique_ptr P5 = std::unique_ptr((((new int)))); + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_unique instead [modernize-make-unique] + // CHECK-FIXES: std::unique_ptr P5 = std::make_unique(); + P5.reset(((((new int))))); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_unique instead [modernize-make-unique] + // CHECK-FIXES: P5 = std::make_unique(); + { // No std. using namespace std; From 58f7bb3f81b5522c68bdfe1d9f4c61cb36aae1c4 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Wed, 10 Oct 2018 07:46:15 +0000 Subject: [PATCH 341/686] [clangd] Make FSProvider const-correct. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344118 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 4 ++-- clangd/ClangdServer.h | 7 ++++--- clangd/FSProvider.h | 4 ++-- unittests/clangd/ClangdTests.cpp | 6 +++--- unittests/clangd/TestFS.h | 2 +- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index f0b94cb95..bdba66e0a 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -97,8 +97,8 @@ ClangdServer::Options ClangdServer::optsForTest() { return Opts; } -ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB, - FileSystemProvider &FSProvider, +ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB, + const FileSystemProvider &FSProvider, DiagnosticsConsumer &DiagConsumer, const Options &Opts) : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider), diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h index c66ed868a..9f17e73c0 100644 --- a/clangd/ClangdServer.h +++ b/clangd/ClangdServer.h @@ -109,7 +109,8 @@ class ClangdServer { /// \p DiagConsumer. Note that a callback to \p DiagConsumer happens on a /// worker thread. Therefore, instances of \p DiagConsumer must properly /// synchronize access to shared state. - ClangdServer(GlobalCompilationDatabase &CDB, FileSystemProvider &FSProvider, + ClangdServer(const GlobalCompilationDatabase &CDB, + const FileSystemProvider &FSProvider, DiagnosticsConsumer &DiagConsumer, const Options &Opts); /// Set the root path of the workspace. @@ -227,9 +228,9 @@ class ClangdServer { tooling::CompileCommand getCompileCommand(PathRef File); - GlobalCompilationDatabase &CDB; + const GlobalCompilationDatabase &CDB; DiagnosticsConsumer &DiagConsumer; - FileSystemProvider &FSProvider; + const FileSystemProvider &FSProvider; /// Used to synchronize diagnostic responses for added and removed files. llvm::StringMap InternalVersion; diff --git a/clangd/FSProvider.h b/clangd/FSProvider.h index 70dfb441a..a11d38101 100644 --- a/clangd/FSProvider.h +++ b/clangd/FSProvider.h @@ -25,13 +25,13 @@ class FileSystemProvider { /// Context::current() will be the context passed to the clang entrypoint, /// such as addDocument(), and will also be propagated to result callbacks. /// Embedders may use this to isolate filesystem accesses. - virtual IntrusiveRefCntPtr getFileSystem() = 0; + virtual IntrusiveRefCntPtr getFileSystem() const = 0; }; class RealFileSystemProvider : public FileSystemProvider { public: // FIXME: returns the single real FS instance, which is not threadsafe. - IntrusiveRefCntPtr getFileSystem() override { + IntrusiveRefCntPtr getFileSystem() const override { return vfs::getRealFileSystem(); } }; diff --git a/unittests/clangd/ClangdTests.cpp b/unittests/clangd/ClangdTests.cpp index b9003a863..87741dfb0 100644 --- a/unittests/clangd/ClangdTests.cpp +++ b/unittests/clangd/ClangdTests.cpp @@ -264,11 +264,11 @@ int b = a; TEST_F(ClangdVFSTest, PropagatesContexts) { static Key Secret; struct FSProvider : public FileSystemProvider { - IntrusiveRefCntPtr getFileSystem() override { + IntrusiveRefCntPtr getFileSystem() const override { Got = Context::current().getExisting(Secret); return buildTestFS({}); } - int Got; + mutable int Got; } FS; struct DiagConsumer : public DiagnosticsConsumer { void onDiagnosticsReady(PathRef File, @@ -973,7 +973,7 @@ TEST(ClangdTests, PreambleVFSStatCache) { ListenStatsFSProvider(llvm::StringMap &CountStats) : CountStats(CountStats) {} - IntrusiveRefCntPtr getFileSystem() override { + IntrusiveRefCntPtr getFileSystem() const override { class ListenStatVFS : public vfs::ProxyFileSystem { public: ListenStatVFS(IntrusiveRefCntPtr FS, diff --git a/unittests/clangd/TestFS.h b/unittests/clangd/TestFS.h index a0efb755d..39c0bb22b 100644 --- a/unittests/clangd/TestFS.h +++ b/unittests/clangd/TestFS.h @@ -29,7 +29,7 @@ buildTestFS(llvm::StringMap const &Files, // A VFS provider that returns TestFSes containing a provided set of files. class MockFSProvider : public FileSystemProvider { public: - IntrusiveRefCntPtr getFileSystem() override { + IntrusiveRefCntPtr getFileSystem() const override { return buildTestFS(Files); } From 04d1afc9dda12d82b784a588fc60c642362da7b9 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Wed, 10 Oct 2018 13:27:25 +0000 Subject: [PATCH 342/686] Lift VFS from clang to llvm (NFC) This patch moves the virtual file system form clang to llvm so it can be used by more projects. Concretely the patch: - Moves VirtualFileSystem.{h|cpp} from clang/Basic to llvm/Support. - Moves the corresponding unit test from clang to llvm. - Moves the vfs namespace from clang::vfs to llvm::vfs. - Formats the lines affected by this change, mostly this is the result of the added llvm namespace. RFC on the mailing list: http://lists.llvm.org/pipermail/llvm-dev/2018-October/126657.html Differential revision: https://reviews.llvm.org/D52783 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344140 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/ClangTidy.cpp | 8 ++--- clang-tidy/ClangTidy.h | 4 +-- clang-tidy/ClangTidyOptions.cpp | 4 +-- clang-tidy/ClangTidyOptions.h | 15 ++++----- clangd/ClangdUnit.cpp | 2 +- clangd/ClangdUnit.h | 12 +++---- clangd/CodeComplete.cpp | 8 ++--- clangd/CodeComplete.h | 4 +-- clangd/Compiler.cpp | 2 +- clangd/Compiler.h | 6 ++-- clangd/FS.cpp | 31 ++++++++++--------- clangd/FS.h | 17 +++++----- clangd/FSProvider.h | 8 ++--- clangd/Headers.h | 2 +- .../change-namespace/ChangeNamespaceTests.cpp | 2 +- unittests/clang-tidy/ClangTidyTest.h | 4 +-- unittests/clangd/ClangdTests.cpp | 12 +++---- unittests/clangd/FSTests.cpp | 7 +++-- unittests/clangd/SymbolCollectorTests.cpp | 6 ++-- unittests/clangd/TestFS.h | 6 ++-- unittests/include-fixer/IncludeFixerTest.cpp | 4 +-- .../find-all-symbols/FindAllSymbolsTests.cpp | 6 ++-- 22 files changed, 88 insertions(+), 82 deletions(-) diff --git a/clang-tidy/ClangTidy.cpp b/clang-tidy/ClangTidy.cpp index 729371fb8..9ab85b613 100644 --- a/clang-tidy/ClangTidy.cpp +++ b/clang-tidy/ClangTidy.cpp @@ -96,7 +96,7 @@ class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer { class ErrorReporter { public: ErrorReporter(ClangTidyContext &Context, bool ApplyFixes, - llvm::IntrusiveRefCntPtr BaseFS) + llvm::IntrusiveRefCntPtr BaseFS) : Files(FileSystemOptions(), BaseFS), DiagOpts(new DiagnosticOptions()), DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)), Diags(IntrusiveRefCntPtr(new DiagnosticIDs), &*DiagOpts, @@ -503,7 +503,7 @@ getCheckOptions(const ClangTidyOptions &Options, void runClangTidy(clang::tidy::ClangTidyContext &Context, const CompilationDatabase &Compilations, ArrayRef InputFiles, - llvm::IntrusiveRefCntPtr BaseFS, + llvm::IntrusiveRefCntPtr BaseFS, bool EnableCheckProfile, llvm::StringRef StoreCheckProfile) { ClangTool Tool(Compilations, InputFiles, std::make_shared(), BaseFS); @@ -590,9 +590,9 @@ void runClangTidy(clang::tidy::ClangTidyContext &Context, void handleErrors(ClangTidyContext &Context, bool Fix, unsigned &WarningsAsErrorsCount, - llvm::IntrusiveRefCntPtr BaseFS) { + llvm::IntrusiveRefCntPtr BaseFS) { ErrorReporter Reporter(Context, Fix, BaseFS); - vfs::FileSystem &FileSystem = + llvm::vfs::FileSystem &FileSystem = *Reporter.getSourceManager().getFileManager().getVirtualFileSystem(); auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory(); if (!InitialWorkingDir) diff --git a/clang-tidy/ClangTidy.h b/clang-tidy/ClangTidy.h index 0ea9a7018..031a7a2b3 100644 --- a/clang-tidy/ClangTidy.h +++ b/clang-tidy/ClangTidy.h @@ -233,7 +233,7 @@ getCheckOptions(const ClangTidyOptions &Options, void runClangTidy(clang::tidy::ClangTidyContext &Context, const tooling::CompilationDatabase &Compilations, ArrayRef InputFiles, - llvm::IntrusiveRefCntPtr BaseFS, + llvm::IntrusiveRefCntPtr BaseFS, bool EnableCheckProfile = false, llvm::StringRef StoreCheckProfile = StringRef()); @@ -245,7 +245,7 @@ void runClangTidy(clang::tidy::ClangTidyContext &Context, /// clang-format configuration file is found, the given \P FormatStyle is used. void handleErrors(ClangTidyContext &Context, bool Fix, unsigned &WarningsAsErrorsCount, - llvm::IntrusiveRefCntPtr BaseFS); + llvm::IntrusiveRefCntPtr BaseFS); /// \brief Serializes replacements into YAML and writes them to the specified /// output stream. diff --git a/clang-tidy/ClangTidyOptions.cpp b/clang-tidy/ClangTidyOptions.cpp index 430f8b31e..c40e97c5f 100644 --- a/clang-tidy/ClangTidyOptions.cpp +++ b/clang-tidy/ClangTidyOptions.cpp @@ -204,11 +204,11 @@ FileOptionsProvider::FileOptionsProvider( const ClangTidyGlobalOptions &GlobalOptions, const ClangTidyOptions &DefaultOptions, const ClangTidyOptions &OverrideOptions, - llvm::IntrusiveRefCntPtr VFS) + llvm::IntrusiveRefCntPtr VFS) : DefaultOptionsProvider(GlobalOptions, DefaultOptions), OverrideOptions(OverrideOptions), FS(std::move(VFS)) { if (!FS) - FS = vfs::getRealFileSystem(); + FS = llvm::vfs::getRealFileSystem(); ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration); } diff --git a/clang-tidy/ClangTidyOptions.h b/clang-tidy/ClangTidyOptions.h index b2a4ce4db..1a7535447 100644 --- a/clang-tidy/ClangTidyOptions.h +++ b/clang-tidy/ClangTidyOptions.h @@ -10,12 +10,12 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H +#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" -#include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/ErrorOr.h" -#include "clang/Basic/VirtualFileSystem.h" +#include "llvm/Support/VirtualFileSystem.h" #include #include #include @@ -218,10 +218,11 @@ class FileOptionsProvider : public DefaultOptionsProvider { /// /// If any of the \param OverrideOptions fields are set, they will override /// whatever options are read from the configuration file. - FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions, - const ClangTidyOptions &DefaultOptions, - const ClangTidyOptions &OverrideOptions, - llvm::IntrusiveRefCntPtr FS = nullptr); + FileOptionsProvider( + const ClangTidyGlobalOptions &GlobalOptions, + const ClangTidyOptions &DefaultOptions, + const ClangTidyOptions &OverrideOptions, + llvm::IntrusiveRefCntPtr FS = nullptr); /// \brief Initializes the \c FileOptionsProvider instance with a custom set /// of configuration file handlers. @@ -255,7 +256,7 @@ class FileOptionsProvider : public DefaultOptionsProvider { llvm::StringMap CachedOptions; ClangTidyOptions OverrideOptions; ConfigFileHandlers ConfigHandlers; - llvm::IntrusiveRefCntPtr FS; + llvm::IntrusiveRefCntPtr FS; }; /// \brief Parses LineFilter from JSON and stores it to the \p Options. diff --git a/clangd/ClangdUnit.cpp b/clangd/ClangdUnit.cpp index 4cc6ef642..85c7fd86a 100644 --- a/clangd/ClangdUnit.cpp +++ b/clangd/ClangdUnit.cpp @@ -126,7 +126,7 @@ ParsedAST::build(std::unique_ptr CI, std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS) { + IntrusiveRefCntPtr VFS) { assert(CI); // Command-line parsing sets DisableFree to true by default, but we don't want // to leak memory in clangd. diff --git a/clangd/ClangdUnit.h b/clangd/ClangdUnit.h index 184a7a6c5..15bf998d8 100644 --- a/clangd/ClangdUnit.h +++ b/clangd/ClangdUnit.h @@ -28,14 +28,14 @@ namespace llvm { class raw_ostream; -} - -namespace clang { -class PCHContainerOperations; namespace vfs { class FileSystem; } +} // namespace llvm + +namespace clang { +class PCHContainerOperations; namespace tooling { struct CompileCommand; @@ -63,7 +63,7 @@ struct PreambleData { /// Information required to run clang, e.g. to parse AST or do code completion. struct ParseInputs { tooling::CompileCommand CompileCommand; - IntrusiveRefCntPtr FS; + IntrusiveRefCntPtr FS; std::string Contents; }; @@ -77,7 +77,7 @@ class ParsedAST { std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS); + IntrusiveRefCntPtr VFS); ParsedAST(ParsedAST &&Other); ParsedAST &operator=(ParsedAST &&Other); diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp index 9c634f5e8..92018b193 100644 --- a/clangd/CodeComplete.cpp +++ b/clangd/CodeComplete.cpp @@ -986,7 +986,7 @@ struct SemaCompleteInput { const PreambleData *Preamble; StringRef Contents; Position Pos; - IntrusiveRefCntPtr VFS; + IntrusiveRefCntPtr VFS; std::shared_ptr PCHs; }; @@ -1007,7 +1007,7 @@ bool semaCodeComplete(std::unique_ptr Consumer, // working dirs. } - IntrusiveRefCntPtr VFS = Input.VFS; + IntrusiveRefCntPtr VFS = Input.VFS; if (Input.Preamble && Input.Preamble->StatCache) VFS = Input.Preamble->StatCache->getConsumingFS(std::move(VFS)); IgnoreDiagnostics DummyDiagsConsumer; @@ -1567,7 +1567,7 @@ speculateCompletionFilter(llvm::StringRef Content, Position Pos) { CodeCompleteResult codeComplete(PathRef FileName, const tooling::CompileCommand &Command, const PreambleData *Preamble, StringRef Contents, Position Pos, - IntrusiveRefCntPtr VFS, + IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, CodeCompleteOptions Opts, SpeculativeFuzzyFind *SpecFuzzyFind) { return CodeCompleteFlow(FileName, @@ -1580,7 +1580,7 @@ SignatureHelp signatureHelp(PathRef FileName, const tooling::CompileCommand &Command, const PreambleData *Preamble, StringRef Contents, Position Pos, - IntrusiveRefCntPtr VFS, + IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, const SymbolIndex *Index) { SignatureHelp Result; diff --git a/clangd/CodeComplete.h b/clangd/CodeComplete.h index d0c8a7067..4d3241427 100644 --- a/clangd/CodeComplete.h +++ b/clangd/CodeComplete.h @@ -224,7 +224,7 @@ CodeCompleteResult codeComplete(PathRef FileName, const tooling::CompileCommand &Command, const PreambleData *Preamble, StringRef Contents, Position Pos, - IntrusiveRefCntPtr VFS, + IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, CodeCompleteOptions Opts, SpeculativeFuzzyFind *SpecFuzzyFind = nullptr); @@ -234,7 +234,7 @@ SignatureHelp signatureHelp(PathRef FileName, const tooling::CompileCommand &Command, const PreambleData *Preamble, StringRef Contents, Position Pos, - IntrusiveRefCntPtr VFS, + IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, const SymbolIndex *Index); diff --git a/clangd/Compiler.cpp b/clangd/Compiler.cpp index 9c7a59b49..27967f9ec 100644 --- a/clangd/Compiler.cpp +++ b/clangd/Compiler.cpp @@ -44,7 +44,7 @@ prepareCompilerInstance(std::unique_ptr CI, const PrecompiledPreamble *Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS, + IntrusiveRefCntPtr VFS, DiagnosticConsumer &DiagsClient) { assert(VFS && "VFS is null"); assert(!CI->getPreprocessorOpts().RetainRemappedFileBuffers && diff --git a/clangd/Compiler.h b/clangd/Compiler.h index 49c1c90c4..7a3c43d61 100644 --- a/clangd/Compiler.h +++ b/clangd/Compiler.h @@ -37,8 +37,8 @@ class IgnoreDiagnostics : public DiagnosticConsumer { /// - Preamble is overriden to use PCH passed to this function. It means the /// changes to the preamble headers or files included in the preamble are /// not visible to this compiler instance. -/// - vfs::FileSystem is used for all underlying file accesses. The actual -/// vfs used by the compiler may be an overlay over the passed vfs. +/// - llvm::vfs::FileSystem is used for all underlying file accesses. The +/// actual vfs used by the compiler may be an overlay over the passed vfs. /// Returns null on errors. When non-null value is returned, it is expected to /// be consumed by FrontendAction::BeginSourceFile to properly destroy \p /// MainFile. @@ -46,7 +46,7 @@ std::unique_ptr prepareCompilerInstance( std::unique_ptr, const PrecompiledPreamble *, std::unique_ptr MainFile, std::shared_ptr, - IntrusiveRefCntPtr, DiagnosticConsumer &); + IntrusiveRefCntPtr, DiagnosticConsumer &); } // namespace clangd } // namespace clang diff --git a/clangd/FS.cpp b/clangd/FS.cpp index fa94b9672..5f5dd5c64 100644 --- a/clangd/FS.cpp +++ b/clangd/FS.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// #include "FS.h" -#include "clang/Basic/VirtualFileSystem.h" +#include "clang/Basic/LLVM.h" #include "llvm/ADT/None.h" #include "llvm/Support/Path.h" @@ -20,7 +20,8 @@ PreambleFileStatusCache::PreambleFileStatusCache(llvm::StringRef MainFilePath) assert(llvm::sys::path::is_absolute(MainFilePath)); } -void PreambleFileStatusCache::update(const vfs::FileSystem &FS, vfs::Status S) { +void PreambleFileStatusCache::update(const llvm::vfs::FileSystem &FS, + llvm::vfs::Status S) { SmallString<32> PathStore(S.getName()); if (FS.makeAbsolute(PathStore)) return; @@ -31,7 +32,7 @@ void PreambleFileStatusCache::update(const vfs::FileSystem &FS, vfs::Status S) { StatCache.insert({PathStore, std::move(S)}); } -llvm::Optional +llvm::Optional PreambleFileStatusCache::lookup(llvm::StringRef File) const { auto I = StatCache.find(File); if (I != StatCache.end()) @@ -39,17 +40,18 @@ PreambleFileStatusCache::lookup(llvm::StringRef File) const { return llvm::None; } -IntrusiveRefCntPtr PreambleFileStatusCache::getProducingFS( - IntrusiveRefCntPtr FS) { +IntrusiveRefCntPtr +PreambleFileStatusCache::getProducingFS( + IntrusiveRefCntPtr FS) { // This invalidates old status in cache if files are re-`open()`ed or // re-`stat()`ed in case file status has changed during preamble build. - class CollectFS : public vfs::ProxyFileSystem { + class CollectFS : public llvm::vfs::ProxyFileSystem { public: - CollectFS(IntrusiveRefCntPtr FS, + CollectFS(IntrusiveRefCntPtr FS, PreambleFileStatusCache &StatCache) : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {} - llvm::ErrorOr> + llvm::ErrorOr> openFileForRead(const Twine &Path) override { auto File = getUnderlyingFS().openFileForRead(Path); if (!File || !*File) @@ -64,7 +66,7 @@ IntrusiveRefCntPtr PreambleFileStatusCache::getProducingFS( return File; } - llvm::ErrorOr status(const Twine &Path) override { + llvm::ErrorOr status(const Twine &Path) override { auto S = getUnderlyingFS().status(Path); if (S) StatCache.update(getUnderlyingFS(), *S); @@ -77,15 +79,16 @@ IntrusiveRefCntPtr PreambleFileStatusCache::getProducingFS( return IntrusiveRefCntPtr(new CollectFS(std::move(FS), *this)); } -IntrusiveRefCntPtr PreambleFileStatusCache::getConsumingFS( - IntrusiveRefCntPtr FS) const { - class CacheVFS : public vfs::ProxyFileSystem { +IntrusiveRefCntPtr +PreambleFileStatusCache::getConsumingFS( + IntrusiveRefCntPtr FS) const { + class CacheVFS : public llvm::vfs::ProxyFileSystem { public: - CacheVFS(IntrusiveRefCntPtr FS, + CacheVFS(IntrusiveRefCntPtr FS, const PreambleFileStatusCache &StatCache) : ProxyFileSystem(std::move(FS)), StatCache(StatCache) {} - llvm::ErrorOr status(const Twine &Path) override { + llvm::ErrorOr status(const Twine &Path) override { if (auto S = StatCache.lookup(Path.str())) return *S; return getUnderlyingFS().status(Path); diff --git a/clangd/FS.h b/clangd/FS.h index 33a8ac93e..d40030400 100644 --- a/clangd/FS.h +++ b/clangd/FS.h @@ -10,8 +10,9 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FS_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FS_H -#include "clang/Basic/VirtualFileSystem.h" +#include "clang/Basic/LLVM.h" #include "llvm/ADT/Optional.h" +#include "llvm/Support/VirtualFileSystem.h" namespace clang { namespace clangd { @@ -39,10 +40,10 @@ class PreambleFileStatusCache { /// corresponds to. The stat for the main file will not be cached. PreambleFileStatusCache(llvm::StringRef MainFilePath); - void update(const vfs::FileSystem &FS, vfs::Status S); + void update(const llvm::vfs::FileSystem &FS, llvm::vfs::Status S); /// \p Path is a path stored in preamble. - llvm::Optional lookup(llvm::StringRef Path) const; + llvm::Optional lookup(llvm::StringRef Path) const; /// Returns a VFS that collects file status. /// Only cache stats for files that exist because @@ -51,18 +52,18 @@ class PreambleFileStatusCache { /// 2) we use the file name in the Status as the cache key. /// /// Note that the returned VFS should not outlive the cache. - IntrusiveRefCntPtr - getProducingFS(IntrusiveRefCntPtr FS); + IntrusiveRefCntPtr + getProducingFS(IntrusiveRefCntPtr FS); /// Returns a VFS that uses the cache collected. /// /// Note that the returned VFS should not outlive the cache. - IntrusiveRefCntPtr - getConsumingFS(IntrusiveRefCntPtr FS) const; + IntrusiveRefCntPtr + getConsumingFS(IntrusiveRefCntPtr FS) const; private: std::string MainFilePath; - llvm::StringMap StatCache; + llvm::StringMap StatCache; }; } // namespace clangd diff --git a/clangd/FSProvider.h b/clangd/FSProvider.h index a11d38101..0d1d5e954 100644 --- a/clangd/FSProvider.h +++ b/clangd/FSProvider.h @@ -10,8 +10,8 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FSPROVIDER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FSPROVIDER_H -#include "clang/Basic/VirtualFileSystem.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/Support/VirtualFileSystem.h" namespace clang { namespace clangd { @@ -25,14 +25,14 @@ class FileSystemProvider { /// Context::current() will be the context passed to the clang entrypoint, /// such as addDocument(), and will also be propagated to result callbacks. /// Embedders may use this to isolate filesystem accesses. - virtual IntrusiveRefCntPtr getFileSystem() const = 0; + virtual IntrusiveRefCntPtr getFileSystem() const = 0; }; class RealFileSystemProvider : public FileSystemProvider { public: // FIXME: returns the single real FS instance, which is not threadsafe. - IntrusiveRefCntPtr getFileSystem() const override { - return vfs::getRealFileSystem(); + IntrusiveRefCntPtr getFileSystem() const override { + return llvm::vfs::getRealFileSystem(); } }; diff --git a/clangd/Headers.h b/clangd/Headers.h index c140a65a8..a193a658e 100644 --- a/clangd/Headers.h +++ b/clangd/Headers.h @@ -13,7 +13,6 @@ #include "Path.h" #include "Protocol.h" #include "SourceCode.h" -#include "clang/Basic/VirtualFileSystem.h" #include "clang/Format/Format.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PPCallbacks.h" @@ -21,6 +20,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/Error.h" +#include "llvm/Support/VirtualFileSystem.h" namespace clang { namespace clangd { diff --git a/unittests/change-namespace/ChangeNamespaceTests.cpp b/unittests/change-namespace/ChangeNamespaceTests.cpp index 917f7b02e..6b1259a36 100644 --- a/unittests/change-namespace/ChangeNamespaceTests.cpp +++ b/unittests/change-namespace/ChangeNamespaceTests.cpp @@ -12,7 +12,6 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" -#include "clang/Basic/VirtualFileSystem.h" #include "clang/Format/Format.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/PCHContainerOperations.h" @@ -21,6 +20,7 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" #include "gtest/gtest.h" #include #include diff --git a/unittests/clang-tidy/ClangTidyTest.h b/unittests/clang-tidy/ClangTidyTest.h index 197c137fe..2d2675489 100644 --- a/unittests/clang-tidy/ClangTidyTest.h +++ b/unittests/clang-tidy/ClangTidyTest.h @@ -99,8 +99,8 @@ runCheckOnCode(StringRef Code, std::vector *Errors = nullptr, Args.push_back(Filename.str()); ast_matchers::MatchFinder Finder; - llvm::IntrusiveRefCntPtr InMemoryFileSystem( - new vfs::InMemoryFileSystem); + llvm::IntrusiveRefCntPtr InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); llvm::IntrusiveRefCntPtr Files( new FileManager(FileSystemOptions(), InMemoryFileSystem)); diff --git a/unittests/clangd/ClangdTests.cpp b/unittests/clangd/ClangdTests.cpp index 87741dfb0..82824fbdd 100644 --- a/unittests/clangd/ClangdTests.cpp +++ b/unittests/clangd/ClangdTests.cpp @@ -264,7 +264,7 @@ int b = a; TEST_F(ClangdVFSTest, PropagatesContexts) { static Key Secret; struct FSProvider : public FileSystemProvider { - IntrusiveRefCntPtr getFileSystem() const override { + IntrusiveRefCntPtr getFileSystem() const override { Got = Context::current().getExisting(Secret); return buildTestFS({}); } @@ -973,19 +973,19 @@ TEST(ClangdTests, PreambleVFSStatCache) { ListenStatsFSProvider(llvm::StringMap &CountStats) : CountStats(CountStats) {} - IntrusiveRefCntPtr getFileSystem() const override { - class ListenStatVFS : public vfs::ProxyFileSystem { + IntrusiveRefCntPtr getFileSystem() const override { + class ListenStatVFS : public llvm::vfs::ProxyFileSystem { public: - ListenStatVFS(IntrusiveRefCntPtr FS, + ListenStatVFS(IntrusiveRefCntPtr FS, llvm::StringMap &CountStats) : ProxyFileSystem(std::move(FS)), CountStats(CountStats) {} - llvm::ErrorOr> + llvm::ErrorOr> openFileForRead(const Twine &Path) override { ++CountStats[llvm::sys::path::filename(Path.str())]; return ProxyFileSystem::openFileForRead(Path); } - llvm::ErrorOr status(const Twine &Path) override { + llvm::ErrorOr status(const Twine &Path) override { ++CountStats[llvm::sys::path::filename(Path.str())]; return ProxyFileSystem::status(Path); } diff --git a/unittests/clangd/FSTests.cpp b/unittests/clangd/FSTests.cpp index 64e6f8a0e..6c0bfec5c 100644 --- a/unittests/clangd/FSTests.cpp +++ b/unittests/clangd/FSTests.cpp @@ -35,9 +35,10 @@ TEST(FSTests, PreambleStatusCache) { // Main file is not cached. EXPECT_FALSE(StatCache.lookup(testPath("main")).hasValue()); - vfs::Status S("fake", llvm::sys::fs::UniqueID(0, 0), - std::chrono::system_clock::now(), 0, 0, 1024, - llvm::sys::fs::file_type::regular_file, llvm::sys::fs::all_all); + llvm::vfs::Status S("fake", llvm::sys::fs::UniqueID(0, 0), + std::chrono::system_clock::now(), 0, 0, 1024, + llvm::sys::fs::file_type::regular_file, + llvm::sys::fs::all_all); StatCache.update(*FS, S); auto ConsumeFS = StatCache.getConsumingFS(FS); auto Cached = ConsumeFS->status(testPath("fake")); diff --git a/unittests/clangd/SymbolCollectorTests.cpp b/unittests/clangd/SymbolCollectorTests.cpp index 5b3c58e4c..7c18e7e3e 100644 --- a/unittests/clangd/SymbolCollectorTests.cpp +++ b/unittests/clangd/SymbolCollectorTests.cpp @@ -13,7 +13,6 @@ #include "index/SymbolCollector.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" -#include "clang/Basic/VirtualFileSystem.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Index/IndexingAction.h" #include "clang/Tooling/Tooling.h" @@ -21,6 +20,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -219,7 +219,7 @@ class SymbolIndexActionFactory : public tooling::FrontendActionFactory { class SymbolCollectorTest : public ::testing::Test { public: SymbolCollectorTest() - : InMemoryFileSystem(new vfs::InMemoryFileSystem), + : InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem), TestHeaderName(testPath("symbol.h")), TestFileName(testPath("symbol.cc")) { TestHeaderURI = URI::createFile(TestHeaderName).toString(); @@ -258,7 +258,7 @@ class SymbolCollectorTest : public ::testing::Test { } protected: - llvm::IntrusiveRefCntPtr InMemoryFileSystem; + llvm::IntrusiveRefCntPtr InMemoryFileSystem; std::string TestHeaderName; std::string TestHeaderURI; std::string TestFileName; diff --git a/unittests/clangd/TestFS.h b/unittests/clangd/TestFS.h index 39c0bb22b..56bf8a355 100644 --- a/unittests/clangd/TestFS.h +++ b/unittests/clangd/TestFS.h @@ -13,23 +13,23 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTFS_H #define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTFS_H #include "ClangdServer.h" -#include "clang/Basic/VirtualFileSystem.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/Path.h" +#include "llvm/Support/VirtualFileSystem.h" namespace clang { namespace clangd { // Builds a VFS that provides access to the provided files, plus temporary // directories. -llvm::IntrusiveRefCntPtr +llvm::IntrusiveRefCntPtr buildTestFS(llvm::StringMap const &Files, llvm::StringMap const &Timestamps = {}); // A VFS provider that returns TestFSes containing a provided set of files. class MockFSProvider : public FileSystemProvider { public: - IntrusiveRefCntPtr getFileSystem() const override { + IntrusiveRefCntPtr getFileSystem() const override { return buildTestFS(Files); } diff --git a/unittests/include-fixer/IncludeFixerTest.cpp b/unittests/include-fixer/IncludeFixerTest.cpp index 4da14f57a..519c08379 100644 --- a/unittests/include-fixer/IncludeFixerTest.cpp +++ b/unittests/include-fixer/IncludeFixerTest.cpp @@ -24,8 +24,8 @@ using find_all_symbols::SymbolAndSignals; static bool runOnCode(tooling::ToolAction *ToolAction, StringRef Code, StringRef FileName, const std::vector &ExtraArgs) { - llvm::IntrusiveRefCntPtr InMemoryFileSystem( - new vfs::InMemoryFileSystem); + llvm::IntrusiveRefCntPtr InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); llvm::IntrusiveRefCntPtr Files( new FileManager(FileSystemOptions(), InMemoryFileSystem)); // FIXME: Investigate why -fms-compatibility breaks tests. diff --git a/unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp b/unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp index 760a25342..1ad9e7f0d 100644 --- a/unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp +++ b/unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp @@ -14,7 +14,6 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" -#include "clang/Basic/VirtualFileSystem.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/PCHContainerOperations.h" #include "clang/Tooling/Tooling.h" @@ -22,6 +21,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" #include "gtest/gtest.h" #include #include @@ -63,8 +63,8 @@ class FindAllSymbolsTest : public ::testing::Test { int used(const SymbolInfo &Symbol) { return Reporter.used(Symbol); } bool runFindAllSymbols(StringRef HeaderCode, StringRef MainCode) { - llvm::IntrusiveRefCntPtr InMemoryFileSystem( - new vfs::InMemoryFileSystem); + llvm::IntrusiveRefCntPtr InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); llvm::IntrusiveRefCntPtr Files( new FileManager(FileSystemOptions(), InMemoryFileSystem)); From ad6cd72787fdee1d56e5589c9bc07df4f5e8b4d8 Mon Sep 17 00:00:00 2001 From: Chandler Carruth Date: Thu, 11 Oct 2018 08:05:10 +0000 Subject: [PATCH 343/686] Fix the qualification of `IntrusiveRefCntPtr` to use `llvm::`. Without this, the code only compiled if the header was included after something introduced the alias from `clang::` to `llvm::` for this type. Any modules build would fail here. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344225 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/FSProvider.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clangd/FSProvider.h b/clangd/FSProvider.h index 0d1d5e954..a39758aed 100644 --- a/clangd/FSProvider.h +++ b/clangd/FSProvider.h @@ -25,13 +25,15 @@ class FileSystemProvider { /// Context::current() will be the context passed to the clang entrypoint, /// such as addDocument(), and will also be propagated to result callbacks. /// Embedders may use this to isolate filesystem accesses. - virtual IntrusiveRefCntPtr getFileSystem() const = 0; + virtual llvm::IntrusiveRefCntPtr + getFileSystem() const = 0; }; class RealFileSystemProvider : public FileSystemProvider { public: // FIXME: returns the single real FS instance, which is not threadsafe. - IntrusiveRefCntPtr getFileSystem() const override { + llvm::IntrusiveRefCntPtr + getFileSystem() const override { return llvm::vfs::getRealFileSystem(); } }; From 5aad99f4909d8bec86b93d0017118b3bd0b23f70 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Thu, 11 Oct 2018 13:06:10 +0000 Subject: [PATCH 344/686] [clangd] Remove no-op crash handler, we never set a crash context. Summary: I think this was just copied from somewhere with the belief that it actually did some crash handling. Of course the question arises: *should* we set one? I don't think so: - clangd used to crash a lot, now it's pretty stable, because we found and fixed the crashes. I think the long-term effects of crashing hard are good. - the implementation can't do any magic, it just uses longjmp to return without running any destructors by default. This is unsafe in general (e.g. mutexes won't unlock) and will certainly end up leaking memory. Whatever UB caused the crash may still stomp all over global state, etc. I think there's an argument for isolating the background indexer (autoindex) because it's not directly under the user's control, the crash surface is larger, and it doesn't particularly need to interact with the rest of clangd. But there, fork() and communicate through the FS is safer. Reviewers: ioeric, ilya-biryukov Subscribers: MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D53034 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344245 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdUnit.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/clangd/ClangdUnit.cpp b/clangd/ClangdUnit.cpp index 85c7fd86a..9d57060e6 100644 --- a/clangd/ClangdUnit.cpp +++ b/clangd/ClangdUnit.cpp @@ -31,7 +31,6 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/raw_ostream.h" #include @@ -141,10 +140,6 @@ ParsedAST::build(std::unique_ptr CI, if (!Clang) return llvm::None; - // Recover resources if we crash before exiting this method. - llvm::CrashRecoveryContextCleanupRegistrar CICleanup( - Clang.get()); - auto Action = llvm::make_unique(); const FrontendInputFile &MainInput = Clang->getFrontendOpts().Inputs[0]; if (!Action->BeginSourceFile(*Clang, MainInput)) { From ba0ffb87517e286aed3ea4f4df705136425d679e Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Thu, 11 Oct 2018 16:09:26 +0000 Subject: [PATCH 345/686] [clang-move] Remove clang:: qualifier Summary: The use sites are enclosed by `namespace clang`, so clang:: is not necessary. Many unqualified names have already been used, e.g. SourceManager SourceLocation LangOptions. This change makes the code terser and more consistent. Reviewers: hokein Reviewed By: hokein Subscribers: ioeric, cfe-commits Differential Revision: https://reviews.llvm.org/D53060 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344256 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-move/ClangMove.cpp | 123 +++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 69 deletions(-) diff --git a/clang-move/ClangMove.cpp b/clang-move/ClangMove.cpp index 0f104e499..ec9db738d 100644 --- a/clang-move/ClangMove.cpp +++ b/clang-move/ClangMove.cpp @@ -128,18 +128,17 @@ AST_POLYMORPHIC_MATCHER_P(isExpansionInFile, AbsoluteFilePath; } -class FindAllIncludes : public clang::PPCallbacks { +class FindAllIncludes : public PPCallbacks { public: explicit FindAllIncludes(SourceManager *SM, ClangMoveTool *const MoveTool) : SM(*SM), MoveTool(MoveTool) {} - void InclusionDirective(clang::SourceLocation HashLoc, - const clang::Token & /*IncludeTok*/, + void InclusionDirective(SourceLocation HashLoc, const Token & /*IncludeTok*/, StringRef FileName, bool IsAngled, - clang::CharSourceRange FilenameRange, - const clang::FileEntry * /*File*/, - StringRef SearchPath, StringRef /*RelativePath*/, - const clang::Module * /*Imported*/, + CharSourceRange FilenameRange, + const FileEntry * /*File*/, StringRef SearchPath, + StringRef /*RelativePath*/, + const Module * /*Imported*/, SrcMgr::CharacteristicKind /*FileType*/) override { if (const auto *FileEntry = SM.getFileEntryForID(SM.getFileID(HashLoc))) MoveTool->addIncludes(FileName, IsAngled, SearchPath, @@ -165,9 +164,9 @@ class FunctionDeclarationMatch : public MatchFinder::MatchCallback { : MoveTool(MoveTool) {} void run(const MatchFinder::MatchResult &Result) override { - const auto *FD = Result.Nodes.getNodeAs("function"); + const auto *FD = Result.Nodes.getNodeAs("function"); assert(FD); - const clang::NamedDecl *D = FD; + const NamedDecl *D = FD; if (const auto *FTD = FD->getDescribedFunctionTemplate()) D = FTD; MoveDeclFromOldFileToNewFile(MoveTool, D); @@ -183,7 +182,7 @@ class VarDeclarationMatch : public MatchFinder::MatchCallback { : MoveTool(MoveTool) {} void run(const MatchFinder::MatchResult &Result) override { - const auto *VD = Result.Nodes.getNodeAs("var"); + const auto *VD = Result.Nodes.getNodeAs("var"); assert(VD); MoveDeclFromOldFileToNewFile(MoveTool, VD); } @@ -198,10 +197,10 @@ class TypeAliasMatch : public MatchFinder::MatchCallback { : MoveTool(MoveTool) {} void run(const MatchFinder::MatchResult &Result) override { - if (const auto *TD = Result.Nodes.getNodeAs("typedef")) + if (const auto *TD = Result.Nodes.getNodeAs("typedef")) MoveDeclFromOldFileToNewFile(MoveTool, TD); else if (const auto *TAD = - Result.Nodes.getNodeAs("type_alias")) { + Result.Nodes.getNodeAs("type_alias")) { const NamedDecl * D = TAD; if (const auto * TD = TAD->getDescribedAliasTemplate()) D = TD; @@ -219,7 +218,7 @@ class EnumDeclarationMatch : public MatchFinder::MatchCallback { : MoveTool(MoveTool) {} void run(const MatchFinder::MatchResult &Result) override { - const auto *ED = Result.Nodes.getNodeAs("enum"); + const auto *ED = Result.Nodes.getNodeAs("enum"); assert(ED); MoveDeclFromOldFileToNewFile(MoveTool, ED); } @@ -233,21 +232,19 @@ class ClassDeclarationMatch : public MatchFinder::MatchCallback { explicit ClassDeclarationMatch(ClangMoveTool *MoveTool) : MoveTool(MoveTool) {} void run(const MatchFinder::MatchResult &Result) override { - clang::SourceManager* SM = &Result.Context->getSourceManager(); - if (const auto *CMD = - Result.Nodes.getNodeAs("class_method")) + SourceManager *SM = &Result.Context->getSourceManager(); + if (const auto *CMD = Result.Nodes.getNodeAs("class_method")) MatchClassMethod(CMD, SM); - else if (const auto *VD = Result.Nodes.getNodeAs( - "class_static_var_decl")) + else if (const auto *VD = + Result.Nodes.getNodeAs("class_static_var_decl")) MatchClassStaticVariable(VD, SM); - else if (const auto *CD = Result.Nodes.getNodeAs( - "moved_class")) + else if (const auto *CD = + Result.Nodes.getNodeAs("moved_class")) MatchClassDeclaration(CD, SM); } private: - void MatchClassMethod(const clang::CXXMethodDecl* CMD, - clang::SourceManager* SM) { + void MatchClassMethod(const CXXMethodDecl *CMD, SourceManager *SM) { // Skip inline class methods. isInline() ast matcher doesn't ignore this // case. if (!CMD->isInlined()) { @@ -262,13 +259,11 @@ class ClassDeclarationMatch : public MatchFinder::MatchCallback { } } - void MatchClassStaticVariable(const clang::NamedDecl *VD, - clang::SourceManager* SM) { + void MatchClassStaticVariable(const NamedDecl *VD, SourceManager *SM) { MoveDeclFromOldFileToNewFile(MoveTool, VD); } - void MatchClassDeclaration(const clang::CXXRecordDecl *CD, - clang::SourceManager* SM) { + void MatchClassDeclaration(const CXXRecordDecl *CD, SourceManager *SM) { // Get class template from its class declaration as UnremovedDecls stores // class template. if (const auto *TC = CD->getDescribedClassTemplate()) @@ -285,9 +280,8 @@ class ClassDeclarationMatch : public MatchFinder::MatchCallback { // Expand to get the end location of the line where the EndLoc of the given // Decl. -SourceLocation -getLocForEndOfDecl(const clang::Decl *D, - const LangOptions &LangOpts = clang::LangOptions()) { +SourceLocation getLocForEndOfDecl(const Decl *D, + const LangOptions &LangOpts = LangOptions()) { const auto &SM = D->getASTContext().getSourceManager(); // If the expansion range is a character range, this is the location of // the first character past the end. Otherwise it's the location of the @@ -319,12 +313,10 @@ getLocForEndOfDecl(const clang::Decl *D, } // Get full range of a Decl including the comments associated with it. -clang::CharSourceRange -getFullRange(const clang::Decl *D, - const clang::LangOptions &options = clang::LangOptions()) { +CharSourceRange getFullRange(const Decl *D, + const LangOptions &options = LangOptions()) { const auto &SM = D->getASTContext().getSourceManager(); - clang::SourceRange Full(SM.getExpansionLoc(D->getBeginLoc()), - getLocForEndOfDecl(D)); + SourceRange Full(SM.getExpansionLoc(D->getBeginLoc()), getLocForEndOfDecl(D)); // Expand to comments that are associated with the Decl. if (const auto *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) { if (SM.isBeforeInTranslationUnit(Full.getEnd(), Comment->getEndLoc())) @@ -335,18 +327,17 @@ getFullRange(const clang::Decl *D, Full.setBegin(Comment->getBeginLoc()); } - return clang::CharSourceRange::getCharRange(Full); + return CharSourceRange::getCharRange(Full); } -std::string getDeclarationSourceText(const clang::Decl *D) { +std::string getDeclarationSourceText(const Decl *D) { const auto &SM = D->getASTContext().getSourceManager(); llvm::StringRef SourceText = - clang::Lexer::getSourceText(getFullRange(D), SM, clang::LangOptions()); + Lexer::getSourceText(getFullRange(D), SM, LangOptions()); return SourceText.str(); } -bool isInHeaderFile(const clang::Decl *D, - llvm::StringRef OriginalRunningDirectory, +bool isInHeaderFile(const Decl *D, llvm::StringRef OriginalRunningDirectory, llvm::StringRef OldHeader) { const auto &SM = D->getASTContext().getSourceManager(); if (OldHeader.empty()) @@ -363,22 +354,22 @@ bool isInHeaderFile(const clang::Decl *D, return false; } -std::vector getNamespaces(const clang::Decl *D) { +std::vector getNamespaces(const Decl *D) { std::vector Namespaces; for (const auto *Context = D->getDeclContext(); Context; Context = Context->getParent()) { - if (llvm::isa(Context) || - llvm::isa(Context)) + if (llvm::isa(Context) || + llvm::isa(Context)) break; - if (const auto *ND = llvm::dyn_cast(Context)) + if (const auto *ND = llvm::dyn_cast(Context)) Namespaces.push_back(ND->getName().str()); } std::reverse(Namespaces.begin(), Namespaces.end()); return Namespaces; } -clang::tooling::Replacements +tooling::Replacements createInsertedReplacements(const std::vector &Includes, const std::vector &Decls, llvm::StringRef FileName, bool IsHeader = false, @@ -463,8 +454,7 @@ createInsertedReplacements(const std::vector &Includes, if (IsHeader) NewCode += "\n#endif // " + GuardName + "\n"; - return clang::tooling::Replacements( - clang::tooling::Replacement(FileName, 0, 0, NewCode)); + return tooling::Replacements(tooling::Replacement(FileName, 0, 0, NewCode)); } // Return a set of all decls which are used/referenced by the given Decls. @@ -488,8 +478,8 @@ getUsedDecls(const HelperDeclRefGraph *RG, } // namespace -std::unique_ptr -ClangMoveAction::CreateASTConsumer(clang::CompilerInstance &Compiler, +std::unique_ptr +ClangMoveAction::CreateASTConsumer(CompilerInstance &Compiler, StringRef /*InFile*/) { Compiler.getPreprocessor().addPPCallbacks(llvm::make_unique( &Compiler.getSourceManager(), &MoveTool)); @@ -673,11 +663,10 @@ void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) { } void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) { - if (const auto *D = - Result.Nodes.getNodeAs("decls_in_header")) { + if (const auto *D = Result.Nodes.getNodeAs("decls_in_header")) { UnremovedDeclsInOldHeader.insert(D); } else if (const auto *FWD = - Result.Nodes.getNodeAs("fwd_decl")) { + Result.Nodes.getNodeAs("fwd_decl")) { // Skip all forward declarations which appear after moved class declaration. if (RemovedDecls.empty()) { if (const auto *DCT = FWD->getDescribedClassTemplate()) @@ -686,13 +675,12 @@ void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) { MovedDecls.push_back(FWD); } } else if (const auto *ND = - Result.Nodes.getNodeAs("helper_decls")) { + Result.Nodes.getNodeAs("helper_decls")) { MovedDecls.push_back(ND); HelperDeclarations.push_back(ND); LLVM_DEBUG(llvm::dbgs() << "Add helper : " << ND->getNameAsString() << " (" << ND << ")\n"); - } else if (const auto *UD = - Result.Nodes.getNodeAs("using_decl")) { + } else if (const auto *UD = Result.Nodes.getNodeAs("using_decl")) { MovedDecls.push_back(UD); } } @@ -704,7 +692,7 @@ std::string ClangMoveTool::makeAbsolutePath(StringRef Path) { void ClangMoveTool::addIncludes(llvm::StringRef IncludeHeader, bool IsAngled, llvm::StringRef SearchPath, llvm::StringRef FileName, - clang::CharSourceRange IncludeFilenameRange, + CharSourceRange IncludeFilenameRange, const SourceManager &SM) { SmallVector HeaderWithSearchPath; llvm::sys::path::append(HeaderWithSearchPath, SearchPath, IncludeHeader); @@ -764,9 +752,8 @@ void ClangMoveTool::removeDeclsInOldFiles() { for (const auto *RemovedDecl : RemovedDecls) { const auto &SM = RemovedDecl->getASTContext().getSourceManager(); auto Range = getFullRange(RemovedDecl); - clang::tooling::Replacement RemoveReplacement( - SM, - clang::CharSourceRange::getCharRange(Range.getBegin(), Range.getEnd()), + tooling::Replacement RemoveReplacement( + SM, CharSourceRange::getCharRange(Range.getBegin(), Range.getEnd()), ""); std::string FilePath = RemoveReplacement.getFilePath().str(); auto Err = Context->FileToReplacements[FilePath].add(RemoveReplacement); @@ -868,20 +855,18 @@ void ClangMoveTool::moveAll(SourceManager &SM, StringRef OldFile, FileID ID = SM.getOrCreateFileID(FE, SrcMgr::C_User); auto Begin = SM.getLocForStartOfFile(ID); auto End = SM.getLocForEndOfFile(ID); - clang::tooling::Replacement RemoveAll ( - SM, clang::CharSourceRange::getCharRange(Begin, End), ""); + tooling::Replacement RemoveAll(SM, CharSourceRange::getCharRange(Begin, End), + ""); std::string FilePath = RemoveAll.getFilePath().str(); - Context->FileToReplacements[FilePath] = - clang::tooling::Replacements(RemoveAll); + Context->FileToReplacements[FilePath] = tooling::Replacements(RemoveAll); StringRef Code = SM.getBufferData(ID); if (!NewFile.empty()) { - auto AllCode = clang::tooling::Replacements( - clang::tooling::Replacement(NewFile, 0, 0, Code)); - auto ReplaceOldInclude = [&](clang::CharSourceRange OldHeaderIncludeRange) { - AllCode = AllCode.merge(clang::tooling::Replacements( - clang::tooling::Replacement(SM, OldHeaderIncludeRange, - '"' + Context->Spec.NewHeader + '"'))); + auto AllCode = + tooling::Replacements(tooling::Replacement(NewFile, 0, 0, Code)); + auto ReplaceOldInclude = [&](CharSourceRange OldHeaderIncludeRange) { + AllCode = AllCode.merge(tooling::Replacements(tooling::Replacement( + SM, OldHeaderIncludeRange, '"' + Context->Spec.NewHeader + '"'))); }; // Fix the case where old.h/old.cc includes "old.h", we replace the // `#include "old.h"` with `#include "new.h"`. @@ -923,7 +908,7 @@ void ClangMoveTool::onEndOfTranslationUnit() { // Ignore symbols that are not supported when checking if there is unremoved // symbol in old header. This makes sure that we always move old files to new // files when all symbols produced from dump_decls are moved. - auto IsSupportedKind = [](const clang::NamedDecl *Decl) { + auto IsSupportedKind = [](const NamedDecl *Decl) { switch (Decl->getKind()) { case Decl::Kind::Function: case Decl::Kind::FunctionTemplate: From 91ebe3b8601fea74970c917a53c40ba1d570ca94 Mon Sep 17 00:00:00 2001 From: Eugene Zelenko Date: Thu, 11 Oct 2018 21:40:32 +0000 Subject: [PATCH 346/686] [Documentation] Rephrase modernize-deprecated-ios-base-aliases description. Add clangd and clang-doc placeholders in Release Notes. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344299 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/ReleaseNotes.rst | 16 +++++++++++++--- .../modernize-deprecated-ios-base-aliases.rst | 4 ++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index 9da5ce6e9..5adb42871 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -44,6 +44,16 @@ Major New Features ... +Improvements to clangd +---------------------- + +The improvements are... + +Improvements to clang-doc +------------------------- + +The improvements are... + Improvements to clang-query --------------------------- @@ -99,12 +109,12 @@ Improvements to clang-tidy Checks for uses of nested namespaces in the form of ``namespace a { namespace b { ... }}`` and offers change to syntax introduced in C++17 standard: ``namespace a::b { ... }``. - + - New :doc:`modernize-deprecated-ios-base-aliases ` check. - This check warns the uses of the deprecated member types of ``std::ios_base`` - and replaces those that have a non-deprecated equivalent. + Detects usage of the deprecated member types of ``std::ios_base`` and replaces + those that have a non-deprecated equivalent. - New :doc:`readability-magic-numbers ` check. diff --git a/docs/clang-tidy/checks/modernize-deprecated-ios-base-aliases.rst b/docs/clang-tidy/checks/modernize-deprecated-ios-base-aliases.rst index 9460cab35..f217ff090 100644 --- a/docs/clang-tidy/checks/modernize-deprecated-ios-base-aliases.rst +++ b/docs/clang-tidy/checks/modernize-deprecated-ios-base-aliases.rst @@ -3,8 +3,8 @@ modernize-deprecated-ios-base-aliases ===================================== -This check warns the uses of the deprecated member types of ``std::ios_base`` -and replaces those that have a non-deprecated equivalent. +Detects usage of the deprecated member types of ``std::ios_base`` and replaces +those that have a non-deprecated equivalent. =================================== =========================== Deprecated member type Replacement From fc24838208eaba7ea03f353704ca0e32977c8cde Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Fri, 12 Oct 2018 10:11:02 +0000 Subject: [PATCH 347/686] [clangd] Support hover on "aut^o *". Reviewers: kadircet Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D53186 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344330 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/XRefs.cpp | 24 ++++++++++++++---------- unittests/clangd/XRefsTests.cpp | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/clangd/XRefs.cpp b/clangd/XRefs.cpp index 4c7182206..6dc65dcee 100644 --- a/clangd/XRefs.cpp +++ b/clangd/XRefs.cpp @@ -579,20 +579,28 @@ class DeducedTypeVisitor : public RecursiveASTVisitor { llvm::Optional getDeducedType() { return DeducedType; } + // Remove the surrounding Reference or Pointer type of the given type T. + QualType UnwrapReferenceOrPointer(QualType T) { + // "auto &" is represented as a ReferenceType containing an AutoType + if (const ReferenceType *RT = dyn_cast(T.getTypePtr())) + return RT->getPointeeType(); + // "auto *" is represented as a PointerType containing an AutoType + if (const PointerType *PT = dyn_cast(T.getTypePtr())) + return PT->getPointeeType(); + return T; + } + // Handle auto initializers: //- auto i = 1; //- decltype(auto) i = 1; //- auto& i = 1; + //- auto* i = &a; bool VisitDeclaratorDecl(DeclaratorDecl *D) { if (!D->getTypeSourceInfo() || D->getTypeSourceInfo()->getTypeLoc().getBeginLoc() != SearchedLocation) return true; - auto DeclT = D->getType(); - // "auto &" is represented as a ReferenceType containing an AutoType - if (const ReferenceType *RT = dyn_cast(DeclT.getTypePtr())) - DeclT = RT->getPointeeType(); - + auto DeclT = UnwrapReferenceOrPointer(D->getType()); const AutoType *AT = dyn_cast(DeclT.getTypePtr()); if (AT && !AT->getDeducedType().isNull()) { // For auto, use the underlying type because the const& would be @@ -626,11 +634,7 @@ class DeducedTypeVisitor : public RecursiveASTVisitor { if (CurLoc != SearchedLocation) return true; - auto T = D->getReturnType(); - // "auto &" is represented as a ReferenceType containing an AutoType. - if (const ReferenceType *RT = dyn_cast(T.getTypePtr())) - T = RT->getPointeeType(); - + auto T = UnwrapReferenceOrPointer(D->getReturnType()); const AutoType *AT = dyn_cast(T.getTypePtr()); if (AT && !AT->getDeducedType().isNull()) { DeducedType = T.getUnqualifiedType(); diff --git a/unittests/clangd/XRefsTests.cpp b/unittests/clangd/XRefsTests.cpp index f6fad9112..98520aad6 100644 --- a/unittests/clangd/XRefsTests.cpp +++ b/unittests/clangd/XRefsTests.cpp @@ -755,6 +755,15 @@ TEST(Hover, All) { )cpp", "int", }, + { + R"cpp(// Simple initialization with auto* + void foo() { + int a = 1; + ^auto* i = &a; + } + )cpp", + "int", + }, { R"cpp(// Auto with initializer list. namespace std @@ -862,6 +871,16 @@ TEST(Hover, All) { )cpp", "struct Bar", }, + { + R"cpp(// auto* in function return + struct Bar {}; + ^auto* test() { + Bar* bar; + return bar; + } + )cpp", + "struct Bar", + }, { R"cpp(// const auto& in function return struct Bar {}; From 083ebf67ecae791a74f88713acbab5217ee8163f Mon Sep 17 00:00:00 2001 From: Adam Balogh Date: Fri, 12 Oct 2018 13:05:21 +0000 Subject: [PATCH 348/686] [clang-tidy] White List Option for performance-unnecessary-value-param, performance-unnecessary-copy-initialization and performance-for-range-copy New option added to these three checks to be able to silence false positives on types that are intentionally passed by value or copied. Such types are e.g. intrusive reference counting pointer types like llvm::IntrusiveRefCntPtr. The new option is named WhiteListTypes and can contain a semicolon-separated list of names of these types. Regular expressions are allowed. Default is empty. Differential Revision: https://reviews.llvm.org/D52727 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344340 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/performance/ForRangeCopyCheck.cpp | 14 +- clang-tidy/performance/ForRangeCopyCheck.h | 1 + .../UnnecessaryCopyInitialization.cpp | 23 +++- .../UnnecessaryCopyInitialization.h | 5 +- .../UnnecessaryValueParamCheck.cpp | 18 ++- .../performance/UnnecessaryValueParamCheck.h | 1 + clang-tidy/utils/Matchers.h | 7 + .../checks/performance-for-range-copy.rst | 7 + ...rmance-unnecessary-copy-initialization.rst | 10 ++ .../performance-unnecessary-value-param.rst | 6 + ...rformance-for-range-copy-allowed-types.cpp | 124 ++++++++++++++++++ ...sary-copy-initialization-allowed-types.cpp | 98 ++++++++++++++ ...-unnecessary-value-param-allowed-types.cpp | 75 +++++++++++ 13 files changed, 378 insertions(+), 11 deletions(-) create mode 100644 test/clang-tidy/performance-for-range-copy-allowed-types.cpp create mode 100644 test/clang-tidy/performance-unnecessary-copy-initialization-allowed-types.cpp create mode 100644 test/clang-tidy/performance-unnecessary-value-param-allowed-types.cpp diff --git a/clang-tidy/performance/ForRangeCopyCheck.cpp b/clang-tidy/performance/ForRangeCopyCheck.cpp index d60a2f349..4b90df43a 100644 --- a/clang-tidy/performance/ForRangeCopyCheck.cpp +++ b/clang-tidy/performance/ForRangeCopyCheck.cpp @@ -10,6 +10,8 @@ #include "ForRangeCopyCheck.h" #include "../utils/DeclRefExprUtils.h" #include "../utils/FixItHintUtils.h" +#include "../utils/Matchers.h" +#include "../utils/OptionsUtils.h" #include "../utils/TypeTraits.h" #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" @@ -21,10 +23,14 @@ namespace performance { ForRangeCopyCheck::ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), - WarnOnAllAutoCopies(Options.get("WarnOnAllAutoCopies", 0)) {} + WarnOnAllAutoCopies(Options.get("WarnOnAllAutoCopies", 0)), + AllowedTypes( + utils::options::parseStringList(Options.get("AllowedTypes", ""))) {} void ForRangeCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "WarnOnAllAutoCopies", WarnOnAllAutoCopies); + Options.store(Opts, "AllowedTypes", + utils::options::serializeStringList(AllowedTypes)); } void ForRangeCopyCheck::registerMatchers(MatchFinder *Finder) { @@ -32,7 +38,10 @@ void ForRangeCopyCheck::registerMatchers(MatchFinder *Finder) { // initialized through MaterializeTemporaryExpr which indicates a type // conversion. auto LoopVar = varDecl( - hasType(hasCanonicalType(unless(anyOf(referenceType(), pointerType())))), + hasType(qualType( + unless(anyOf(hasCanonicalType(anyOf(referenceType(), pointerType())), + hasDeclaration(namedDecl( + matchers::matchesAnyListedName(AllowedTypes))))))), unless(hasInitializer(expr(hasDescendant(materializeTemporaryExpr()))))); Finder->addMatcher(cxxForRangeStmt(hasLoopVariable(LoopVar.bind("loopVar"))) .bind("forRange"), @@ -41,6 +50,7 @@ void ForRangeCopyCheck::registerMatchers(MatchFinder *Finder) { void ForRangeCopyCheck::check(const MatchFinder::MatchResult &Result) { const auto *Var = Result.Nodes.getNodeAs("loopVar"); + // Ignore code in macros since we can't place the fixes correctly. if (Var->getBeginLoc().isMacroID()) return; diff --git a/clang-tidy/performance/ForRangeCopyCheck.h b/clang-tidy/performance/ForRangeCopyCheck.h index f88a126e5..de8b2c911 100644 --- a/clang-tidy/performance/ForRangeCopyCheck.h +++ b/clang-tidy/performance/ForRangeCopyCheck.h @@ -40,6 +40,7 @@ class ForRangeCopyCheck : public ClangTidyCheck { ASTContext &Context); const bool WarnOnAllAutoCopies; + const std::vector AllowedTypes; }; } // namespace performance diff --git a/clang-tidy/performance/UnnecessaryCopyInitialization.cpp b/clang-tidy/performance/UnnecessaryCopyInitialization.cpp index 177497c41..ef6bcd6f6 100644 --- a/clang-tidy/performance/UnnecessaryCopyInitialization.cpp +++ b/clang-tidy/performance/UnnecessaryCopyInitialization.cpp @@ -12,6 +12,7 @@ #include "../utils/DeclRefExprUtils.h" #include "../utils/FixItHintUtils.h" #include "../utils/Matchers.h" +#include "../utils/OptionsUtils.h" namespace clang { namespace tidy { @@ -30,6 +31,12 @@ void recordFixes(const VarDecl &Var, ASTContext &Context, using namespace ::clang::ast_matchers; using utils::decl_ref_expr::isOnlyUsedAsConst; +UnnecessaryCopyInitialization::UnnecessaryCopyInitialization( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + AllowedTypes( + utils::options::parseStringList(Options.get("AllowedTypes", ""))) {} + void UnnecessaryCopyInitialization::registerMatchers(MatchFinder *Finder) { auto ConstReference = referenceType(pointee(qualType(isConstQualified()))); auto ConstOrConstReference = @@ -50,12 +57,17 @@ void UnnecessaryCopyInitialization::registerMatchers(MatchFinder *Finder) { callExpr(callee(functionDecl(returns(ConstReference))), unless(callee(cxxMethodDecl()))); - auto localVarCopiedFrom = [](const internal::Matcher &CopyCtorArg) { + auto localVarCopiedFrom = [this](const internal::Matcher &CopyCtorArg) { return compoundStmt( forEachDescendant( declStmt( has(varDecl(hasLocalStorage(), - hasType(matchers::isExpensiveToCopy()), + hasType(qualType( + allOf(hasCanonicalType( + matchers::isExpensiveToCopy()), + unless(hasDeclaration(namedDecl( + matchers::matchesAnyListedName( + AllowedTypes))))))), unless(isImplicit()), hasInitializer( cxxConstructExpr( @@ -84,6 +96,7 @@ void UnnecessaryCopyInitialization::check( const auto *ObjectArg = Result.Nodes.getNodeAs("objectArg"); const auto *BlockStmt = Result.Nodes.getNodeAs("blockStmt"); const auto *CtorCall = Result.Nodes.getNodeAs("ctorCall"); + // Do not propose fixes if the DeclStmt has multiple VarDecls or in macros // since we cannot place them correctly. bool IssueFix = @@ -144,6 +157,12 @@ void UnnecessaryCopyInitialization::handleCopyFromLocalVar( recordFixes(NewVar, Context, Diagnostic); } +void UnnecessaryCopyInitialization::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "AllowedTypes", + utils::options::serializeStringList(AllowedTypes)); +} + } // namespace performance } // namespace tidy } // namespace clang diff --git a/clang-tidy/performance/UnnecessaryCopyInitialization.h b/clang-tidy/performance/UnnecessaryCopyInitialization.h index 8ed414273..1844aa4bf 100644 --- a/clang-tidy/performance/UnnecessaryCopyInitialization.h +++ b/clang-tidy/performance/UnnecessaryCopyInitialization.h @@ -26,10 +26,10 @@ namespace performance { // const references, and const pointers to const. class UnnecessaryCopyInitialization : public ClangTidyCheck { public: - UnnecessaryCopyInitialization(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + UnnecessaryCopyInitialization(StringRef Name, ClangTidyContext *Context); void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; private: void handleCopyFromMethodReturn(const VarDecl &Var, const Stmt &BlockStmt, @@ -38,6 +38,7 @@ class UnnecessaryCopyInitialization : public ClangTidyCheck { void handleCopyFromLocalVar(const VarDecl &NewVar, const VarDecl &OldVar, const Stmt &BlockStmt, bool IssueFix, ASTContext &Context); + const std::vector AllowedTypes; }; } // namespace performance diff --git a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp index db5a12f9b..ac76cea36 100644 --- a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp +++ b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp @@ -12,6 +12,7 @@ #include "../utils/DeclRefExprUtils.h" #include "../utils/FixItHintUtils.h" #include "../utils/Matchers.h" +#include "../utils/OptionsUtils.h" #include "../utils/TypeTraits.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/Lexer.h" @@ -68,17 +69,22 @@ UnnecessaryValueParamCheck::UnnecessaryValueParamCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), IncludeStyle(utils::IncludeSorter::parseIncludeStyle( - Options.getLocalOrGlobal("IncludeStyle", "llvm"))) {} + Options.getLocalOrGlobal("IncludeStyle", "llvm"))), + AllowedTypes( + utils::options::parseStringList(Options.get("AllowedTypes", ""))) {} void UnnecessaryValueParamCheck::registerMatchers(MatchFinder *Finder) { // This check is specific to C++ and doesn't apply to languages like // Objective-C. if (!getLangOpts().CPlusPlus) return; - const auto ExpensiveValueParamDecl = - parmVarDecl(hasType(hasCanonicalType(allOf( - unless(referenceType()), matchers::isExpensiveToCopy()))), - decl().bind("param")); + const auto ExpensiveValueParamDecl = parmVarDecl( + hasType(qualType(allOf( + hasCanonicalType(matchers::isExpensiveToCopy()), + unless(anyOf(hasCanonicalType(referenceType()), + hasDeclaration(namedDecl( + matchers::matchesAnyListedName(AllowedTypes)))))))), + decl().bind("param")); Finder->addMatcher( functionDecl(hasBody(stmt()), isDefinition(), unless(isImplicit()), unless(cxxMethodDecl(anyOf(isOverride(), isFinal()))), @@ -173,6 +179,8 @@ void UnnecessaryValueParamCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "IncludeStyle", utils::IncludeSorter::toString(IncludeStyle)); + Options.store(Opts, "AllowedTypes", + utils::options::serializeStringList(AllowedTypes)); } void UnnecessaryValueParamCheck::onEndOfTranslationUnit() { diff --git a/clang-tidy/performance/UnnecessaryValueParamCheck.h b/clang-tidy/performance/UnnecessaryValueParamCheck.h index ae5ef7e96..a1d658693 100644 --- a/clang-tidy/performance/UnnecessaryValueParamCheck.h +++ b/clang-tidy/performance/UnnecessaryValueParamCheck.h @@ -40,6 +40,7 @@ class UnnecessaryValueParamCheck : public ClangTidyCheck { MutationAnalyzers; std::unique_ptr Inserter; const utils::IncludeSorter::IncludeStyle IncludeStyle; + const std::vector AllowedTypes; }; } // namespace performance diff --git a/clang-tidy/utils/Matchers.h b/clang-tidy/utils/Matchers.h index aeb639faf..849b36fb7 100644 --- a/clang-tidy/utils/Matchers.h +++ b/clang-tidy/utils/Matchers.h @@ -48,6 +48,13 @@ AST_MATCHER_FUNCTION(ast_matchers::TypeMatcher, isReferenceToConst) { return referenceType(pointee(qualType(isConstQualified()))); } +AST_MATCHER_P(NamedDecl, matchesAnyListedName, std::vector, + NameList) { + return llvm::any_of(NameList, [&Node](const std::string &Name) { + return llvm::Regex(Name).match(Node.getName()); + }); +} + } // namespace matchers } // namespace tidy } // namespace clang diff --git a/docs/clang-tidy/checks/performance-for-range-copy.rst b/docs/clang-tidy/checks/performance-for-range-copy.rst index 2e7db98d8..35747271c 100644 --- a/docs/clang-tidy/checks/performance-for-range-copy.rst +++ b/docs/clang-tidy/checks/performance-for-range-copy.rst @@ -25,3 +25,10 @@ Options When non-zero, warns on any use of `auto` as the type of the range-based for loop variable. Default is `0`. + +.. option:: AllowedTypes + + A semicolon-separated list of names of types allowed to be copied in each + iteration. Regular expressions are accepted, e.g. `[Rr]ef(erence)?$` matches + every type with suffix `Ref`, `ref`, `Reference` and `reference`. The default + is empty. diff --git a/docs/clang-tidy/checks/performance-unnecessary-copy-initialization.rst b/docs/clang-tidy/checks/performance-unnecessary-copy-initialization.rst index 2bd77551a..661c3072e 100644 --- a/docs/clang-tidy/checks/performance-unnecessary-copy-initialization.rst +++ b/docs/clang-tidy/checks/performance-unnecessary-copy-initialization.rst @@ -35,3 +35,13 @@ Example: string UnnecessaryCopy2 = UnnecessaryCopy1; UnnecessaryCopy2.find("bar"); } + +Options +------- + +.. option:: AllowedTypes + + A semicolon-separated list of names of types allowed to be initialized by + copying. Regular expressions are accepted, e.g. `[Rr]ef(erence)?$` matches + every type with suffix `Ref`, `ref`, `Reference` and `reference`. The + default is empty. diff --git a/docs/clang-tidy/checks/performance-unnecessary-value-param.rst b/docs/clang-tidy/checks/performance-unnecessary-value-param.rst index e69610ecf..21a5c65f6 100644 --- a/docs/clang-tidy/checks/performance-unnecessary-value-param.rst +++ b/docs/clang-tidy/checks/performance-unnecessary-value-param.rst @@ -61,3 +61,9 @@ Options A string specifying which include-style is used, `llvm` or `google`. Default is `llvm`. + +.. option:: AllowedTypes + + A semicolon-separated list of names of types allowed to be passed by value. + Regular expressions are accepted, e.g. `[Rr]ef(erence)?$` matches every type + with suffix `Ref`, `ref`, `Reference` and `reference`. The default is empty. diff --git a/test/clang-tidy/performance-for-range-copy-allowed-types.cpp b/test/clang-tidy/performance-for-range-copy-allowed-types.cpp new file mode 100644 index 000000000..a3e7a6bf2 --- /dev/null +++ b/test/clang-tidy/performance-for-range-copy-allowed-types.cpp @@ -0,0 +1,124 @@ +// RUN: %check_clang_tidy %s performance-for-range-copy %t -- -config="{CheckOptions: [{key: performance-for-range-copy.AllowedTypes, value: '[Pp]ointer$;[Pp]tr$;[Rr]ef(erence)?$'}]}" -- -std=c++11 -fno-delayed-template-parsing + +template +struct Iterator { + void operator++() {} + const T& operator*() { + static T* TT = new T(); + return *TT; + } + bool operator!=(const Iterator &) { return false; } + typedef const T& const_reference; +}; +template +struct View { + T begin() { return T(); } + T begin() const { return T(); } + T end() { return T(); } + T end() const { return T(); } + typedef typename T::const_reference const_reference; +}; + +struct SmartPointer { + ~SmartPointer(); +}; + +struct smart_pointer { + ~smart_pointer(); +}; + +struct SmartPtr { + ~SmartPtr(); +}; + +struct smart_ptr { + ~smart_ptr(); +}; + +struct SmartReference { + ~SmartReference(); +}; + +struct smart_reference { + ~smart_reference(); +}; + +struct SmartRef { + ~SmartRef(); +}; + +struct smart_ref { + ~smart_ref(); +}; + +struct OtherType { + ~OtherType(); +}; + +template struct SomeComplexTemplate { + ~SomeComplexTemplate(); +}; + +typedef SomeComplexTemplate NotTooComplexRef; + +void negativeSmartPointer() { + for (auto P : View>()) { + auto P2 = P; + } +} + +void negative_smart_pointer() { + for (auto p : View>()) { + auto p2 = p; + } +} + +void negativeSmartPtr() { + for (auto P : View>()) { + auto P2 = P; + } +} + +void negative_smart_ptr() { + for (auto p : View>()) { + auto p2 = p; + } +} + +void negativeSmartReference() { + for (auto R : View>()) { + auto R2 = R; + } +} + +void negative_smart_reference() { + for (auto r : View>()) { + auto r2 = r; + } +} + +void negativeSmartRef() { + for (auto R : View>()) { + auto R2 = R; + } +} + +void negative_smart_ref() { + for (auto r : View>()) { + auto r2 = r; + } +} + +void positiveOtherType() { + for (auto O : View>()) { + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy] + // CHECK-FIXES: for (const auto& O : View>()) { + auto O2 = O; + } +} + +void negativeNotTooComplexRef() { + for (NotTooComplexRef R : View>()) { + auto R2 = R; + } +} diff --git a/test/clang-tidy/performance-unnecessary-copy-initialization-allowed-types.cpp b/test/clang-tidy/performance-unnecessary-copy-initialization-allowed-types.cpp new file mode 100644 index 000000000..420efa8db --- /dev/null +++ b/test/clang-tidy/performance-unnecessary-copy-initialization-allowed-types.cpp @@ -0,0 +1,98 @@ +// RUN: %check_clang_tidy %s performance-unnecessary-copy-initialization %t -- -config="{CheckOptions: [{key: performance-unnecessary-copy-initialization.AllowedTypes, value: '[Pp]ointer$;[Pp]tr$;[Rr]ef(erence)?$'}]}" -- + +struct SmartPointer { + ~SmartPointer(); +}; + +struct smart_pointer { + ~smart_pointer(); +}; + +struct SmartPtr { + ~SmartPtr(); +}; + +struct smart_ptr { + ~smart_ptr(); +}; + +struct SmartReference { + ~SmartReference(); +}; + +struct smart_reference { + ~smart_reference(); +}; + +struct SmartRef { + ~SmartRef(); +}; + +struct smart_ref { + ~smart_ref(); +}; + +struct OtherType { + ~OtherType(); +}; + +template struct SomeComplexTemplate { + ~SomeComplexTemplate(); +}; + +typedef SomeComplexTemplate NotTooComplexRef; + +const SmartPointer &getSmartPointer(); +const smart_pointer &get_smart_pointer(); +const SmartPtr &getSmartPtr(); +const smart_ptr &get_smart_ptr(); +const SmartReference &getSmartReference(); +const smart_reference &get_smart_reference(); +const SmartRef &getSmartRef(); +const smart_ref &get_smart_ref(); +const OtherType &getOtherType(); +const NotTooComplexRef &getNotTooComplexRef(); + +void negativeSmartPointer() { + const auto P = getSmartPointer(); +} + +void negative_smart_pointer() { + const auto p = get_smart_pointer(); +} + +void negativeSmartPtr() { + const auto P = getSmartPtr(); +} + +void negative_smart_ptr() { + const auto p = get_smart_ptr(); +} + +void negativeSmartReference() { + const auto R = getSmartReference(); +} + +void negative_smart_reference() { + const auto r = get_smart_reference(); +} + +void negativeSmartRef() { + const auto R = getSmartRef(); +} + +void negative_smart_ref() { + const auto r = get_smart_ref(); +} + +void positiveOtherType() { + const auto O = getOtherType(); + // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'O' is copy-constructed from a const reference; consider making it a const reference [performance-unnecessary-copy-initialization] + // CHECK-FIXES: const auto& O = getOtherType(); +} + +void negativeNotTooComplexRef() { + const NotTooComplexRef R = getNotTooComplexRef(); + // Using `auto` here would result in the "canonical" type which does not match + // the pattern. +} diff --git a/test/clang-tidy/performance-unnecessary-value-param-allowed-types.cpp b/test/clang-tidy/performance-unnecessary-value-param-allowed-types.cpp new file mode 100644 index 000000000..054732cd9 --- /dev/null +++ b/test/clang-tidy/performance-unnecessary-value-param-allowed-types.cpp @@ -0,0 +1,75 @@ +// RUN: %check_clang_tidy %s performance-unnecessary-value-param %t -- -config="{CheckOptions: [{key: performance-unnecessary-value-param.AllowedTypes, value: '[Pp]ointer$;[Pp]tr$;[Rr]ef(erence)?$'}]}" -- + +struct SmartPointer { + ~SmartPointer(); +}; + +struct smart_pointer { + ~smart_pointer(); +}; + +struct SmartPtr { + ~SmartPtr(); +}; + +struct smart_ptr { + ~smart_ptr(); +}; + +struct SmartReference { + ~SmartReference(); +}; + +struct smart_reference { + ~smart_reference(); +}; + +struct SmartRef { + ~SmartRef(); +}; + +struct smart_ref { + ~smart_ref(); +}; + +struct OtherType { + ~OtherType(); +}; + +template struct SomeComplexTemplate { + ~SomeComplexTemplate(); +}; + +typedef SomeComplexTemplate NotTooComplexRef; + +void negativeSmartPointer(SmartPointer P) { +} + +void negative_smart_pointer(smart_pointer p) { +} + +void negativeSmartPtr(SmartPtr P) { +} + +void negative_smart_ptr(smart_ptr p) { +} + +void negativeSmartReference(SmartReference R) { +} + +void negative_smart_reference(smart_reference r) { +} + +void negativeSmartRef(SmartRef R) { +} + +void negative_smart_ref(smart_ref r) { +} + +void positiveOtherType(OtherType O) { + // CHECK-MESSAGES: [[@LINE-1]]:34: warning: the parameter 'O' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param] + // CHECK-FIXES: void positiveOtherType(const OtherType& O) { +} + +void negativeNotTooComplexRef(NotTooComplexRef R) { +} From 912d3b3ff4efdc0ba4bba0f1900f9731e7b98511 Mon Sep 17 00:00:00 2001 From: Zinovy Nis Date: Fri, 12 Oct 2018 13:35:47 +0000 Subject: [PATCH 349/686] [clang-tidy] Fix check_clang_tidy.py trivially passing default CHECK Differential Revision: https://reviews.llvm.org/D53194 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344343 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clang-tidy/check_clang_tidy.py | 66 ++++++++++++++--------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/test/clang-tidy/check_clang_tidy.py b/test/clang-tidy/check_clang_tidy.py index 9d2b5f789..9768011a3 100755 --- a/test/clang-tidy/check_clang_tidy.py +++ b/test/clang-tidy/check_clang_tidy.py @@ -51,7 +51,7 @@ def main(): parser.add_argument('check_name') parser.add_argument('temp_file_name') parser.add_argument('-check-suffix', '-check-suffixes', - default=[], type=csv, + default=[''], type=csv, help="comma-separated list of FileCheck suffixes") args, extra_args = parser.parse_known_args() @@ -96,41 +96,37 @@ def main(): has_check_messages = False has_check_notes = False - if any(args.check_suffix): - for check in args.check_suffix: - if not re.match('^[A-Z0-9\-]+$', check): - sys.exit('Only A..Z, 0..9 and "-" are ' + - 'allowed in check suffixes list, but "%s" was given' % (check)) - - file_check_suffix = '-' + check - check_fixes_prefix = 'CHECK-FIXES' + file_check_suffix - check_messages_prefix = 'CHECK-MESSAGES' + file_check_suffix - check_notes_prefix = 'CHECK-NOTES' + file_check_suffix - - has_check_fix = check_fixes_prefix in input_text - has_check_message = check_messages_prefix in input_text - has_check_note = check_notes_prefix in input_text - - if has_check_note and has_check_message: - sys.exit('Please use either %s or %s but not both' % - (check_notes_prefix, check_messages_prefix)) - - if not has_check_fix and not has_check_message and not has_check_note: - sys.exit('%s, %s or %s not found in the input' % - (check_fixes_prefix, check_messages_prefix, check_notes_prefix)) - - has_check_fixes = has_check_fixes or has_check_fix - has_check_messages = has_check_messages or has_check_message - has_check_notes = has_check_notes or has_check_note - - check_fixes_prefixes.append(check_fixes_prefix) - check_messages_prefixes.append(check_messages_prefix) - check_notes_prefixes.append(check_notes_prefix) - else: - check_fixes_prefixes = ['CHECK-FIXES'] - check_messages_prefixes = ['CHECK-MESSAGES'] - check_notes_prefixes = ['CHECK-NOTES'] + for check in args.check_suffix: + if check and not re.match('^[A-Z0-9\-]+$', check): + sys.exit('Only A..Z, 0..9 and "-" are ' + + 'allowed in check suffixes list, but "%s" was given' % (check)) + file_check_suffix = ('-' + check) if check else '' + check_fixes_prefix = 'CHECK-FIXES' + file_check_suffix + check_messages_prefix = 'CHECK-MESSAGES' + file_check_suffix + check_notes_prefix = 'CHECK-NOTES' + file_check_suffix + + has_check_fix = check_fixes_prefix in input_text + has_check_message = check_messages_prefix in input_text + has_check_note = check_notes_prefix in input_text + + if has_check_note and has_check_message: + sys.exit('Please use either %s or %s but not both' % + (check_notes_prefix, check_messages_prefix)) + + if not has_check_fix and not has_check_message and not has_check_note: + sys.exit('%s, %s or %s not found in the input' % + (check_fixes_prefix, check_messages_prefix, check_notes_prefix)) + + has_check_fixes = has_check_fixes or has_check_fix + has_check_messages = has_check_messages or has_check_message + has_check_notes = has_check_notes or has_check_note + + check_fixes_prefixes.append(check_fixes_prefix) + check_messages_prefixes.append(check_messages_prefix) + check_notes_prefixes.append(check_notes_prefix) + + assert has_check_fixes or has_check_messages or has_check_notes # Remove the contents of the CHECK lines to avoid CHECKs matching on # themselves. We need to keep the comments to preserve line numbers while # avoiding empty lines which could potentially trigger formatting-related From e25c7d5fd1e6726049167f20e063c54ccbfebdc3 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Fri, 12 Oct 2018 16:41:37 +0000 Subject: [PATCH 350/686] Fix one additional test broken by the YAML quoting change. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344362 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/include-fixer/merge.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/include-fixer/merge.test b/test/include-fixer/merge.test index cee751b4d..230d38da7 100644 --- a/test/include-fixer/merge.test +++ b/test/include-fixer/merge.test @@ -6,7 +6,7 @@ Name: bar Contexts: - ContextType: Namespace ContextName: a -FilePath: ../include/bar.h +FilePath: '../include/bar.h' Type: Class Seen: 1 Used: 1 @@ -16,7 +16,7 @@ Name: bar Contexts: - ContextType: Namespace ContextName: a -FilePath: ../include/barbar.h +FilePath: '../include/barbar.h' Type: Class Seen: 1 Used: 0 From 60587ac679c0e49adc4a28f1b6dc40844ec2aabb Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Fri, 12 Oct 2018 16:51:48 +0000 Subject: [PATCH 351/686] [clangd] Return Command objects from onCodeAction, rather than ad-hoc JSON. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344363 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 17 +++++++++-------- clangd/Protocol.h | 1 - 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index ca898a153..02496ad48 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -339,20 +339,21 @@ void ClangdLSPServer::onCodeAction(CodeActionParams &Params) { return replyError(ErrorCode::InvalidParams, "onCodeAction called for non-added file"); - json::Array Commands; + std::vector Commands; for (Diagnostic &D : Params.context.diagnostics) { for (auto &F : getFixes(Params.textDocument.uri.file(), D)) { WorkspaceEdit WE; std::vector Edits(F.Edits.begin(), F.Edits.end()); - WE.changes = {{Params.textDocument.uri.uri(), std::move(Edits)}}; - Commands.push_back(json::Object{ - {"title", llvm::formatv("Apply fix: {0}", F.Message)}, - {"command", ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}, - {"arguments", {WE}}, - }); + Commands.emplace_back(); + Commands.back().title = llvm::formatv("Apply fix: {0}", F.Message); + Commands.back().command = ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND; + Commands.back().workspaceEdit.emplace(); + Commands.back().workspaceEdit->changes = { + {Params.textDocument.uri.uri(), std::move(Edits)}, + }; } } - reply(std::move(Commands)); + reply(json::Array(Commands)); } void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) { diff --git a/clangd/Protocol.h b/clangd/Protocol.h index 22da2e3af..bd0973f52 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -673,7 +673,6 @@ bool fromJSON(const llvm::json::Value &, ExecuteCommandParams &); struct Command : public ExecuteCommandParams { std::string title; }; - llvm::json::Value toJSON(const Command &C); /// Represents information about programming constructs like variables, classes, From 6159683008ba29a26cf290c0466ad4b4e207b907 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Fri, 12 Oct 2018 17:22:36 +0000 Subject: [PATCH 352/686] [clang-tidy] New checker for not null-terminated result caused by strlen(), size() or equal length New checker called bugprone-not-null-terminated-result. This check finds function calls where it is possible to cause a not null-terminated result. Usually the proper length of a string is strlen(src) + 1 or equal length of this expression, because the null terminator needs an extra space. Without the null terminator it can result in undefined behaviour when the string is read. The following function calls are checked: memcpy, wmemcpy, memcpy_s, wmemcpy_s, memchr, wmemchr, memmove, wmemmove, memmove_s, wmemmove_s, memset, wmemset, strerror_s, strncmp, wcsncmp, strxfrm, wcsxfrm The following is a real-world example where the programmer forgot to increase the passed third argument, which is size_t length. That is why the length of the allocated memory is problematic too. static char *StringCpy(const std::string &str) { char *result = reinterpret_cast(malloc(str.size())); memcpy(result, str.data(), str.size()); return result; } After running the tool fix-it rewrites all the necessary code according to the given options. If it is necessary, the buffer size will be increased to hold the null terminator. static char *StringCpy(const std::string &str) { char *result = reinterpret_cast(malloc(str.size() + 1)); strcpy(result, str.data()); return result; } Patch by Charusso. Differential ID: https://reviews.llvm.org/D45050 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344374 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/bugprone/BugproneTidyModule.cpp | 3 + clang-tidy/bugprone/CMakeLists.txt | 1 + .../bugprone/NotNullTerminatedResultCheck.cpp | 1024 +++++++++++++++++ .../bugprone/NotNullTerminatedResultCheck.h | 67 ++ docs/ReleaseNotes.rst | 9 + .../bugprone-not-null-terminated-result.rst | 132 +++ docs/clang-tidy/checks/list.rst | 1 + ...rminated-result-in-initialization-strlen.c | 106 ++ ...ull-terminated-result-memcpy-before-safe.c | 78 ++ ...null-terminated-result-memcpy-safe-cxx.cpp | 160 +++ ...null-terminated-result-memcpy-safe-other.c | 115 ++ ...e-not-null-terminated-result-memcpy-safe.c | 137 +++ ...gprone-not-null-terminated-result-strlen.c | 146 +++ ...rone-not-null-terminated-result-wcslen.cpp | 131 +++ ...ull-terminated-result-wmemcpy-safe-cxx.cpp | 136 +++ 15 files changed, 2246 insertions(+) create mode 100644 clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp create mode 100644 clang-tidy/bugprone/NotNullTerminatedResultCheck.h create mode 100644 docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst create mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c create mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c create mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp create mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c create mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c create mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-strlen.c create mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp create mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp diff --git a/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tidy/bugprone/BugproneTidyModule.cpp index 09a252b30..2d022ff37 100644 --- a/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -30,6 +30,7 @@ #include "MisplacedWideningCastCheck.h" #include "MoveForwardingReferenceCheck.h" #include "MultipleStatementMacroCheck.h" +#include "NotNullTerminatedResultCheck.h" #include "ParentVirtualCallCheck.h" #include "SizeofContainerCheck.h" #include "SizeofExpressionCheck.h" @@ -98,6 +99,8 @@ class BugproneModule : public ClangTidyModule { "bugprone-multiple-statement-macro"); CheckFactories.registerCheck( "bugprone-narrowing-conversions"); + CheckFactories.registerCheck( + "bugprone-not-null-terminated-result"); CheckFactories.registerCheck( "bugprone-parent-virtual-call"); CheckFactories.registerCheck( diff --git a/clang-tidy/bugprone/CMakeLists.txt b/clang-tidy/bugprone/CMakeLists.txt index b20997cc4..7ed0b07df 100644 --- a/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tidy/bugprone/CMakeLists.txt @@ -21,6 +21,7 @@ add_clang_library(clangTidyBugproneModule MisplacedWideningCastCheck.cpp MoveForwardingReferenceCheck.cpp MultipleStatementMacroCheck.cpp + NotNullTerminatedResultCheck.cpp ParentVirtualCallCheck.cpp SizeofContainerCheck.cpp SizeofExpressionCheck.cpp diff --git a/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp b/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp new file mode 100644 index 000000000..f4c3392d3 --- /dev/null +++ b/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp @@ -0,0 +1,1024 @@ +//===--- NotNullTerminatedResultCheck.cpp - clang-tidy ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NotNullTerminatedResultCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/PPCallbacks.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace bugprone { + +static const char *const FuncExprName = "entire-called-function-expr"; +static const char *const CastExprName = "cast-expr"; +static const char *const UnknownDestName = "destination-length-is-unknown"; +static const char *const NotJustCharTyName = "unsigned-or-signed-char"; +static const char *const DestArrayTyName = "destination-is-array-type"; +static const char *const DestVarDeclName = "destination-variable-declaration"; +static const char *const SrcVarDeclName = "source-variable-declaration"; +static const char *const UnknownLengthName = "given-length-is-unknown"; +static const char *const WrongLengthExprName = "strlen-or-size"; +static const char *const DestMallocExprName = "destination-malloc-expr"; +static const char *const DestExprName = "destination-decl-ref-expr"; +static const char *const SrcExprName = "source-expression-or-string-literal"; +static const char *const LengthExprName = "given-length-expression"; + +enum class LengthHandleKind { Increase, Decrease }; + +namespace { +static Preprocessor *PP; +} // namespace + +// The capacity: VariableArrayType, ConstantArrayType, argument of a 'malloc()' +// family function or an argument of a custom memory allocation. +static const Expr *getDestCapacityExpr(const MatchFinder::MatchResult &Result); + +static int getDestCapacity(const MatchFinder::MatchResult &Result); + +// Length could be an IntegerLiteral or length of a StringLiteral. +static int getLength(const Expr *E, const MatchFinder::MatchResult &Result); + +static int getGivenLength(const MatchFinder::MatchResult &Result); + +static StringRef exprToStr(const Expr *E, + const MatchFinder::MatchResult &Result); + +static SourceLocation exprLocEnd(const Expr *E, + const MatchFinder::MatchResult &Result) { + return Lexer::getLocForEndOfToken(E->getEndLoc(), 0, *Result.SourceManager, + Result.Context->getLangOpts()); +} + +//===----------------------------------------------------------------------===// +// Rewrite decision helper functions. +//===----------------------------------------------------------------------===// + +// Increment by integer '1' can result in overflow if it is the maximal value. +// After that it will be extended to 'size_t' and its value will be wrong, +// therefore we have to inject '+ 1UL' instead. +static bool isInjectUL(const MatchFinder::MatchResult &Result) { + return getGivenLength(Result) == std::numeric_limits::max(); +} + +// If the capacity of the destination array is unknown it is denoted as unknown. +static bool isKnownDest(const MatchFinder::MatchResult &Result) { + return !Result.Nodes.getNodeAs(UnknownDestName); +} + +// True if the capacity of the destination array is based on the given length, +// therefore it looks like it cannot overflow (e.g. 'malloc(given_length + 1)' +// Note: If the capacity and the given length is equal then the new function +// is a simple 'cpy()' and because it returns true it prevents increasing the +// given length. +static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result); + +// If we write/read from the same array it should be already null-terminated. +static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result); + +// We catch integers as a given length so we have to see if the length of the +// source array is the same length so that the function call is wrong. +static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result); + +// Example: memcpy(dest, str.data(), str.length()); +static bool isStringDataAndLength(const MatchFinder::MatchResult &Result); + +static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result); + +static bool isLengthEqualToSrcLength(const MatchFinder::MatchResult &Result); + +//===----------------------------------------------------------------------===// +// Code injection functions. +//===----------------------------------------------------------------------===// + +static void lengthDecrease(const Expr *LengthExpr, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); +static void lengthIncrease(const Expr *LengthExpr, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); + +// Increase or decrease an integral expression by one. +static void lengthExprHandle(LengthHandleKind LengthHandle, + const Expr *LengthExpr, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); + +// Increase or decrease the passed integral argument by one. +static void lengthArgHandle(LengthHandleKind LengthHandle, int ArgPos, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); + +// If the destination array is the same length as the given length we have to +// increase the capacity by one to create space for the the null terminator. +static bool destCapacityFix(const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); + +static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); + +static void renameFunc(StringRef NewFuncName, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); + +static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); + +static void insertDestCapacityArg(bool IsOverflows, StringRef Name, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); + +static void insertNullTerminatorExpr(StringRef Name, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); + +NotNullTerminatedResultCheck::NotNullTerminatedResultCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + WantToUseSafeFunctions(Options.get("WantToUseSafeFunctions", 1)) {} + +void NotNullTerminatedResultCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "WantToUseSafeFunctions", WantToUseSafeFunctions); +} + +void NotNullTerminatedResultCheck::registerPPCallbacks( + CompilerInstance &Compiler) { + PP = &Compiler.getPreprocessor(); +} + +namespace { +AST_MATCHER_P(Expr, hasDefinition, ast_matchers::internal::Matcher, + InnerMatcher) { + const Expr *SimpleNode = &Node; + SimpleNode = SimpleNode->IgnoreParenImpCasts(); + + if (InnerMatcher.matches(*SimpleNode, Finder, Builder)) + return true; + + auto DREHasInit = ignoringImpCasts( + declRefExpr(to(varDecl(hasInitializer(ignoringImpCasts(InnerMatcher)))))); + + if (DREHasInit.matches(*SimpleNode, Finder, Builder)) + return true; + + // - Example: int getLength(const char *str) { return strlen(str); } + auto CallExprReturnInit = ignoringImpCasts( + callExpr(callee(functionDecl(hasBody(has(returnStmt(hasReturnValue( + ignoringImpCasts(anyOf(DREHasInit, InnerMatcher)))))))))); + + if (CallExprReturnInit.matches(*SimpleNode, Finder, Builder)) + return true; + + // - Example: int length = getLength(src); + auto DREHasReturnInit = ignoringImpCasts( + declRefExpr(to(varDecl(hasInitializer(CallExprReturnInit))))); + + if (DREHasReturnInit.matches(*SimpleNode, Finder, Builder)) + return true; + + const char *const VarDeclName = "variable-declaration"; + auto DREHasDefinition = ignoringImpCasts(declRefExpr( + allOf(to(varDecl().bind(VarDeclName)), + hasAncestor(compoundStmt(hasDescendant(binaryOperator( + hasLHS(declRefExpr(to(varDecl(equalsBoundNode(VarDeclName))))), + hasRHS(ignoringImpCasts(InnerMatcher))))))))); + + if (DREHasDefinition.matches(*SimpleNode, Finder, Builder)) + return true; + + return false; +} +} // namespace + +void NotNullTerminatedResultCheck::registerMatchers(MatchFinder *Finder) { + auto IncOp = + binaryOperator(hasOperatorName("+"), + hasEitherOperand(ignoringParenImpCasts(integerLiteral()))); + + auto DecOp = + binaryOperator(hasOperatorName("-"), + hasEitherOperand(ignoringParenImpCasts(integerLiteral()))); + + auto HasIncOp = anyOf(ignoringImpCasts(IncOp), hasDescendant(IncOp)); + auto HasDecOp = anyOf(ignoringImpCasts(DecOp), hasDescendant(DecOp)); + + auto StringTy = type(hasUnqualifiedDesugaredType(recordType( + hasDeclaration(cxxRecordDecl(hasName("::std::basic_string")))))); + + auto AnyOfStringTy = + anyOf(hasType(StringTy), hasType(qualType(pointsTo(StringTy)))); + + auto CharTy = + anyOf(asString("char"), asString("wchar_t"), + allOf(anyOf(asString("unsigned char"), asString("signed char")), + type().bind(NotJustCharTyName))); + + auto CharTyArray = hasType(qualType(hasCanonicalType( + arrayType(hasElementType(CharTy)).bind(DestArrayTyName)))); + + auto CharTyPointer = + hasType(qualType(hasCanonicalType(pointerType(pointee(CharTy))))); + + auto AnyOfCharTy = anyOf(CharTyArray, CharTyPointer); + + //===--------------------------------------------------------------------===// + // The following six cases match problematic length expressions. + //===--------------------------------------------------------------------===// + + // - Example: char src[] = "foo"; strlen(src); + auto Strlen = + callExpr(callee(functionDecl(hasAnyName("::strlen", "::wcslen")))) + .bind(WrongLengthExprName); + + // - Example: std::string str = "foo"; str.size(); + auto SizeOrLength = + cxxMemberCallExpr( + allOf(on(expr(AnyOfStringTy)), + has(memberExpr(member(hasAnyName("size", "length")))))) + .bind(WrongLengthExprName); + + // - Example: char src[] = "foo"; sizeof(src); + auto SizeOfCharExpr = unaryExprOrTypeTraitExpr(has(expr(hasType(qualType( + hasCanonicalType(anyOf(arrayType(hasElementType(isAnyCharacter())), + pointerType(pointee(isAnyCharacter()))))))))); + + auto WrongLength = + anyOf(ignoringImpCasts(Strlen), ignoringImpCasts(SizeOrLength), + hasDescendant(Strlen), hasDescendant(SizeOrLength)); + + // - Example: length = strlen(src); + auto DREWithoutInc = + ignoringImpCasts(declRefExpr(to(varDecl(hasInitializer(WrongLength))))); + + auto AnyOfCallOrDREWithoutInc = anyOf(DREWithoutInc, WrongLength); + + // - Example: int getLength(const char *str) { return strlen(str); } + auto CallExprReturnWithoutInc = ignoringImpCasts(callExpr(callee(functionDecl( + hasBody(has(returnStmt(hasReturnValue(AnyOfCallOrDREWithoutInc)))))))); + + // - Example: int length = getLength(src); + auto DREHasReturnWithoutInc = ignoringImpCasts( + declRefExpr(to(varDecl(hasInitializer(CallExprReturnWithoutInc))))); + + auto AnyOfWrongLengthInit = + anyOf(AnyOfCallOrDREWithoutInc, CallExprReturnWithoutInc, + DREHasReturnWithoutInc); + + enum class StrlenKind { WithInc, WithoutInc }; + + const auto AnyOfLengthExpr = [=](StrlenKind LengthKind) { + return ignoringImpCasts(allOf( + unless(hasDefinition(SizeOfCharExpr)), + anyOf(allOf((LengthKind == StrlenKind::WithoutInc) + ? ignoringImpCasts(unless(hasDefinition(HasIncOp))) + : ignoringImpCasts( + allOf(hasDefinition(HasIncOp), + unless(hasDefinition(HasDecOp)))), + AnyOfWrongLengthInit), + ignoringImpCasts(integerLiteral().bind(WrongLengthExprName))), + expr().bind(LengthExprName))); + }; + + auto LengthWithoutInc = AnyOfLengthExpr(StrlenKind::WithoutInc); + auto LengthWithInc = AnyOfLengthExpr(StrlenKind::WithInc); + + //===--------------------------------------------------------------------===// + // The following five cases match the 'destination' array length's + // expression which is used in memcpy() and memmove() matchers. + //===--------------------------------------------------------------------===// + + auto SizeExpr = anyOf(SizeOfCharExpr, integerLiteral(equals(1))); + + auto MallocLengthExpr = allOf( + anyOf(argumentCountIs(1), argumentCountIs(2)), + hasAnyArgument(allOf(unless(SizeExpr), + expr(ignoringImpCasts(anyOf(HasIncOp, anything()))) + .bind(DestMallocExprName)))); + + // - Example: (char *)malloc(length); + auto DestMalloc = anyOf(castExpr(has(callExpr(MallocLengthExpr))), + callExpr(MallocLengthExpr)); + + // - Example: new char[length]; + auto DestCXXNewExpr = ignoringImpCasts( + cxxNewExpr(hasArraySize(expr().bind(DestMallocExprName)))); + + auto AnyOfDestInit = anyOf(DestMalloc, DestCXXNewExpr); + + // - Example: char dest[13]; or char dest[length]; + auto DestArrayTyDecl = declRefExpr( + to(anyOf(varDecl(CharTyArray).bind(DestVarDeclName), + varDecl(hasInitializer(AnyOfDestInit)).bind(DestVarDeclName)))); + + // - Example: foo[bar[baz]].qux; (or just ParmVarDecl) + auto DestUnknownDecl = + declRefExpr(allOf(to(varDecl(AnyOfCharTy).bind(DestVarDeclName)), + expr().bind(UnknownDestName))); + + auto AnyOfDestDecl = + allOf(anyOf(hasDefinition(anyOf(AnyOfDestInit, DestArrayTyDecl)), + DestUnknownDecl, anything()), + expr().bind(DestExprName)); + + auto SrcDecl = declRefExpr( + allOf(to(decl().bind(SrcVarDeclName)), + anyOf(hasAncestor(cxxMemberCallExpr().bind(SrcExprName)), + expr().bind(SrcExprName)))); + + auto SrcDeclMayInBinOp = + anyOf(ignoringImpCasts(SrcDecl), hasDescendant(SrcDecl)); + + auto AnyOfSrcDecl = anyOf(ignoringImpCasts(stringLiteral().bind(SrcExprName)), + SrcDeclMayInBinOp); + + auto NullTerminatorExpr = binaryOperator( + hasLHS(hasDescendant( + declRefExpr(to(varDecl(equalsBoundNode(DestVarDeclName)))))), + hasRHS(ignoringImpCasts( + anyOf(characterLiteral(equals(0U)), integerLiteral(equals(0)))))); + + //===--------------------------------------------------------------------===// + // The following nineteen cases match problematic function calls. + //===--------------------------------------------------------------------===// + + const auto WithoutSrc = [=](StringRef Name, int LengthPos, + StrlenKind LengthKind) { + return allOf( + callee(functionDecl(hasName(Name))), + hasArgument( + 0, allOf(AnyOfDestDecl, unless(hasAncestor(compoundStmt( + hasDescendant(NullTerminatorExpr)))))), + hasArgument(LengthPos, (LengthKind == StrlenKind::WithoutInc) + ? LengthWithoutInc + : LengthWithInc)); + }; + + const auto WithSrc = [=](StringRef Name, int SourcePos, int LengthPos, + StrlenKind LengthKind) { + return allOf(callee(functionDecl(hasName(Name))), + hasArgument(SourcePos ? 0 : 1, + allOf(AnyOfDestDecl, + unless(hasAncestor(compoundStmt( + hasDescendant(NullTerminatorExpr)))))), + hasArgument(SourcePos, AnyOfSrcDecl), + hasArgument(LengthPos, (LengthKind == StrlenKind::WithoutInc) + ? LengthWithoutInc + : LengthWithInc)); + }; + + auto Memcpy = WithSrc("::memcpy", 1, 2, StrlenKind::WithoutInc); + auto Wmemcpy = WithSrc("::wmemcpy", 1, 2, StrlenKind::WithoutInc); + auto Memcpy_s = WithSrc("::memcpy_s", 2, 3, StrlenKind::WithoutInc); + auto Wmemcpy_s = WithSrc("::wmemcpy_s", 2, 3, StrlenKind::WithoutInc); + auto Memchr = WithSrc("::memchr", 0, 2, StrlenKind::WithoutInc); + auto Wmemchr = WithSrc("::wmemchr", 0, 2, StrlenKind::WithoutInc); + auto Memmove = WithSrc("::memmove", 1, 2, StrlenKind::WithoutInc); + auto Wmemmove = WithSrc("::wmemmove", 1, 2, StrlenKind::WithoutInc); + auto Memmove_s = WithSrc("::memmove_s", 2, 3, StrlenKind::WithoutInc); + auto Wmemmove_s = WithSrc("::wmemmove_s", 2, 3, StrlenKind::WithoutInc); + auto Memset = WithoutSrc("::memset", 2, StrlenKind::WithInc); + auto Wmemset = WithoutSrc("::wmemset", 2, StrlenKind::WithInc); + auto Strerror_s = WithoutSrc("::strerror_s", 1, StrlenKind::WithoutInc); + auto StrncmpLHS = WithSrc("::strncmp", 1, 2, StrlenKind::WithInc); + auto WcsncmpLHS = WithSrc("::wcsncmp", 1, 2, StrlenKind::WithInc); + auto StrncmpRHS = WithSrc("::strncmp", 0, 2, StrlenKind::WithInc); + auto WcsncmpRHS = WithSrc("::wcsncmp", 0, 2, StrlenKind::WithInc); + auto Strxfrm = WithSrc("::strxfrm", 1, 2, StrlenKind::WithoutInc); + auto Wcsxfrm = WithSrc("::wcsxfrm", 1, 2, StrlenKind::WithoutInc); + + auto AnyOfMatchers = + anyOf(Memcpy, Wmemcpy, Memcpy_s, Wmemcpy_s, Memchr, Wmemchr, Memmove, + Wmemmove, Memmove_s, Wmemmove_s, Memset, Wmemset, Strerror_s, + StrncmpLHS, WcsncmpLHS, StrncmpRHS, WcsncmpRHS, Strxfrm, Wcsxfrm); + + Finder->addMatcher(callExpr(AnyOfMatchers).bind(FuncExprName), this); + + Finder->addMatcher( + castExpr(has(callExpr(anyOf(Memchr, Wmemchr)).bind(FuncExprName))) + .bind(CastExprName), + this); +} + +void NotNullTerminatedResultCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); + if (FuncExpr->getBeginLoc().isMacroID()) + return; + + if (WantToUseSafeFunctions && PP->isMacroDefined("__STDC_LIB_EXT1__")) { + Optional AreSafeFunctionsWanted; + + Preprocessor::macro_iterator It = PP->macro_begin(); + while (It != PP->macro_end() && !AreSafeFunctionsWanted.hasValue()) { + if (It->first->getName() == "__STDC_WANT_LIB_EXT1__") { + const auto *MI = PP->getMacroInfo(It->first); + const auto &T = MI->tokens().back(); + StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength()); + llvm::APInt IntValue; + ValueStr.getAsInteger(10, IntValue); + AreSafeFunctionsWanted = IntValue.getZExtValue(); + } + + ++It; + } + + if (AreSafeFunctionsWanted.hasValue()) + UseSafeFunctions = AreSafeFunctionsWanted.getValue(); + } + + StringRef Name = FuncExpr->getDirectCallee()->getName(); + if (Name.startswith("mem") || Name.startswith("wmem")) + memoryHandlerFunctionFix(Name, Result); + else if (Name == "strerror_s") + strerror_sFix(Result); + else if (Name.endswith("ncmp")) + ncmpFix(Name, Result); + else if (Name.endswith("xfrm")) + xfrmFix(Name, Result); +} + +void NotNullTerminatedResultCheck::memoryHandlerFunctionFix( + StringRef Name, const MatchFinder::MatchResult &Result) { + if (isCorrectGivenLength(Result)) + return; + + if (Name.endswith("chr")) { + memchrFix(Name, Result); + return; + } + + if ((Name.contains("cpy") || Name.contains("move")) && + isDestAndSrcEquals(Result)) + return; + + auto Diag = + diag(Result.Nodes.getNodeAs(FuncExprName)->getBeginLoc(), + "the result from calling '%0' is not null-terminated") + << Name; + + if (Name.endswith("cpy")) + memcpyFix(Name, Result, Diag); + else if (Name.endswith("cpy_s")) + memcpy_sFix(Name, Result, Diag); + else if (Name.endswith("move")) + memmoveFix(Name, Result, Diag); + else if (Name.endswith("move_s")) { + destCapacityFix(Result, Diag); + lengthArgHandle(LengthHandleKind::Increase, 3, Result, Diag); + } else if (Name.endswith("set")) { + lengthArgHandle(LengthHandleKind::Decrease, 2, Result, Diag); + } +} + +void NotNullTerminatedResultCheck::memcpyFix( + StringRef Name, const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + bool IsOverflows = destCapacityFix(Result, Diag); + + // If it cannot be rewritten to string handler function. + if (Result.Nodes.getNodeAs(NotJustCharTyName)) { + if (UseSafeFunctions && isKnownDest(Result)) { + renameFunc((Name[0] != 'w') ? "memcpy_s" : "wmemcpy_s", Result, Diag); + insertDestCapacityArg(IsOverflows, Name, Result, Diag); + } + + lengthArgHandle(LengthHandleKind::Increase, 2, Result, Diag); + return; + } + + bool IsCopy = + isLengthEqualToSrcLength(Result) || isDestBasedOnGivenLength(Result); + + bool IsSafe = UseSafeFunctions && IsOverflows && isKnownDest(Result) && + !isDestBasedOnGivenLength(Result); + + bool IsDestLengthNotRequired = + IsSafe && getLangOpts().CPlusPlus && + Result.Nodes.getNodeAs(DestArrayTyName); + + renameMemcpy(Name, IsCopy, IsSafe, Result, Diag); + + if (IsSafe && !IsDestLengthNotRequired) + insertDestCapacityArg(IsOverflows, Name, Result, Diag); + + if (IsCopy) + removeArg(2, Result, Diag); + + if (!IsCopy && !IsSafe) + insertNullTerminatorExpr(Name, Result, Diag); +} + +void NotNullTerminatedResultCheck::memcpy_sFix( + StringRef Name, const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + bool IsOverflows = destCapacityFix(Result, Diag); + + if (Result.Nodes.getNodeAs(NotJustCharTyName)) { + lengthArgHandle(LengthHandleKind::Increase, 3, Result, Diag); + return; + } + + bool RemoveDestLength = getLangOpts().CPlusPlus && + Result.Nodes.getNodeAs(DestArrayTyName); + bool IsCopy = isLengthEqualToSrcLength(Result); + bool IsSafe = IsOverflows; + + renameMemcpy(Name, IsCopy, IsSafe, Result, Diag); + + if (!IsSafe || (IsSafe && RemoveDestLength)) + removeArg(1, Result, Diag); + else if (IsOverflows && isKnownDest(Result)) + lengthArgHandle(LengthHandleKind::Increase, 1, Result, Diag); + + if (IsCopy) + removeArg(3, Result, Diag); + + if (!IsCopy && !IsSafe) + insertNullTerminatorExpr(Name, Result, Diag); +} + +void NotNullTerminatedResultCheck::memchrFix( + StringRef Name, const MatchFinder::MatchResult &Result) { + const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); + if (const auto GivenCL = + dyn_cast_or_null(FuncExpr->getArg(1))) + if (GivenCL->getValue() != 0) + return; + + auto Diag = diag(FuncExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(), + "the length is too short to include the null terminator"); + + if (const auto *CastExpr = Result.Nodes.getNodeAs(CastExprName)) { + const auto CastRemoveFix = FixItHint::CreateRemoval(SourceRange( + CastExpr->getBeginLoc(), FuncExpr->getBeginLoc().getLocWithOffset(-1))); + Diag << CastRemoveFix; + } + StringRef NewFuncName = (Name[0] != 'w') ? "strchr" : "wcschr"; + renameFunc(NewFuncName, Result, Diag); + removeArg(2, Result, Diag); +} + +void NotNullTerminatedResultCheck::memmoveFix( + StringRef Name, const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + bool IsOverflows = destCapacityFix(Result, Diag); + + if (UseSafeFunctions && isKnownDest(Result)) { + renameFunc((Name[0] != 'w') ? "memmove_s" : "wmemmove_s", Result, Diag); + insertDestCapacityArg(IsOverflows, Name, Result, Diag); + } + + lengthArgHandle(LengthHandleKind::Increase, 2, Result, Diag); +} + +void NotNullTerminatedResultCheck::strerror_sFix( + const MatchFinder::MatchResult &Result) { + StringRef Name = "strerror_s"; + auto Diag = + diag(Result.Nodes.getNodeAs(FuncExprName)->getBeginLoc(), + "the result from calling '%0' is not null-terminated and " + "missing the last character of the error message") + << Name; + + destCapacityFix(Result, Diag); + lengthArgHandle(LengthHandleKind::Increase, 1, Result, Diag); +} + +void NotNullTerminatedResultCheck::ncmpFix( + StringRef Name, const MatchFinder::MatchResult &Result) { + const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); + const Expr *FirstArgExpr = FuncExpr->getArg(0)->IgnoreImpCasts(); + const Expr *SecondArgExpr = FuncExpr->getArg(1)->IgnoreImpCasts(); + bool IsLengthTooLong = false; + + if (const auto *LengthExpr = + Result.Nodes.getNodeAs(WrongLengthExprName)) { + const Expr *LengthExprArg = LengthExpr->getArg(0); + StringRef FirstExprStr = exprToStr(FirstArgExpr, Result).trim(' '); + StringRef SecondExprStr = exprToStr(SecondArgExpr, Result).trim(' '); + StringRef LengthArgStr = exprToStr(LengthExprArg, Result).trim(' '); + IsLengthTooLong = + LengthArgStr == FirstExprStr || LengthArgStr == SecondExprStr; + } else { + int SrcLength = + getLength(Result.Nodes.getNodeAs(SrcExprName), Result); + int GivenLength = getGivenLength(Result); + IsLengthTooLong = GivenLength - 1 == SrcLength; + } + + if (!IsLengthTooLong && !isStringDataAndLength(Result)) + return; + + auto Diag = diag(FuncExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(), + "comparison length is too long and might lead to a " + "buffer overflow"); + + lengthArgHandle(LengthHandleKind::Decrease, 2, Result, Diag); +} + +void NotNullTerminatedResultCheck::xfrmFix( + StringRef Name, const MatchFinder::MatchResult &Result) { + if (!isDestCapacityOverflows(Result)) + return; + + auto Diag = + diag(Result.Nodes.getNodeAs(FuncExprName)->getBeginLoc(), + "the result from calling '%0' is not null-terminated") + << Name; + + destCapacityFix(Result, Diag); + lengthArgHandle(LengthHandleKind::Increase, 2, Result, Diag); +} + +//===---------------------------------------------------------------------===// +// All the helper functions. +//===---------------------------------------------------------------------===// + +static StringRef exprToStr(const Expr *E, + const MatchFinder::MatchResult &Result) { + if (!E) + return ""; + + return Lexer::getSourceText( + CharSourceRange::getTokenRange(E->getSourceRange()), + *Result.SourceManager, Result.Context->getLangOpts(), 0); +} + +static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result) { + if (const auto *DestVD = Result.Nodes.getNodeAs(DestVarDeclName)) + if (const auto *SrcVD = Result.Nodes.getNodeAs(SrcVarDeclName)) + return DestVD->getCanonicalDecl() == SrcVD->getCanonicalDecl(); + + return false; +} + +static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result) { + if (Result.Nodes.getNodeAs(WrongLengthExprName)) + return !isLengthEqualToSrcLength(Result); + + return false; +} + +static const Expr *getDestCapacityExpr(const MatchFinder::MatchResult &Result) { + if (const auto *DestMalloc = Result.Nodes.getNodeAs(DestMallocExprName)) + return DestMalloc; + + if (const auto *DestTy = Result.Nodes.getNodeAs(DestArrayTyName)) + if (const auto *DestVAT = dyn_cast_or_null(DestTy)) + return DestVAT->getSizeExpr(); + + if (const auto *DestVD = Result.Nodes.getNodeAs(DestVarDeclName)) + if (const auto DestTL = DestVD->getTypeSourceInfo()->getTypeLoc()) + if (const auto DestCTL = DestTL.getAs()) + return DestCTL.getSizeExpr(); + + return nullptr; +} + +static int getLength(const Expr *E, const MatchFinder::MatchResult &Result) { + llvm::APSInt Length; + + if (const auto *LengthDRE = dyn_cast_or_null(E)) + if (const auto *LengthVD = dyn_cast_or_null(LengthDRE->getDecl())) + if (!isa(LengthVD)) + if (const Expr *LengthInit = LengthVD->getInit()) + if (LengthInit->EvaluateAsInt(Length, *Result.Context)) + return Length.getZExtValue(); + + if (const auto *LengthIL = dyn_cast_or_null(E)) + return LengthIL->getValue().getZExtValue(); + + if (const auto *StrDRE = dyn_cast_or_null(E)) + if (const auto *StrVD = dyn_cast_or_null(StrDRE->getDecl())) + if (const Expr *StrInit = StrVD->getInit()) + if (const auto *StrSL = + dyn_cast_or_null(StrInit->IgnoreImpCasts())) + return StrSL->getLength(); + + if (const auto *SrcSL = dyn_cast_or_null(E)) + return SrcSL->getLength(); + + return 0; +} + +static int getDestCapacity(const MatchFinder::MatchResult &Result) { + if (const auto *DestCapacityExpr = getDestCapacityExpr(Result)) + return getLength(DestCapacityExpr, Result); + + return 0; +} + +static int getGivenLength(const MatchFinder::MatchResult &Result) { + const auto *LengthExpr = Result.Nodes.getNodeAs(LengthExprName); + if (int Length = getLength(LengthExpr, Result)) + return Length; + + if (const auto *StrlenExpr = dyn_cast_or_null(LengthExpr)) + if (StrlenExpr->getNumArgs() > 0) + if (const Expr *StrlenArg = StrlenExpr->getArg(0)->IgnoreImpCasts()) + if (int StrlenArgLength = getLength(StrlenArg, Result)) + return StrlenArgLength; + + return 0; +} + +static bool isStringDataAndLength(const MatchFinder::MatchResult &Result) { + StringRef DestStr = + exprToStr(Result.Nodes.getNodeAs(DestExprName), Result); + StringRef SrcStr = + exprToStr(Result.Nodes.getNodeAs(SrcExprName), Result); + StringRef GivenLengthStr = + exprToStr(Result.Nodes.getNodeAs(LengthExprName), Result); + + bool ProblematicLength = + GivenLengthStr.contains(".size") || GivenLengthStr.contains(".length"); + + return ProblematicLength && + (SrcStr.contains(".data") || DestStr.contains(".data")); +} + +static bool isLengthEqualToSrcLength(const MatchFinder::MatchResult &Result) { + if (isStringDataAndLength(Result)) + return true; + + int GivenLength = getGivenLength(Result); + + // It is the length without the null terminator. + int SrcLength = getLength(Result.Nodes.getNodeAs(SrcExprName), Result); + + if (GivenLength != 0 && GivenLength == SrcLength) + return true; + + // If 'strlen()' check the VarDecl of the argument is equal to source VarDecl. + if (const auto *StrlenExpr = Result.Nodes.getNodeAs(LengthExprName)) + if (StrlenExpr->getNumArgs() > 0) + if (const auto *StrlenDRE = dyn_cast_or_null( + StrlenExpr->getArg(0)->IgnoreImpCasts())) + if (const auto *SrcVD = Result.Nodes.getNodeAs(SrcVarDeclName)) + return dyn_cast_or_null(StrlenDRE->getDecl()) == SrcVD; + + return false; +} + +static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result) { + if (!isKnownDest(Result)) + return true; + + const auto *DestCapacityExpr = getDestCapacityExpr(Result); + const auto *LengthExpr = Result.Nodes.getNodeAs(LengthExprName); + int DestCapacity = getLength(DestCapacityExpr, Result); + int GivenLength = getGivenLength(Result); + + if (GivenLength != 0 && DestCapacity != 0) + return isLengthEqualToSrcLength(Result) && DestCapacity == GivenLength; + + StringRef DestCapacityExprStr = exprToStr(DestCapacityExpr, Result); + StringRef LengthExprStr = exprToStr(LengthExpr, Result); + + // Assume that it cannot overflow if the expression of the destination + // capacity contains '+ 1'. + if (DestCapacityExprStr.contains("+1") || DestCapacityExprStr.contains("+ 1")) + return false; + + if (DestCapacityExprStr != "" && DestCapacityExprStr == LengthExprStr) + return true; + + return true; +} + +static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result) { + StringRef DestCapacityExprStr = + exprToStr(getDestCapacityExpr(Result), Result).trim(' '); + StringRef LengthExprStr = + exprToStr(Result.Nodes.getNodeAs(LengthExprName), Result).trim(' '); + + return DestCapacityExprStr != "" && LengthExprStr != "" && + DestCapacityExprStr.contains(LengthExprStr); +} + +static void lengthDecrease(const Expr *LengthExpr, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + // This is the following structure: ((strlen(src) * 2) + 1) + // InnerOpExpr: ~~~~~~~~~~~~^~~ + // OuterOpExpr: ~~~~~~~~~~~~~~~~~~^~~ + if (const auto *OuterOpExpr = + dyn_cast_or_null(LengthExpr->IgnoreParenCasts())) { + const Expr *LHSExpr = OuterOpExpr->getLHS(); + const Expr *RHSExpr = OuterOpExpr->getRHS(); + const auto *InnerOpExpr = + isa(RHSExpr->IgnoreCasts()) ? LHSExpr : RHSExpr; + + // This is the following structure: ((strlen(src) * 2) + 1) + // LHSRemoveRange: ~~ + // RHSRemoveRange: ~~~~~~ + SourceRange LHSRemoveRange(LengthExpr->getBeginLoc(), + InnerOpExpr->getBeginLoc().getLocWithOffset(-1)); + SourceRange RHSRemoveRange(exprLocEnd(InnerOpExpr, Result), + LengthExpr->getEndLoc()); + const auto LHSRemoveFix = FixItHint::CreateRemoval(LHSRemoveRange); + const auto RHSRemoveFix = FixItHint::CreateRemoval(RHSRemoveRange); + + if (LengthExpr->getBeginLoc() == InnerOpExpr->getBeginLoc()) + Diag << RHSRemoveFix; + else if (LengthExpr->getEndLoc() == InnerOpExpr->getEndLoc()) + Diag << LHSRemoveFix; + else + Diag << LHSRemoveFix << RHSRemoveFix; + } else { + const auto InsertDecreaseFix = + FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), " - 1"); + Diag << InsertDecreaseFix; + } +} + +static void lengthIncrease(const Expr *LengthExpr, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + bool NeedInnerParen = dyn_cast_or_null(LengthExpr) && + cast(LengthExpr)->getOpcode() != BO_Add; + + if (NeedInnerParen) { + const auto InsertFirstParenFix = + FixItHint::CreateInsertion(LengthExpr->getBeginLoc(), "("); + const auto InsertPlusOneAndSecondParenFix = + FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), + !isInjectUL(Result) ? ") + 1" : ") + 1UL"); + Diag << InsertFirstParenFix << InsertPlusOneAndSecondParenFix; + } else { + const auto InsertPlusOneFix = + FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), + !isInjectUL(Result) ? " + 1" : " + 1UL"); + Diag << InsertPlusOneFix; + } +} + +static void lengthExprHandle(LengthHandleKind LengthHandle, + const Expr *LengthExpr, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + if (!LengthExpr) + return; + + bool IsMacroDefinition = false; + StringRef LengthExprStr = exprToStr(LengthExpr, Result); + + Preprocessor::macro_iterator It = PP->macro_begin(); + while (It != PP->macro_end() && !IsMacroDefinition) { + if (It->first->getName() == LengthExprStr) + IsMacroDefinition = true; + + ++It; + } + + if (!IsMacroDefinition) { + if (const auto *LengthIL = dyn_cast_or_null(LengthExpr)) { + const size_t NewLength = LengthIL->getValue().getZExtValue() + + (LengthHandle == LengthHandleKind::Increase + ? (isInjectUL(Result) ? 1UL : 1) + : -1); + const auto NewLengthFix = FixItHint::CreateReplacement( + LengthIL->getSourceRange(), + (Twine(NewLength) + (isInjectUL(Result) ? "UL" : "")).str()); + Diag << NewLengthFix; + return; + } + + if (LengthHandle == LengthHandleKind::Increase) + lengthIncrease(LengthExpr, Result, Diag); + else + lengthDecrease(LengthExpr, Result, Diag); + } else { + if (LengthHandle == LengthHandleKind::Increase) { + const auto InsertPlusOneFix = + FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), + !isInjectUL(Result) ? " + 1" : " + 1UL"); + Diag << InsertPlusOneFix; + } else { + const auto InsertMinusOneFix = + FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), " - 1"); + Diag << InsertMinusOneFix; + } + } +} + +static void lengthArgHandle(LengthHandleKind LengthHandle, int ArgPos, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); + const Expr *LengthExpr = FuncExpr->getArg(ArgPos)->IgnoreImpCasts(); + lengthExprHandle(LengthHandle, LengthExpr, Result, Diag); +} + +static bool destCapacityFix(const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + bool IsOverflows = isDestCapacityOverflows(Result); + if (IsOverflows) + lengthExprHandle(LengthHandleKind::Increase, getDestCapacityExpr(Result), + Result, Diag); + + return IsOverflows; +} + +static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + // This is the following structure: (src, '\0', strlen(src)) + // ArgToRemove: ~~~~~~~~~~~ + // LHSArg: ~~~~ + // RemoveArgFix: ~~~~~~~~~~~~~ + const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); + const Expr *ArgToRemove = FuncExpr->getArg(ArgPos); + const Expr *LHSArg = FuncExpr->getArg(ArgPos - 1); + const auto RemoveArgFix = FixItHint::CreateRemoval( + SourceRange(exprLocEnd(LHSArg, Result), + exprLocEnd(ArgToRemove, Result).getLocWithOffset(-1))); + Diag << RemoveArgFix; +} + +static void renameFunc(StringRef NewFuncName, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); + int FuncNameLength = + FuncExpr->getDirectCallee()->getIdentifier()->getLength(); + SourceRange FuncNameRange( + FuncExpr->getBeginLoc(), + FuncExpr->getBeginLoc().getLocWithOffset(FuncNameLength - 1)); + + const auto FuncNameFix = + FixItHint::CreateReplacement(FuncNameRange, NewFuncName); + Diag << FuncNameFix; +} + +static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + SmallString<10> NewFuncName; + NewFuncName = (Name[0] != 'w') ? "str" : "wcs"; + NewFuncName += IsCopy ? "cpy" : "ncpy"; + NewFuncName += IsSafe ? "_s" : ""; + renameFunc(NewFuncName, Result, Diag); +} + +static void insertDestCapacityArg(bool IsOverflows, StringRef Name, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); + SmallString<64> NewSecondArg; + + if (int DestLength = getDestCapacity(Result)) { + NewSecondArg = Twine(IsOverflows ? DestLength + 1 : DestLength).str(); + } else { + NewSecondArg = + (Twine(exprToStr(getDestCapacityExpr(Result), Result)) + + (IsOverflows ? (!isInjectUL(Result) ? " + 1" : " + 1UL") : "")) + .str(); + } + + NewSecondArg += ", "; + const auto InsertNewArgFix = FixItHint::CreateInsertion( + FuncExpr->getArg(1)->getBeginLoc(), NewSecondArg); + Diag << InsertNewArgFix; +} + +static void insertNullTerminatorExpr(StringRef Name, + const MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag) { + const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); + int FuncLocStartColumn = + Result.SourceManager->getPresumedColumnNumber(FuncExpr->getBeginLoc()); + SourceRange SpaceRange( + FuncExpr->getBeginLoc().getLocWithOffset(-FuncLocStartColumn + 1), + FuncExpr->getBeginLoc()); + StringRef SpaceBeforeStmtStr = Lexer::getSourceText( + CharSourceRange::getCharRange(SpaceRange), *Result.SourceManager, + Result.Context->getLangOpts(), 0); + + SmallString<128> NewAddNullTermExprStr; + NewAddNullTermExprStr = + (Twine('\n') + SpaceBeforeStmtStr + + exprToStr(Result.Nodes.getNodeAs(DestExprName), Result) + "[" + + exprToStr(Result.Nodes.getNodeAs(LengthExprName), Result) + + "] = " + ((Name[0] != 'w') ? "\'\\0\';" : "L\'\\0\';")) + .str(); + + const auto AddNullTerminatorExprFix = FixItHint::CreateInsertion( + exprLocEnd(FuncExpr, Result).getLocWithOffset(1), NewAddNullTermExprStr); + Diag << AddNullTerminatorExprFix; +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/bugprone/NotNullTerminatedResultCheck.h b/clang-tidy/bugprone/NotNullTerminatedResultCheck.h new file mode 100644 index 000000000..762203e19 --- /dev/null +++ b/clang-tidy/bugprone/NotNullTerminatedResultCheck.h @@ -0,0 +1,67 @@ +//===--- NotNullTerminatedResultCheck.h - clang-tidy ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NOT_NULL_TERMINATED_RESULT_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NOT_NULL_TERMINATED_RESULT_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace bugprone { + +/// Finds function calls where it is possible to cause a not null-terminated +/// result. Usually the proper length of a string is ``strlen(src) + 1`` or +/// equal length of this expression, because the null terminator needs an extra +/// space. Without the null terminator it can result in undefined behaviour +/// when the string is read. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-not-null-terminated-result.html +class NotNullTerminatedResultCheck : public ClangTidyCheck { +public: + NotNullTerminatedResultCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void registerPPCallbacks(CompilerInstance &Compiler) override; + +private: + // If non-zero it is specifying if the target environment is considered to + // implement '_s' suffixed memory and string handler functions which are safer + // than older version (e.g. 'memcpy_s()'). The default value is ``1``. + const int WantToUseSafeFunctions; + + bool UseSafeFunctions = false; + + void memoryHandlerFunctionFix( + StringRef Name, const ast_matchers::MatchFinder::MatchResult &Result); + void memcpyFix(StringRef Name, + const ast_matchers::MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); + void memcpy_sFix(StringRef Name, + const ast_matchers::MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); + void memchrFix(StringRef Name, + const ast_matchers::MatchFinder::MatchResult &Result); + void memmoveFix(StringRef Name, + const ast_matchers::MatchFinder::MatchResult &Result, + DiagnosticBuilder &Diag); + void strerror_sFix(const ast_matchers::MatchFinder::MatchResult &Result); + void ncmpFix(StringRef Name, + const ast_matchers::MatchFinder::MatchResult &Result); + void xfrmFix(StringRef Name, + const ast_matchers::MatchFinder::MatchResult &Result); +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NOT_NULL_TERMINATED_RESULT_H diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index 5adb42871..4ea031490 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -116,6 +116,15 @@ Improvements to clang-tidy Detects usage of the deprecated member types of ``std::ios_base`` and replaces those that have a non-deprecated equivalent. +- New :doc:`bugprone-not-null-terminated-result + ` check + + Finds function calls where it is possible to cause a not null-terminated + result. Usually the proper length of a string is ``strlen(src) + 1`` or equal + length of this expression, because the null terminator needs an extra space. + Without the null terminator it can result in undefined behaviour when the + string is read. + - New :doc:`readability-magic-numbers ` check. diff --git a/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst b/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst new file mode 100644 index 000000000..72f465819 --- /dev/null +++ b/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst @@ -0,0 +1,132 @@ +.. title:: clang-tidy - bugprone-not-null-terminated-result + +bugprone-not-null-terminated-result +=================================== + +Finds function calls where it is possible to cause a not null-terminated result. +Usually the proper length of a string is ``strlen(src) + 1`` or equal length of +this expression, because the null terminator needs an extra space. Without the +null terminator it can result in undefined behaviour when the string is read. + +The following function calls are checked: + +``memcpy``, ``wmemcpy``, ``memcpy_s``, ``wmemcpy_s``, ``memchr``, ``wmemchr``, +``memmove``, ``wmemmove``, ``memmove_s``, ``wmemmove_s``, ``memset``, +``wmemset``, ``strerror_s``, ``strncmp``, ``wcsncmp``, ``strxfrm``, ``wcsxfrm`` + +The following is a real-world example where the programmer forgot to increase +the passed third argument, which is ``size_t length``. That is why the length +of the allocated memory is problematic too. + + .. code-block:: c + + static char *StringCpy(const std::string &str) { + char *result = reinterpret_cast(malloc(str.size())); + memcpy(result, str.data(), str.size()); + return result; + } + +In addition to issuing warnings, fix-it rewrites all the necessary code. If it +is necessary, the buffer size will be increased to hold the null terminator. + + .. code-block:: c + + static char *StringCpy(const std::string &str) { + char *result = reinterpret_cast(malloc(str.size() + 1)); + strcpy(result, str.data()); + return result; + } + +.. _MemcpyTransformation: + +Transformation rules of 'memcpy()' +---------------------------------- + +It is possible to rewrite the ``memcpy()`` and ``memcpy_s()`` calls as the +following four functions: ``strcpy()``, ``strncpy()``, ``strcpy_s()``, +``strncpy_s()``, where the latter two are the safer versions of the former two. +Respectively it is possible to rewrite ``wmemcpy()`` functions in the same way. + +Rewrite to a string handler function is not possible: + +- If the type of the destination array is not just ``char`` (``unsigned char`` + or ``signed char``), that means the new function is cannot be any string + handler function. Fix-it adds ``+ 1`` to the given length of copy function. + +Rewrite based on the destination array: + +- If copy to the destination array cannot *overflow then the new function should + be the older copy function (ending with ``cpy``), because it is more + efficient than the safe version. + +- If copy to the destination array can *overflow and + ``AreSafeFunctionsAvailable`` is set to ``Yes``, ``y`` or non-zero and it is + possible to obtain the capacity of the destination array then the new function + could be the safe version (ending with ``cpy_s``). + +- If the new function is could be safe version and C++ files are analysed then + the length of the destination array can be omitted. + +- *It is possible to overflow: + - Unknown the capacity of the destination array. + - If the given length is equal to the destination capacity. + +Rewrite based on the length of the source string: + +- If the given length is ``strlen(source)`` or equal length of this expression + then the new function should be the older copy function (ending with ``cpy``), + as it is more efficient than the safe version. + +- Otherwise we assume that the programmer wanted to copy `n` characters, so the + new function is ``ncpy``-like which is could be safe. + +Transformations with 'strlen()' or equal length of this expression +------------------------------------------------------------------ + +In general, the following transformations are could happen: + +(Note: If a wide-character handler function exists of the following functions +it handled in the same way.) + +Memory handler functions +^^^^^^^^^^^^^^^^^^^^^^^^ + +- ``memcpy``: See in the + :ref:`Transformation rules of 'memcpy()'` section. + +- ``memchr``: + - Usually there is a C-style cast, and it is needed to be removed, because the + new function ``strchr``'s return type is correct. + - Also the given length is not needed in the new function. + +- ``memmove``: + - If safe functions are available the new function is ``memmove_s``, it has + four arguments: + - destination array, + - length of the destination array, + - source string, + - length of the source string which is incremented by one. + - If safe functions are not available the given length is incremented by one. + +- ``memmove_s``: given length is incremented by one. + +- ``memset``: given length has to be truncated without the ``+ 1``. + +String handler functions +^^^^^^^^^^^^^^^^^^^^^^^^ + +- ``strerror_s``: given length is incremented by one. + +- ``strncmp``: If the third argument is the first or the second argument's + ``length + 1``, then it has to be truncated without the ``+ 1`` operation. + +- ``strxfrm``: given length is incremented by one. + +Options +------- + +.. option:: WantToUseSafeFunctions + + An integer non-zero value specifying if the target environment is considered + to implement '_s' suffixed memory and string handler functions which are + safer than older version (e.g. 'memcpy_s()'). The default value is ``1``. diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index cc966a9b0..90079f01c 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -44,6 +44,7 @@ Clang-Tidy Checks bugprone-misplaced-widening-cast bugprone-move-forwarding-reference bugprone-multiple-statement-macro + bugprone-not-null-terminated-result bugprone-parent-virtual-call bugprone-sizeof-container bugprone-sizeof-expression diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c b/test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c new file mode 100644 index 000000000..0b5669ec1 --- /dev/null +++ b/test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c @@ -0,0 +1,106 @@ +// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ +// RUN: -- -std=c11 + +typedef unsigned int size_t; +typedef int errno_t; +size_t strlen(const char *); +char *strerror(int); +char *strchr(const char *, int); +errno_t *strncpy_s(char *, const char *, size_t); +errno_t strerror_s(char *, size_t, int); +int strncmp(const char *, const char *, size_t); +size_t strxfrm(char *, const char *, size_t); + +void *memchr(const void *, int, size_t); +void *memset(void *, int, size_t); + +int getLengthWithInc(const char *str) { + return strlen(str) + 1; +} + + +void bad_memchr(char *position, const char *src) { + int length = strlen(src); + position = (char *)memchr(src, '\0', length); + // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result] + // CHECK-FIXES: position = strchr(src, '\0'); +} + +void good_memchr(char *pos, const char *src) { + pos = strchr(src, '\0'); +} + +void bad_memset_1(const char *src) { + char dest[13]; + memset(dest, '-', getLengthWithInc(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memset' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: memset(dest, '-', getLengthWithInc(src) - 1); +} + +void good_memset1(const char *src) { + char dst[13]; + memset(dst, '-', getLengthWithInc(src) - 1); +} + +void bad_strerror_s(int errno) { + char dest[13]; + int length = strlen(strerror(errno)); + strerror_s(dest, length, errno); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'strerror_s' is not null-terminated and missing the last character of the error message [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest[14]; + // CHECK-FIXES-NEXT: int length = strlen(strerror(errno)); + // CHECK-FIXES-NEXT: strerror_s(dest, length + 1, errno); +} + +void good_strerror_s(int errno) { + char dst[14]; + int length = strlen(strerror(errno)); + strerror_s(dst, length + 1, errno); +} + +int bad_strncmp_1(char *str1, const char *str2) { + int length = strlen(str1) + 1; + return strncmp(str1, str2, length); + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] + // CHECK-FIXES: strncmp(str1, str2, length - 1); +} + +int good_strncmp_1(char *str1, const char *str2) { + int length = strlen(str1) + 1; + return strncmp(str1, str2, length - 1); +} + +int bad_strncmp_2(char *str2) { + return strncmp(str2, "foobar", (strlen("foobar") + 1)); + // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] + // CHECK-FIXES: strncmp(str2, "foobar", strlen("foobar")); +} + +int bad_strncmp_3(char *str3) { + return strncmp(str3, "foobar", 1 + strlen("foobar")); + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] + // CHECK-FIXES: strncmp(str3, "foobar", strlen("foobar")); +} + +int good_strncmp_2_3(char *str) { + return strncmp(str, "foobar", strlen("foobar")); +} + +void bad_strxfrm(const char *long_source_name) { + char long_destination_name[13]; + int very_long_length_definition_name = strlen(long_source_name); + strxfrm(long_destination_name, long_source_name, + very_long_length_definition_name); + // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: the result from calling 'strxfrm' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char long_destination_name[14]; + // CHECK-FIXES-NEXT: int very_long_length_definition_name = strlen(long_source_name); + // CHECK-FIXES-NEXT: strxfrm(long_destination_name, long_source_name, + // CHECK-FIXES-NEXT: very_long_length_definition_name + 1); +} + +void good_strxfrm(const char *long_source_name) { + char long_destination_name[14]; + int very_long_length_definition_name = strlen(long_source_name); + strxfrm(long_destination_name, long_source_name, + very_long_length_definition_name + 1); +} diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c b/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c new file mode 100644 index 000000000..4f9117471 --- /dev/null +++ b/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c @@ -0,0 +1,78 @@ +// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ +// RUN: -config="{CheckOptions: \ +// RUN: [{key: bugprone-not-null-terminated-result.WantToUseSafeFunctions, \ +// RUN: value: 1}]}" \ +// RUN: -- -std=c11 + +// It is not defined therefore the safe functions are unavailable. +// #define __STDC_LIB_EXT1__ 1 + +#define __STDC_WANT_LIB_EXT1__ 1 + +typedef unsigned int size_t; +typedef int errno_t; +size_t strlen(const char *); +void *malloc(size_t); + +char *strcpy(char *, const char *); +void *memcpy(void *, const void *, size_t); + + +//===----------------------------------------------------------------------===// +// memcpy() - destination array tests +//===----------------------------------------------------------------------===// + +void bad_memcpy_not_just_char_dest(const char *src) { + unsigned char dest00[13]; + memcpy(dest00, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: memcpy(dest00, src, strlen(src) + 1); +} + +void good_memcpy_not_just_char_dest(const char *src) { + unsigned char dst00[13]; + memcpy(dst00, src, strlen(src) + 1); +} + +void bad_memcpy_known_dest(const char *src) { + char dest01[13]; + memcpy(dest01, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: strcpy(dest01, src); +} + +void good_memcpy_known_dest(const char *src) { + char dst01[13]; + strcpy(dst01, src); +} + +//===----------------------------------------------------------------------===// +// memcpy() - length tests +//===----------------------------------------------------------------------===// + +void bad_memcpy_full_source_length(const char *src) { + char dest20[13]; + memcpy(dest20, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: strcpy(dest20, src); +} + +void good_memcpy_full_source_length(const char *src) { + char dst20[13]; + strcpy(dst20, src); +} + +void bad_memcpy_partial_source_length(const char *src) { + char dest21[13]; + memcpy(dest21, src, strlen(src) - 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: strncpy(dest21, src, strlen(src) - 1); + // CHECK-FIXES-NEXT: dest21[strlen(src) - 1] = '\0'; +} + +void good_memcpy_partial_source_length(const char *src) { + char dst21[13]; + strncpy(dst21, src, strlen(src) - 1); + dst21[strlen(src) - 1] = '\0'; +} + diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp b/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp new file mode 100644 index 000000000..e5a73791c --- /dev/null +++ b/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp @@ -0,0 +1,160 @@ +// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ +// RUN: -- -std=c++11 + +#define __STDC_LIB_EXT1__ 1 +#define __STDC_WANT_LIB_EXT1__ 1 + +namespace std { +template +struct basic_string { + basic_string(); + const T *data() const; + unsigned long size() const; +}; +typedef basic_string string; +} +typedef unsigned int size_t; +typedef int errno_t; +size_t strlen(const char *); +void *malloc(size_t); +void *realloc(void *, size_t); + +template +errno_t strncpy_s(char (&dest)[size], const char *src, size_t length); +errno_t strncpy_s(char *, size_t, const char *, size_t); + +template +char *strncpy(char (&dest)[size], const char *src, size_t length); +char *strncpy(char *, const char *, size_t); + +template +errno_t strcpy_s(char (&dest)[size], const char *); +errno_t strcpy_s(char *, size_t, const char *); + +template +char *strcpy(char (&dest)[size], const char *); +char *strcpy(char *, const char *); + +errno_t memcpy_s(void *, size_t, const void *, size_t); +void *memcpy(void *, const void *, size_t); + + +//===----------------------------------------------------------------------===// +// memcpy() - destination array tests +//===----------------------------------------------------------------------===// + +void bad_memcpy_not_just_char_dest(const char *src) { + unsigned char dest00[13]; + memcpy(dest00, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: unsigned char dest00[14]; + // CHECK-FIXES-NEXT: memcpy_s(dest00, 14, src, strlen(src) + 1); +} + +void good_memcpy_not_just_char_dest(const char *src) { + unsigned char dst00[14]; + memcpy_s(dst00, 14, src, strlen(src) + 1); +} + +void bad_memcpy_known_dest(const char *src) { + char dest01[13]; + memcpy(dest01, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: dest01[14]; + // CHECK-FIXES-NEXT: strcpy_s(dest01, src); +} + +void good_memcpy_known_dest(const char *src) { + char dst01[14]; + strcpy_s(dst01, src); +} + +//===----------------------------------------------------------------------===// +// memcpy() - length tests +//===----------------------------------------------------------------------===// + +void bad_memcpy_full_source_length(std::string src) { + char *dest20; + dest20 = reinterpret_cast(malloc(src.size())); + memcpy(dest20, src.data(), src.size()); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: dest20 = reinterpret_cast(malloc(src.size() + 1)); + // CHECK-FIXES-NEXT: strcpy(dest20, src.data()); +} + +void good_memcpy_full_source_length(std::string src) { + char dst20[14]; + strcpy_s(dst20, src.data()); +} + +void bad_memcpy_partial_source_length(const char *src) { + char dest21[13]; + memcpy(dest21, src, strlen(src) - 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest21[14]; + // CHECK-FIXES-NEXT: strncpy_s(dest21, src, strlen(src) - 1); +} + +void good_memcpy_partial_source_length(const char *src) { + char dst21[14]; + strncpy_s(dst21, src, strlen(src) - 1); +} + + +//===----------------------------------------------------------------------===// +// memcpy_s() - destination array tests +//===----------------------------------------------------------------------===// + +void bad_memcpy_s_unknown_dest(char *dest40, const char *src) { + memcpy_s(dest40, 13, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: strcpy_s(dest40, 13, src); +} + +void good_memcpy_s_unknown_dest(char *dst40, const char *src) { + strcpy_s(dst40, 13, src); +} + +void bad_memcpy_s_known_dest(const char *src) { + char dest41[13]; + memcpy_s(dest41, 13, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest41[14]; + // CHECK-FIXES: strcpy_s(dest41, src); +} + +void good_memcpy_s_known_dest(const char *src) { + char dst41[14]; + strcpy_s(dst41, src); +} + +//===----------------------------------------------------------------------===// +// memcpy_s() - length tests +//===----------------------------------------------------------------------===// + +void bad_memcpy_s_full_source_length(const char *src) { + char dest60[13]; + memcpy_s(dest60, 13, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest60[14]; + // CHECK-FIXES-NEXT: strcpy_s(dest60, src); +} + +void good_memcpy_s_full_source_length(const char *src) { + char dst60[14]; + strcpy_s(dst60, src); +} + +void bad_memcpy_s_partial_source_length(const char *src) { + char dest61[13]; + memcpy_s(dest61, 13, src, strlen(src) - 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest61[14]; + // CHECK-FIXES-NEXT: strncpy_s(dest61, src, strlen(src) - 1); +} + +void good_memcpy_s_partial_source_length(const char *src) { + char dst61[14]; + strncpy_s(dst61, src, strlen(src) - 1); +} + diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c b/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c new file mode 100644 index 000000000..84152255a --- /dev/null +++ b/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c @@ -0,0 +1,115 @@ +// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ +// RUN: -- -std=c11 + +#define __STDC_LIB_EXT1__ 1 +#define __STDC_WANT_LIB_EXT1__ 1 + +typedef unsigned int size_t; +typedef int errno_t; +size_t strlen(const char *); +void *malloc(size_t); +void *realloc(void *, size_t); + +errno_t strncpy_s(char *, size_t, const char *, size_t); +errno_t strcpy_s(char *, size_t, const char *); +char *strcpy(char *, const char *); + +errno_t memcpy_s(void *, size_t, const void *, size_t); +void *memcpy(void *, const void *, size_t); + +#define SRC_LENGTH 3 +#define SRC "foo" + + +void good_memcpy_known_src() { + char dest[13]; + char src[] = "foobar"; + memcpy(dest, src, sizeof(src)); +} + +void good_memcpy_null_terminated(const char *src) { + char dest[13]; + const int length = strlen(src); + memcpy(dest, src, length); + dest[length] = '\0'; +} + +void good_memcpy_proper_length(const char *src) { + char *dest = 0; + int length = strlen(src) + 1; + dest = (char *)malloc(length); + memcpy(dest, src, length); +} + +void may_bad_memcpy_unknown_length(const char *src, int length) { + char dest[13]; + memcpy(dest, src, length); +} + +void may_bad_memcpy_const_length(const char *src) { + char dest[13]; + memcpy(dest, src, 12); +} + +void bad_memcpy_unknown_dest(char *dest01, const char *src) { + memcpy(dest01, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: strcpy(dest01, src); +} + +void good_memcpy_unknown_dest(char *dst01, const char *src) { + strcpy(dst01, src); +} + +void bad_memcpy_variable_array(int dest_length) { + char dest02[dest_length + 1]; + memcpy(dest02, "foobarbazqux", strlen("foobarbazqux")); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: strcpy(dest02, "foobarbazqux"); +} + +void good_memcpy_variable_array(int dest_length) { + char dst02[dest_length + 1]; + strcpy(dst02, "foobarbazqux"); +} + +void bad_memcpy_equal_src_length_and_length() { + char dest03[13]; + const char *src = "foobarbazqux"; + memcpy(dest03, src, 12); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: strcpy(dest03, src); +} + +void good_memcpy_equal_src_length_and_length() { + char dst03[13]; + const char *src = "foobarbazqux"; + strcpy(dst03, src); +} + +void bad_memcpy_dest_size_overflows(const char *src) { + const int length = strlen(src); + char *dest04 = (char *)malloc(length); + memcpy(dest04, src, length); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char *dest04 = (char *)malloc(length + 1); + // CHECK-FIXES-NEXT: strcpy(dest04, src); +} + +void good_memcpy_dest_size_overflows(const char *src) { + const int length = strlen(src); + char *dst04 = (char *)malloc(length + 1); + strcpy(dst04, src); +} + +void bad_memcpy_macro() { + unsigned char dest05[13]; + memcpy(dest05, SRC, SRC_LENGTH); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: memcpy_s(dest05, 13, SRC, SRC_LENGTH + 1); +} + +void good_memcpy_macro() { + unsigned char dst05[13]; + memcpy_s(dst05, 13, SRC, SRC_LENGTH + 1); +} diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c b/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c new file mode 100644 index 000000000..dfbd5487a --- /dev/null +++ b/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c @@ -0,0 +1,137 @@ +// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ +// RUN: -- -std=c11 + +#define __STDC_LIB_EXT1__ 1 +#define __STDC_WANT_LIB_EXT1__ 1 + +typedef unsigned int size_t; +typedef int errno_t; +size_t strlen(const char *); +void *malloc(size_t); +void *realloc(void *, size_t); + +errno_t strncpy_s(char *, size_t, const char *, size_t); +errno_t strcpy_s(char *, size_t, const char *); +char *strcpy(char *, const char *); + +errno_t memcpy_s(void *, size_t, const void *, size_t); +void *memcpy(void *, const void *, size_t); + +//===----------------------------------------------------------------------===// +// memcpy() - destination array tests +//===----------------------------------------------------------------------===// + +void bad_memcpy_not_just_char_dest(const char *src) { + unsigned char dest00[13]; + memcpy(dest00, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: unsigned char dest00[14]; + // CHECK-FIXES-NEXT: memcpy_s(dest00, 14, src, strlen(src) + 1); +} + +void good_memcpy_not_just_char_dest(const char *src) { + unsigned char dst00[14]; + memcpy_s(dst00, 14, src, strlen(src) + 1); +} + +void bad_memcpy_known_dest(const char *src) { + char dest01[13]; + memcpy(dest01, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest01[14]; + // CHECK-FIXES: strcpy_s(dest01, 14, src); +} + +void good_memcpy_known_dest(const char *src) { + char dst01[14]; + strcpy_s(dst01, 14, src); +} + +//===----------------------------------------------------------------------===// +// memcpy() - length tests +//===----------------------------------------------------------------------===// + +void bad_memcpy_full_source_length(const char *src) { + char dest20[13]; + memcpy(dest20, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest20[14]; + // CHECK-FIXES-NEXT: strcpy_s(dest20, 14, src); +} + +void good_memcpy_full_source_length(const char *src) { + char dst20[14]; + strcpy_s(dst20, 14, src); +} + +void bad_memcpy_partial_source_length(const char *src) { + char dest21[13]; + memcpy(dest21, src, strlen(src) - 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest21[14]; + // CHECK-FIXES-NEXT: strncpy_s(dest21, 14, src, strlen(src) - 1); +} + +void good__memcpy_partial_source_length(const char *src) { + char dst21[14]; + strncpy_s(dst21, 14, src, strlen(src) - 1); +} + + +//===----------------------------------------------------------------------===// +// memcpy_s() - destination array tests +//===----------------------------------------------------------------------===// + +void bad_memcpy_s_unknown_dest(char *dest40, const char *src) { + memcpy_s(dest40, 13, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: strcpy_s(dest40, 13, src); +} + +void good_memcpy_s_unknown_dest(char *dst40, const char *src) { + strcpy_s(dst40, 13, src); +} + +void bad_memcpy_s_known_dest(const char *src) { + char dest41[13]; + memcpy_s(dest41, 13, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest41[14]; + // CHECK-FIXES-NEXT: strcpy_s(dest41, 14, src); +} + +void good_memcpy_s_known_dest(const char *src) { + char dst41[14]; + strcpy_s(dst41, 14, src); +} + +//===----------------------------------------------------------------------===// +// memcpy_s() - length tests +//===----------------------------------------------------------------------===// + +void bad_memcpy_s_full_source_length(const char *src) { + char dest60[13]; + memcpy_s(dest60, 13, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest60[14]; + // CHECK-FIXES-NEXT: strcpy_s(dest60, 14, src); +} + +void good_memcpy_s_full_source_length(const char *src) { + char dst60[14]; + strcpy_s(dst60, 14, src); +} + +void bad_memcpy_s_partial_source_length(const char *src) { + char dest61[13]; + memcpy_s(dest61, 13, src, strlen(src) - 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest61[14]; + // CHECK-FIXES-NEXT: strncpy_s(dest61, 14, src, strlen(src) - 1); +} + +void good_memcpy_s_partial_source_length(const char *src) { + char dst61[14]; + strncpy_s(dst61, 14, src, strlen(src) - 1); +} + diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-strlen.c b/test/clang-tidy/bugprone-not-null-terminated-result-strlen.c new file mode 100644 index 000000000..a7d04f5e2 --- /dev/null +++ b/test/clang-tidy/bugprone-not-null-terminated-result-strlen.c @@ -0,0 +1,146 @@ +// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ +// RUN: -- -std=c11 + +#define __STDC_LIB_EXT1__ 1 +#define __STDC_WANT_LIB_EXT1__ 1 + +typedef unsigned int size_t; +typedef int errno_t; +size_t strlen(const char *); +char *strerror(int); + +char *strchr(const char *, int); +errno_t strncpy_s(char *, size_t, const char *, size_t); +errno_t strerror_s(char *, size_t, int); +int strncmp(const char *, const char *, size_t); +size_t strxfrm(char *, const char *, size_t); + +void *memchr(const void *, int, size_t); +void *memmove(void *, const void *, size_t); +errno_t memmove_s(void *, size_t, const void *, size_t); +void *memset(void *, int, size_t); + + +void bad_memchr_1(char *position, const char *src) { + position = (char *)memchr(src, '\0', strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result] + // CHECK-FIXES: position = strchr(src, '\0'); +} + +void good_memchr_1(char *pos, const char *src) { + pos = strchr(src, '\0'); +} + +void bad_memchr_2(char *position) { + position = (char *)memchr("foobar", '\0', 6); + // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result] + // CHECK-FIXES: position = strchr("foobar", '\0'); +} + +void good_memchr_2(char *pos) { + pos = strchr("foobar", '\0'); +} + + +void bad_memmove(const char *src) { + char dest[13]; + memmove(dest, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memmove' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest[14]; + // CHECK-FIXES-NEXT: memmove_s(dest, 14, src, strlen(src) + 1); +} + +void good_memmove(const char *src) { + char dst[14]; + memmove_s(dst, 13, src, strlen(src) + 1); +} + +void bad_memmove_s(char *dest, const char *src) { + memmove_s(dest, 13, src, strlen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memmove_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: memmove_s(dest, 13, src, strlen(src) + 1); +} + +void good_memmove_s_1(char *dest, const char *src) { + memmove_s(dest, 13, src, strlen(src) + 1); +} + +void bad_memset(const char *src) { + char dest[13]; + memset(dest, '-', strlen(src) + 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memset' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: memset(dest, '-', strlen(src)); +} + +void good_memset(const char *src) { + char dst[13]; + memset(dst, '-', strlen(src)); +} + +void bad_strerror_s(int errno) { + char dest[13]; + strerror_s(dest, strlen(strerror(errno)), errno); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'strerror_s' is not null-terminated and missing the last character of the error message [bugprone-not-null-terminated-result] + // CHECK-FIXES: char dest[14]; + // CHECK-FIXES-NEXT: strerror_s(dest, strlen(strerror(errno)) + 1, errno); +} + +void good_strerror_s(int errno) { + char dst[14]; + strerror_s(dst, strlen(strerror(errno)) + 1, errno); +} + +int bad_strncmp_1(char *str0, const char *str1) { + return strncmp(str0, str1, (strlen(str0) + 1)); + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] + // CHECK-FIXES: strncmp(str0, str1, strlen(str0)); +} + +int bad_strncmp_2(char *str2, const char *str3) { + return strncmp(str2, str3, 1 + strlen(str2)); + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] + // CHECK-FIXES: strncmp(str2, str3, strlen(str2)); +} + +int good_strncmp_1_2(char *str4, const char *str5) { + return strncmp(str4, str5, strlen(str4)); +} + +int bad_strncmp_3(char *str6) { + return strncmp(str6, "string", 7); + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] + // CHECK-FIXES: strncmp(str6, "string", 6); +} + +int good_strncmp_3(char *str7) { + return strncmp(str7, "string", 6); +} + +void bad_strxfrm_1(const char *long_source_name) { + char long_destination_array_name[13]; + strxfrm(long_destination_array_name, long_source_name, + strlen(long_source_name)); + // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: the result from calling 'strxfrm' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char long_destination_array_name[14]; + // CHECK-FIXES-NEXT: strxfrm(long_destination_array_name, long_source_name, + // CHECK-FIXES-NEXT: strlen(long_source_name) + 1); +} + +void good_strxfrm_1(const char *long_source_name) { + char long_destination_array_name[14]; + strxfrm(long_destination_array_name, long_source_name, + strlen(long_source_name) + 1); +} + +void bad_strxfrm_2() { + char long_destination_array_name1[16]; + strxfrm(long_destination_array_name1, "long_source_name", 16); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'strxfrm' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: char long_destination_array_name1[17]; + // CHECK-FIXES: strxfrm(long_destination_array_name1, "long_source_name", 17); +} + +void good_strxfrm_2() { + char long_destination_array_name2[17]; + strxfrm(long_destination_array_name2, "long_source_name", 17); +} diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp b/test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp new file mode 100644 index 000000000..c25637b87 --- /dev/null +++ b/test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp @@ -0,0 +1,131 @@ +// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ +// RUN: -- -std=c++11 + +#define __STDC_LIB_EXT1__ 1 +#define __STDC_WANT_LIB_EXT1__ 1 + +typedef unsigned int size_t; +typedef int errno_t; +size_t wcslen(const wchar_t *); + +wchar_t *wcschr(const wchar_t *, int); +errno_t wcsncpy_s(wchar_t *, size_t, const wchar_t *, size_t); +int wcsncmp(const wchar_t *, const wchar_t *, size_t); +size_t wcsxfrm(wchar_t *, const wchar_t *, size_t); + +void *wmemchr(const void *, int, size_t); +void *wmemmove(void *, const void *, size_t); +errno_t wmemmove_s(void *, size_t, const void *, size_t); +void *wmemset(void *, int, size_t); + + +void bad_wmemchr_1(wchar_t *position, const wchar_t *src) { + position = (wchar_t *)wmemchr(src, L'\0', wcslen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result] + // CHECK-FIXES: position = wcschr(src, L'\0'); +} + +void good_wmemchr_1(wchar_t *pos, const wchar_t *src) { + pos = wcschr(src, L'\0'); +} + +void bad_wmemchr_2(wchar_t *position) { + position = (wchar_t *)wmemchr(L"foobar", L'\0', 6); + // CHECK-MESSAGES: :[[@LINE-1]]:51: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result] + // CHECK-FIXES: position = wcschr(L"foobar", L'\0'); +} + +void good_wmemchr_2(wchar_t *pos) { + pos = wcschr(L"foobar", L'\0'); +} + + +void bad_wmemmove(const wchar_t *src) { + wchar_t dest[13]; + wmemmove(dest, src, wcslen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemmove' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: wchar_t dest[14]; + // CHECK-FIXES-NEXT: wmemmove_s(dest, 14, src, wcslen(src) + 1); +} + +void good_wmemmove(const wchar_t *src) { + wchar_t dst[14]; + wmemmove_s(dst, 13, src, wcslen(src) + 1); +} + +void bad_wmemmove_s(wchar_t *dest, const wchar_t *src) { + wmemmove_s(dest, 13, src, wcslen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemmove_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: wmemmove_s(dest, 13, src, wcslen(src) + 1); +} + +void good_wmemmove_s_1(wchar_t *dest, const wchar_t *src) { + wmemmove_s(dest, 13, src, wcslen(src) + 1); +} + +void bad_wmemset(const wchar_t *src) { + wchar_t dest[13]; + wmemset(dest, L'-', wcslen(src) + 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemset' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: wmemset(dest, L'-', wcslen(src)); +} + +void good_wmemset(const wchar_t *src) { + wchar_t dst[13]; + wmemset(dst, L'-', wcslen(src)); +} + +int bad_wcsncmp_1(wchar_t *wcs0, const wchar_t *wcs1) { + return wcsncmp(wcs0, wcs1, (wcslen(wcs0) + 1)); + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] + // CHECK-FIXES: wcsncmp(wcs0, wcs1, wcslen(wcs0)); +} + +int bad_wcsncmp_2(wchar_t *wcs2, const wchar_t *wcs3) { + return wcsncmp(wcs2, wcs3, 1 + wcslen(wcs2)); + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] + // CHECK-FIXES: wcsncmp(wcs2, wcs3, wcslen(wcs2)); +} + +int good_wcsncmp_1_2(wchar_t *wcs4, const wchar_t *wcs5) { + return wcsncmp(wcs4, wcs5, wcslen(wcs4)); +} + +int bad_wcsncmp_3(wchar_t *wcs6) { + return wcsncmp(wcs6, L"string", 7); + // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] + // CHECK-FIXES: wcsncmp(wcs6, L"string", 6); +} + +int good_wcsncmp_3(wchar_t *wcs7) { + return wcsncmp(wcs7, L"string", 6); +} + +void bad_wcsxfrm_1(const wchar_t *long_source_name) { + wchar_t long_destination_array_name[13]; + wcsxfrm(long_destination_array_name, long_source_name, + wcslen(long_source_name)); + // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: the result from calling 'wcsxfrm' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: wchar_t long_destination_array_name[14]; + // CHECK-FIXES-NEXT: wcsxfrm(long_destination_array_name, long_source_name, + // CHECK-FIXES-NEXT: wcslen(long_source_name) + 1); +} + +void good_wcsxfrm_1(const wchar_t *long_source_name) { + wchar_t long_destination_array_name[14]; + wcsxfrm(long_destination_array_name, long_source_name, + wcslen(long_source_name) + 1); +} + +void bad_wcsxfrm_2() { + wchar_t long_destination_array_name1[16]; + wcsxfrm(long_destination_array_name1, L"long_source_name", 16); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wcsxfrm' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: wchar_t long_destination_array_name1[17]; + // CHECK-FIXES: wcsxfrm(long_destination_array_name1, L"long_source_name", 17); +} + +void good_wcsxfrm_2() { + wchar_t long_destination_array_name2[17]; + wcsxfrm(long_destination_array_name2, L"long_source_name", 17); +} diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp b/test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp new file mode 100644 index 000000000..91867bd48 --- /dev/null +++ b/test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp @@ -0,0 +1,136 @@ +// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ +// RUN: -- -std=c++11 + +#define __STDC_LIB_EXT1__ 1 +#define __STDC_WANT_LIB_EXT1__ 1 + +typedef unsigned int size_t; +typedef int errno_t; +size_t wcslen(const wchar_t *); +void *malloc(size_t); +void *realloc(void *, size_t); + +template +errno_t wcsncpy_s(wchar_t (&dest)[size], const wchar_t *src, size_t length); +errno_t wcsncpy_s(wchar_t *, size_t, const wchar_t *, size_t); + +template +wchar_t *wcsncpy(wchar_t (&dest)[size], const wchar_t *src, size_t length); +wchar_t *wcsncpy(wchar_t *, const wchar_t *, size_t); + +template +errno_t wcscpy_s(wchar_t (&dest)[size], const wchar_t *); +errno_t wcscpy_s(wchar_t *, size_t, const wchar_t *); + +template +wchar_t *wcscpy(wchar_t (&dest)[size], const wchar_t *); +wchar_t *wcscpy(wchar_t *, const wchar_t *); + +errno_t wmemcpy_s(wchar_t *, size_t, const wchar_t *, size_t); +wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t); + + +//===----------------------------------------------------------------------===// +// wmemcpy() - destination array tests +//===----------------------------------------------------------------------===// + +void bad_wmemcpy_known_dest(const wchar_t *src) { + wchar_t dest01[13]; + wmemcpy(dest01, src, wcslen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: wchar_t dest01[14]; + // CHECK-FIXES-NEXT: wcscpy_s(dest01, src); +} + +void good_wmemcpy_known_dest(const wchar_t *src) { + wchar_t dst01[14]; + wcscpy_s(dst01, src); +} + +//===----------------------------------------------------------------------===// +// wmemcpy() - length tests +//===----------------------------------------------------------------------===// + +void bad_wmemcpy_full_source_length(const wchar_t *src) { + wchar_t dest20[13]; + wmemcpy(dest20, src, wcslen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: wchar_t dest20[14]; + // CHECK-FIXES-NEXT: wcscpy_s(dest20, src); +} + +void good_wmemcpy_full_source_length(const wchar_t *src) { + wchar_t dst20[14]; + wcscpy_s(dst20, src); +} + +void bad_wmemcpy_partial_source_length(const wchar_t *src) { + wchar_t dest21[13]; + wmemcpy(dest21, src, wcslen(src) - 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: wchar_t dest21[14]; + // CHECK-FIXES-NEXT: wcsncpy_s(dest21, src, wcslen(src) - 1); +} + +void good_wmemcpy_partial_source_length(const wchar_t *src) { + wchar_t dst21[14]; + wcsncpy_s(dst21, src, wcslen(src) - 1); +} + +//===----------------------------------------------------------------------===// +// wmemcpy_s() - destination array tests +//===----------------------------------------------------------------------===// + +void bad_wmemcpy_s_unknown_dest(wchar_t *dest40, const wchar_t *src) { + wmemcpy_s(dest40, 13, src, wcslen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: wcscpy_s(dest40, 13, src); +} + +void good_wmemcpy_s_unknown_dest(wchar_t *dst40, const wchar_t *src) { + wcscpy_s(dst40, 13, src); +} + +void bad_wmemcpy_s_known_dest(const wchar_t *src) { + wchar_t dest41[13]; + wmemcpy_s(dest41, 13, src, wcslen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: wchar_t dest41[14]; + // CHECK-FIXES-NEXT: wcscpy_s(dest41, src); +} + +void good_wmemcpy_s_known_dest(const wchar_t *src) { + wchar_t dst41[13]; + wcscpy_s(dst41, src); +} + +//===----------------------------------------------------------------------===// +// wmemcpy_s() - length tests +//===----------------------------------------------------------------------===// + +void bad_wmemcpy_s_full_source_length(const wchar_t *src) { + wchar_t dest60[13]; + wmemcpy_s(dest60, 13, src, wcslen(src)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: wchar_t dest60[14]; + // CHECK-FIXES-NEXT: wcscpy_s(dest60, src); +} + +void good_wmemcpy_s_full_source_length(const wchar_t *src) { + wchar_t dst60[13]; + wcscpy_s(dst60, src); +} + +void bad_wmemcpy_s_partial_source_length(const wchar_t *src) { + wchar_t dest61[13]; + wmemcpy_s(dest61, 13, src, wcslen(src) - 1); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy_s' is not null-terminated [bugprone-not-null-terminated-result] + // CHECK-FIXES: wchar_t dest61[14]; + // CHECK-FIXES-NEXT: wcsncpy_s(dest61, src, wcslen(src) - 1); +} + +void good_wmemcpy_s_partial_source_length(const wchar_t *src) { + wchar_t dst61[13]; + wcsncpy_s(dst61, src, wcslen(src) - 1); +} + From 197f4c1680d143f25fe3e1b915d8dcfdb243b4b4 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Fri, 12 Oct 2018 17:36:04 +0000 Subject: [PATCH 353/686] [doc] fix markup in clang-tidy bugprone-not-null-terminated-result git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344379 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../checks/bugprone-not-null-terminated-result.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst b/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst index 72f465819..9a0ce54c5 100644 --- a/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst +++ b/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst @@ -55,11 +55,11 @@ Rewrite to a string handler function is not possible: Rewrite based on the destination array: -- If copy to the destination array cannot *overflow then the new function should +- If copy to the destination array cannot overflow then the new function should be the older copy function (ending with ``cpy``), because it is more efficient than the safe version. -- If copy to the destination array can *overflow and +- If copy to the destination array can overflow and ``AreSafeFunctionsAvailable`` is set to ``Yes``, ``y`` or non-zero and it is possible to obtain the capacity of the destination array then the new function could be the safe version (ending with ``cpy_s``). @@ -67,7 +67,7 @@ Rewrite based on the destination array: - If the new function is could be safe version and C++ files are analysed then the length of the destination array can be omitted. -- *It is possible to overflow: +- It is possible to overflow: - Unknown the capacity of the destination array. - If the given length is equal to the destination capacity. @@ -95,17 +95,21 @@ Memory handler functions :ref:`Transformation rules of 'memcpy()'` section. - ``memchr``: + - Usually there is a C-style cast, and it is needed to be removed, because the new function ``strchr``'s return type is correct. - Also the given length is not needed in the new function. - ``memmove``: + - If safe functions are available the new function is ``memmove_s``, it has four arguments: + - destination array, - length of the destination array, - source string, - length of the source string which is incremented by one. + - If safe functions are not available the given length is incremented by one. - ``memmove_s``: given length is incremented by one. From d373657428f200dd31a6019cbc36cd991f06f334 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Fri, 12 Oct 2018 17:47:43 +0000 Subject: [PATCH 354/686] [clangd] NFC fix semicolon warning git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344384 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/IndexAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clangd/index/IndexAction.cpp b/clangd/index/IndexAction.cpp index 68f58336c..2aad9489d 100644 --- a/clangd/index/IndexAction.cpp +++ b/clangd/index/IndexAction.cpp @@ -74,7 +74,7 @@ createStaticIndexingAction(SymbolCollector::Options Opts, return llvm::make_unique( std::make_shared(std::move(Opts)), std::move(Includes), IndexOpts, SymbolsCallback, RefsCallback); -}; +} } // namespace clangd } // namespace clang From dc5cec35da61e93883fbaa9b6d390e60607608af Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sat, 13 Oct 2018 07:58:05 +0000 Subject: [PATCH 355/686] [clang-tidy] add IgnoreMacros option to modernize-use-equals-delete And also enable it by default to be consistent with e.g. modernize-use-using. This improves consistency inside the check itself as well: both checks are now disabled in macros by default. This helps e.g. when running this check on client code where the macro is provided by the system, so there is no easy way to modify it. Reviewed By: alexfh Differential Revision: https://reviews.llvm.org/D53217 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344440 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/modernize/UseEqualsDeleteCheck.cpp | 8 +++++++- clang-tidy/modernize/UseEqualsDeleteCheck.h | 7 ++++++- docs/clang-tidy/checks/modernize-use-equals-delete.rst | 5 +++++ test/clang-tidy/modernize-use-equals-delete-macros.cpp | 10 ++++++++++ test/clang-tidy/modernize-use-equals-delete.cpp | 6 ++++++ 5 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 test/clang-tidy/modernize-use-equals-delete-macros.cpp diff --git a/clang-tidy/modernize/UseEqualsDeleteCheck.cpp b/clang-tidy/modernize/UseEqualsDeleteCheck.cpp index f5adb13f5..fc8425d94 100644 --- a/clang-tidy/modernize/UseEqualsDeleteCheck.cpp +++ b/clang-tidy/modernize/UseEqualsDeleteCheck.cpp @@ -21,6 +21,10 @@ namespace modernize { static const char SpecialFunction[] = "SpecialFunction"; static const char DeletedNotPublic[] = "DeletedNotPublic"; +void UseEqualsDeleteCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IgnoreMacros", IgnoreMacros); +} + void UseEqualsDeleteCheck::registerMatchers(MatchFinder *Finder) { if (!getLangOpts().CPlusPlus) return; @@ -57,6 +61,8 @@ void UseEqualsDeleteCheck::check(const MatchFinder::MatchResult &Result) { SourceLocation EndLoc = Lexer::getLocForEndOfToken( Func->getEndLoc(), 0, *Result.SourceManager, getLangOpts()); + if (Func->getLocation().isMacroID() && IgnoreMacros) + return; // FIXME: Improve FixItHint to make the method public. diag(Func->getLocation(), "use '= delete' to prohibit calling of a special member function") @@ -66,7 +72,7 @@ void UseEqualsDeleteCheck::check(const MatchFinder::MatchResult &Result) { // Ignore this warning in macros, since it's extremely noisy in code using // DISALLOW_COPY_AND_ASSIGN-style macros and there's no easy way to // automatically fix the warning when macros are in play. - if (Func->getLocation().isMacroID()) + if (Func->getLocation().isMacroID() && IgnoreMacros) return; // FIXME: Add FixItHint to make the method public. diag(Func->getLocation(), "deleted member function should be public"); diff --git a/clang-tidy/modernize/UseEqualsDeleteCheck.h b/clang-tidy/modernize/UseEqualsDeleteCheck.h index 1daa1a857..716f045d0 100644 --- a/clang-tidy/modernize/UseEqualsDeleteCheck.h +++ b/clang-tidy/modernize/UseEqualsDeleteCheck.h @@ -38,9 +38,14 @@ namespace modernize { class UseEqualsDeleteCheck : public ClangTidyCheck { public: UseEqualsDeleteCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + : ClangTidyCheck(Name, Context), + IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", 1) != 0) {} + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + const bool IgnoreMacros; }; } // namespace modernize diff --git a/docs/clang-tidy/checks/modernize-use-equals-delete.rst b/docs/clang-tidy/checks/modernize-use-equals-delete.rst index bfa54e4e8..f47826f9e 100644 --- a/docs/clang-tidy/checks/modernize-use-equals-delete.rst +++ b/docs/clang-tidy/checks/modernize-use-equals-delete.rst @@ -23,3 +23,8 @@ all other member functions implemented. A& operator=(const A&) = delete; }; + +.. option:: IgnoreMacros + + If this option is set to non-zero (default is `1`), the check will not warn + about functions declared inside macros. diff --git a/test/clang-tidy/modernize-use-equals-delete-macros.cpp b/test/clang-tidy/modernize-use-equals-delete-macros.cpp new file mode 100644 index 000000000..1b5a89cca --- /dev/null +++ b/test/clang-tidy/modernize-use-equals-delete-macros.cpp @@ -0,0 +1,10 @@ +// RUN: %check_clang_tidy %s modernize-use-equals-delete %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-equals-delete.IgnoreMacros, value: 0}]}" \ +// RUN: -- -std=c++11 + +#define MACRO(type) void operator=(type const &) +class C { +private: + MACRO(C); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function +}; diff --git a/test/clang-tidy/modernize-use-equals-delete.cpp b/test/clang-tidy/modernize-use-equals-delete.cpp index 6ab2ae493..988c8e5b2 100644 --- a/test/clang-tidy/modernize-use-equals-delete.cpp +++ b/test/clang-tidy/modernize-use-equals-delete.cpp @@ -185,3 +185,9 @@ struct ProtectedDeletedMacro2 { DISALLOW_COPY_AND_ASSIGN(ProtectedDeletedMacro2); }; +// This resulted in a warning by default. +#define MACRO(type) void operator=(type const &) +class C { +private: + MACRO(C); +}; From c9451cb321c4551d991b5e5aa7485c01fd2a44f0 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Sat, 13 Oct 2018 09:30:46 +0000 Subject: [PATCH 356/686] Revert "[doc] fix markup in clang-tidy bugprone-not-null-terminated-result" This reverts commit r344379. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344441 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../checks/bugprone-not-null-terminated-result.rst | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst b/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst index 9a0ce54c5..72f465819 100644 --- a/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst +++ b/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst @@ -55,11 +55,11 @@ Rewrite to a string handler function is not possible: Rewrite based on the destination array: -- If copy to the destination array cannot overflow then the new function should +- If copy to the destination array cannot *overflow then the new function should be the older copy function (ending with ``cpy``), because it is more efficient than the safe version. -- If copy to the destination array can overflow and +- If copy to the destination array can *overflow and ``AreSafeFunctionsAvailable`` is set to ``Yes``, ``y`` or non-zero and it is possible to obtain the capacity of the destination array then the new function could be the safe version (ending with ``cpy_s``). @@ -67,7 +67,7 @@ Rewrite based on the destination array: - If the new function is could be safe version and C++ files are analysed then the length of the destination array can be omitted. -- It is possible to overflow: +- *It is possible to overflow: - Unknown the capacity of the destination array. - If the given length is equal to the destination capacity. @@ -95,21 +95,17 @@ Memory handler functions :ref:`Transformation rules of 'memcpy()'` section. - ``memchr``: - - Usually there is a C-style cast, and it is needed to be removed, because the new function ``strchr``'s return type is correct. - Also the given length is not needed in the new function. - ``memmove``: - - If safe functions are available the new function is ``memmove_s``, it has four arguments: - - destination array, - length of the destination array, - source string, - length of the source string which is incremented by one. - - If safe functions are not available the given length is incremented by one. - ``memmove_s``: given length is incremented by one. From 350098cd6ba6ff0cc56245cd1fc136decacbb538 Mon Sep 17 00:00:00 2001 From: Jonas Toth Date: Sat, 13 Oct 2018 09:30:58 +0000 Subject: [PATCH 357/686] Revert "[clang-tidy] New checker for not null-terminated result caused by strlen(), size() or equal length" This reverts commit r344374. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344442 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/bugprone/BugproneTidyModule.cpp | 3 - clang-tidy/bugprone/CMakeLists.txt | 1 - .../bugprone/NotNullTerminatedResultCheck.cpp | 1024 ----------------- .../bugprone/NotNullTerminatedResultCheck.h | 67 -- docs/ReleaseNotes.rst | 9 - .../bugprone-not-null-terminated-result.rst | 132 --- docs/clang-tidy/checks/list.rst | 1 - ...rminated-result-in-initialization-strlen.c | 106 -- ...ull-terminated-result-memcpy-before-safe.c | 78 -- ...null-terminated-result-memcpy-safe-cxx.cpp | 160 --- ...null-terminated-result-memcpy-safe-other.c | 115 -- ...e-not-null-terminated-result-memcpy-safe.c | 137 --- ...gprone-not-null-terminated-result-strlen.c | 146 --- ...rone-not-null-terminated-result-wcslen.cpp | 131 --- ...ull-terminated-result-wmemcpy-safe-cxx.cpp | 136 --- 15 files changed, 2246 deletions(-) delete mode 100644 clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp delete mode 100644 clang-tidy/bugprone/NotNullTerminatedResultCheck.h delete mode 100644 docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst delete mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c delete mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c delete mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp delete mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c delete mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c delete mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-strlen.c delete mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp delete mode 100644 test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp diff --git a/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tidy/bugprone/BugproneTidyModule.cpp index 2d022ff37..09a252b30 100644 --- a/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -30,7 +30,6 @@ #include "MisplacedWideningCastCheck.h" #include "MoveForwardingReferenceCheck.h" #include "MultipleStatementMacroCheck.h" -#include "NotNullTerminatedResultCheck.h" #include "ParentVirtualCallCheck.h" #include "SizeofContainerCheck.h" #include "SizeofExpressionCheck.h" @@ -99,8 +98,6 @@ class BugproneModule : public ClangTidyModule { "bugprone-multiple-statement-macro"); CheckFactories.registerCheck( "bugprone-narrowing-conversions"); - CheckFactories.registerCheck( - "bugprone-not-null-terminated-result"); CheckFactories.registerCheck( "bugprone-parent-virtual-call"); CheckFactories.registerCheck( diff --git a/clang-tidy/bugprone/CMakeLists.txt b/clang-tidy/bugprone/CMakeLists.txt index 7ed0b07df..b20997cc4 100644 --- a/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tidy/bugprone/CMakeLists.txt @@ -21,7 +21,6 @@ add_clang_library(clangTidyBugproneModule MisplacedWideningCastCheck.cpp MoveForwardingReferenceCheck.cpp MultipleStatementMacroCheck.cpp - NotNullTerminatedResultCheck.cpp ParentVirtualCallCheck.cpp SizeofContainerCheck.cpp SizeofExpressionCheck.cpp diff --git a/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp b/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp deleted file mode 100644 index f4c3392d3..000000000 --- a/clang-tidy/bugprone/NotNullTerminatedResultCheck.cpp +++ /dev/null @@ -1,1024 +0,0 @@ -//===--- NotNullTerminatedResultCheck.cpp - clang-tidy ----------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "NotNullTerminatedResultCheck.h" -#include "clang/AST/ASTContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Lex/Lexer.h" -#include "clang/Lex/PPCallbacks.h" - -using namespace clang::ast_matchers; - -namespace clang { -namespace tidy { -namespace bugprone { - -static const char *const FuncExprName = "entire-called-function-expr"; -static const char *const CastExprName = "cast-expr"; -static const char *const UnknownDestName = "destination-length-is-unknown"; -static const char *const NotJustCharTyName = "unsigned-or-signed-char"; -static const char *const DestArrayTyName = "destination-is-array-type"; -static const char *const DestVarDeclName = "destination-variable-declaration"; -static const char *const SrcVarDeclName = "source-variable-declaration"; -static const char *const UnknownLengthName = "given-length-is-unknown"; -static const char *const WrongLengthExprName = "strlen-or-size"; -static const char *const DestMallocExprName = "destination-malloc-expr"; -static const char *const DestExprName = "destination-decl-ref-expr"; -static const char *const SrcExprName = "source-expression-or-string-literal"; -static const char *const LengthExprName = "given-length-expression"; - -enum class LengthHandleKind { Increase, Decrease }; - -namespace { -static Preprocessor *PP; -} // namespace - -// The capacity: VariableArrayType, ConstantArrayType, argument of a 'malloc()' -// family function or an argument of a custom memory allocation. -static const Expr *getDestCapacityExpr(const MatchFinder::MatchResult &Result); - -static int getDestCapacity(const MatchFinder::MatchResult &Result); - -// Length could be an IntegerLiteral or length of a StringLiteral. -static int getLength(const Expr *E, const MatchFinder::MatchResult &Result); - -static int getGivenLength(const MatchFinder::MatchResult &Result); - -static StringRef exprToStr(const Expr *E, - const MatchFinder::MatchResult &Result); - -static SourceLocation exprLocEnd(const Expr *E, - const MatchFinder::MatchResult &Result) { - return Lexer::getLocForEndOfToken(E->getEndLoc(), 0, *Result.SourceManager, - Result.Context->getLangOpts()); -} - -//===----------------------------------------------------------------------===// -// Rewrite decision helper functions. -//===----------------------------------------------------------------------===// - -// Increment by integer '1' can result in overflow if it is the maximal value. -// After that it will be extended to 'size_t' and its value will be wrong, -// therefore we have to inject '+ 1UL' instead. -static bool isInjectUL(const MatchFinder::MatchResult &Result) { - return getGivenLength(Result) == std::numeric_limits::max(); -} - -// If the capacity of the destination array is unknown it is denoted as unknown. -static bool isKnownDest(const MatchFinder::MatchResult &Result) { - return !Result.Nodes.getNodeAs(UnknownDestName); -} - -// True if the capacity of the destination array is based on the given length, -// therefore it looks like it cannot overflow (e.g. 'malloc(given_length + 1)' -// Note: If the capacity and the given length is equal then the new function -// is a simple 'cpy()' and because it returns true it prevents increasing the -// given length. -static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result); - -// If we write/read from the same array it should be already null-terminated. -static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result); - -// We catch integers as a given length so we have to see if the length of the -// source array is the same length so that the function call is wrong. -static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result); - -// Example: memcpy(dest, str.data(), str.length()); -static bool isStringDataAndLength(const MatchFinder::MatchResult &Result); - -static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result); - -static bool isLengthEqualToSrcLength(const MatchFinder::MatchResult &Result); - -//===----------------------------------------------------------------------===// -// Code injection functions. -//===----------------------------------------------------------------------===// - -static void lengthDecrease(const Expr *LengthExpr, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); -static void lengthIncrease(const Expr *LengthExpr, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); - -// Increase or decrease an integral expression by one. -static void lengthExprHandle(LengthHandleKind LengthHandle, - const Expr *LengthExpr, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); - -// Increase or decrease the passed integral argument by one. -static void lengthArgHandle(LengthHandleKind LengthHandle, int ArgPos, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); - -// If the destination array is the same length as the given length we have to -// increase the capacity by one to create space for the the null terminator. -static bool destCapacityFix(const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); - -static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); - -static void renameFunc(StringRef NewFuncName, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); - -static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); - -static void insertDestCapacityArg(bool IsOverflows, StringRef Name, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); - -static void insertNullTerminatorExpr(StringRef Name, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); - -NotNullTerminatedResultCheck::NotNullTerminatedResultCheck( - StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), - WantToUseSafeFunctions(Options.get("WantToUseSafeFunctions", 1)) {} - -void NotNullTerminatedResultCheck::storeOptions( - ClangTidyOptions::OptionMap &Opts) { - Options.store(Opts, "WantToUseSafeFunctions", WantToUseSafeFunctions); -} - -void NotNullTerminatedResultCheck::registerPPCallbacks( - CompilerInstance &Compiler) { - PP = &Compiler.getPreprocessor(); -} - -namespace { -AST_MATCHER_P(Expr, hasDefinition, ast_matchers::internal::Matcher, - InnerMatcher) { - const Expr *SimpleNode = &Node; - SimpleNode = SimpleNode->IgnoreParenImpCasts(); - - if (InnerMatcher.matches(*SimpleNode, Finder, Builder)) - return true; - - auto DREHasInit = ignoringImpCasts( - declRefExpr(to(varDecl(hasInitializer(ignoringImpCasts(InnerMatcher)))))); - - if (DREHasInit.matches(*SimpleNode, Finder, Builder)) - return true; - - // - Example: int getLength(const char *str) { return strlen(str); } - auto CallExprReturnInit = ignoringImpCasts( - callExpr(callee(functionDecl(hasBody(has(returnStmt(hasReturnValue( - ignoringImpCasts(anyOf(DREHasInit, InnerMatcher)))))))))); - - if (CallExprReturnInit.matches(*SimpleNode, Finder, Builder)) - return true; - - // - Example: int length = getLength(src); - auto DREHasReturnInit = ignoringImpCasts( - declRefExpr(to(varDecl(hasInitializer(CallExprReturnInit))))); - - if (DREHasReturnInit.matches(*SimpleNode, Finder, Builder)) - return true; - - const char *const VarDeclName = "variable-declaration"; - auto DREHasDefinition = ignoringImpCasts(declRefExpr( - allOf(to(varDecl().bind(VarDeclName)), - hasAncestor(compoundStmt(hasDescendant(binaryOperator( - hasLHS(declRefExpr(to(varDecl(equalsBoundNode(VarDeclName))))), - hasRHS(ignoringImpCasts(InnerMatcher))))))))); - - if (DREHasDefinition.matches(*SimpleNode, Finder, Builder)) - return true; - - return false; -} -} // namespace - -void NotNullTerminatedResultCheck::registerMatchers(MatchFinder *Finder) { - auto IncOp = - binaryOperator(hasOperatorName("+"), - hasEitherOperand(ignoringParenImpCasts(integerLiteral()))); - - auto DecOp = - binaryOperator(hasOperatorName("-"), - hasEitherOperand(ignoringParenImpCasts(integerLiteral()))); - - auto HasIncOp = anyOf(ignoringImpCasts(IncOp), hasDescendant(IncOp)); - auto HasDecOp = anyOf(ignoringImpCasts(DecOp), hasDescendant(DecOp)); - - auto StringTy = type(hasUnqualifiedDesugaredType(recordType( - hasDeclaration(cxxRecordDecl(hasName("::std::basic_string")))))); - - auto AnyOfStringTy = - anyOf(hasType(StringTy), hasType(qualType(pointsTo(StringTy)))); - - auto CharTy = - anyOf(asString("char"), asString("wchar_t"), - allOf(anyOf(asString("unsigned char"), asString("signed char")), - type().bind(NotJustCharTyName))); - - auto CharTyArray = hasType(qualType(hasCanonicalType( - arrayType(hasElementType(CharTy)).bind(DestArrayTyName)))); - - auto CharTyPointer = - hasType(qualType(hasCanonicalType(pointerType(pointee(CharTy))))); - - auto AnyOfCharTy = anyOf(CharTyArray, CharTyPointer); - - //===--------------------------------------------------------------------===// - // The following six cases match problematic length expressions. - //===--------------------------------------------------------------------===// - - // - Example: char src[] = "foo"; strlen(src); - auto Strlen = - callExpr(callee(functionDecl(hasAnyName("::strlen", "::wcslen")))) - .bind(WrongLengthExprName); - - // - Example: std::string str = "foo"; str.size(); - auto SizeOrLength = - cxxMemberCallExpr( - allOf(on(expr(AnyOfStringTy)), - has(memberExpr(member(hasAnyName("size", "length")))))) - .bind(WrongLengthExprName); - - // - Example: char src[] = "foo"; sizeof(src); - auto SizeOfCharExpr = unaryExprOrTypeTraitExpr(has(expr(hasType(qualType( - hasCanonicalType(anyOf(arrayType(hasElementType(isAnyCharacter())), - pointerType(pointee(isAnyCharacter()))))))))); - - auto WrongLength = - anyOf(ignoringImpCasts(Strlen), ignoringImpCasts(SizeOrLength), - hasDescendant(Strlen), hasDescendant(SizeOrLength)); - - // - Example: length = strlen(src); - auto DREWithoutInc = - ignoringImpCasts(declRefExpr(to(varDecl(hasInitializer(WrongLength))))); - - auto AnyOfCallOrDREWithoutInc = anyOf(DREWithoutInc, WrongLength); - - // - Example: int getLength(const char *str) { return strlen(str); } - auto CallExprReturnWithoutInc = ignoringImpCasts(callExpr(callee(functionDecl( - hasBody(has(returnStmt(hasReturnValue(AnyOfCallOrDREWithoutInc)))))))); - - // - Example: int length = getLength(src); - auto DREHasReturnWithoutInc = ignoringImpCasts( - declRefExpr(to(varDecl(hasInitializer(CallExprReturnWithoutInc))))); - - auto AnyOfWrongLengthInit = - anyOf(AnyOfCallOrDREWithoutInc, CallExprReturnWithoutInc, - DREHasReturnWithoutInc); - - enum class StrlenKind { WithInc, WithoutInc }; - - const auto AnyOfLengthExpr = [=](StrlenKind LengthKind) { - return ignoringImpCasts(allOf( - unless(hasDefinition(SizeOfCharExpr)), - anyOf(allOf((LengthKind == StrlenKind::WithoutInc) - ? ignoringImpCasts(unless(hasDefinition(HasIncOp))) - : ignoringImpCasts( - allOf(hasDefinition(HasIncOp), - unless(hasDefinition(HasDecOp)))), - AnyOfWrongLengthInit), - ignoringImpCasts(integerLiteral().bind(WrongLengthExprName))), - expr().bind(LengthExprName))); - }; - - auto LengthWithoutInc = AnyOfLengthExpr(StrlenKind::WithoutInc); - auto LengthWithInc = AnyOfLengthExpr(StrlenKind::WithInc); - - //===--------------------------------------------------------------------===// - // The following five cases match the 'destination' array length's - // expression which is used in memcpy() and memmove() matchers. - //===--------------------------------------------------------------------===// - - auto SizeExpr = anyOf(SizeOfCharExpr, integerLiteral(equals(1))); - - auto MallocLengthExpr = allOf( - anyOf(argumentCountIs(1), argumentCountIs(2)), - hasAnyArgument(allOf(unless(SizeExpr), - expr(ignoringImpCasts(anyOf(HasIncOp, anything()))) - .bind(DestMallocExprName)))); - - // - Example: (char *)malloc(length); - auto DestMalloc = anyOf(castExpr(has(callExpr(MallocLengthExpr))), - callExpr(MallocLengthExpr)); - - // - Example: new char[length]; - auto DestCXXNewExpr = ignoringImpCasts( - cxxNewExpr(hasArraySize(expr().bind(DestMallocExprName)))); - - auto AnyOfDestInit = anyOf(DestMalloc, DestCXXNewExpr); - - // - Example: char dest[13]; or char dest[length]; - auto DestArrayTyDecl = declRefExpr( - to(anyOf(varDecl(CharTyArray).bind(DestVarDeclName), - varDecl(hasInitializer(AnyOfDestInit)).bind(DestVarDeclName)))); - - // - Example: foo[bar[baz]].qux; (or just ParmVarDecl) - auto DestUnknownDecl = - declRefExpr(allOf(to(varDecl(AnyOfCharTy).bind(DestVarDeclName)), - expr().bind(UnknownDestName))); - - auto AnyOfDestDecl = - allOf(anyOf(hasDefinition(anyOf(AnyOfDestInit, DestArrayTyDecl)), - DestUnknownDecl, anything()), - expr().bind(DestExprName)); - - auto SrcDecl = declRefExpr( - allOf(to(decl().bind(SrcVarDeclName)), - anyOf(hasAncestor(cxxMemberCallExpr().bind(SrcExprName)), - expr().bind(SrcExprName)))); - - auto SrcDeclMayInBinOp = - anyOf(ignoringImpCasts(SrcDecl), hasDescendant(SrcDecl)); - - auto AnyOfSrcDecl = anyOf(ignoringImpCasts(stringLiteral().bind(SrcExprName)), - SrcDeclMayInBinOp); - - auto NullTerminatorExpr = binaryOperator( - hasLHS(hasDescendant( - declRefExpr(to(varDecl(equalsBoundNode(DestVarDeclName)))))), - hasRHS(ignoringImpCasts( - anyOf(characterLiteral(equals(0U)), integerLiteral(equals(0)))))); - - //===--------------------------------------------------------------------===// - // The following nineteen cases match problematic function calls. - //===--------------------------------------------------------------------===// - - const auto WithoutSrc = [=](StringRef Name, int LengthPos, - StrlenKind LengthKind) { - return allOf( - callee(functionDecl(hasName(Name))), - hasArgument( - 0, allOf(AnyOfDestDecl, unless(hasAncestor(compoundStmt( - hasDescendant(NullTerminatorExpr)))))), - hasArgument(LengthPos, (LengthKind == StrlenKind::WithoutInc) - ? LengthWithoutInc - : LengthWithInc)); - }; - - const auto WithSrc = [=](StringRef Name, int SourcePos, int LengthPos, - StrlenKind LengthKind) { - return allOf(callee(functionDecl(hasName(Name))), - hasArgument(SourcePos ? 0 : 1, - allOf(AnyOfDestDecl, - unless(hasAncestor(compoundStmt( - hasDescendant(NullTerminatorExpr)))))), - hasArgument(SourcePos, AnyOfSrcDecl), - hasArgument(LengthPos, (LengthKind == StrlenKind::WithoutInc) - ? LengthWithoutInc - : LengthWithInc)); - }; - - auto Memcpy = WithSrc("::memcpy", 1, 2, StrlenKind::WithoutInc); - auto Wmemcpy = WithSrc("::wmemcpy", 1, 2, StrlenKind::WithoutInc); - auto Memcpy_s = WithSrc("::memcpy_s", 2, 3, StrlenKind::WithoutInc); - auto Wmemcpy_s = WithSrc("::wmemcpy_s", 2, 3, StrlenKind::WithoutInc); - auto Memchr = WithSrc("::memchr", 0, 2, StrlenKind::WithoutInc); - auto Wmemchr = WithSrc("::wmemchr", 0, 2, StrlenKind::WithoutInc); - auto Memmove = WithSrc("::memmove", 1, 2, StrlenKind::WithoutInc); - auto Wmemmove = WithSrc("::wmemmove", 1, 2, StrlenKind::WithoutInc); - auto Memmove_s = WithSrc("::memmove_s", 2, 3, StrlenKind::WithoutInc); - auto Wmemmove_s = WithSrc("::wmemmove_s", 2, 3, StrlenKind::WithoutInc); - auto Memset = WithoutSrc("::memset", 2, StrlenKind::WithInc); - auto Wmemset = WithoutSrc("::wmemset", 2, StrlenKind::WithInc); - auto Strerror_s = WithoutSrc("::strerror_s", 1, StrlenKind::WithoutInc); - auto StrncmpLHS = WithSrc("::strncmp", 1, 2, StrlenKind::WithInc); - auto WcsncmpLHS = WithSrc("::wcsncmp", 1, 2, StrlenKind::WithInc); - auto StrncmpRHS = WithSrc("::strncmp", 0, 2, StrlenKind::WithInc); - auto WcsncmpRHS = WithSrc("::wcsncmp", 0, 2, StrlenKind::WithInc); - auto Strxfrm = WithSrc("::strxfrm", 1, 2, StrlenKind::WithoutInc); - auto Wcsxfrm = WithSrc("::wcsxfrm", 1, 2, StrlenKind::WithoutInc); - - auto AnyOfMatchers = - anyOf(Memcpy, Wmemcpy, Memcpy_s, Wmemcpy_s, Memchr, Wmemchr, Memmove, - Wmemmove, Memmove_s, Wmemmove_s, Memset, Wmemset, Strerror_s, - StrncmpLHS, WcsncmpLHS, StrncmpRHS, WcsncmpRHS, Strxfrm, Wcsxfrm); - - Finder->addMatcher(callExpr(AnyOfMatchers).bind(FuncExprName), this); - - Finder->addMatcher( - castExpr(has(callExpr(anyOf(Memchr, Wmemchr)).bind(FuncExprName))) - .bind(CastExprName), - this); -} - -void NotNullTerminatedResultCheck::check( - const MatchFinder::MatchResult &Result) { - const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); - if (FuncExpr->getBeginLoc().isMacroID()) - return; - - if (WantToUseSafeFunctions && PP->isMacroDefined("__STDC_LIB_EXT1__")) { - Optional AreSafeFunctionsWanted; - - Preprocessor::macro_iterator It = PP->macro_begin(); - while (It != PP->macro_end() && !AreSafeFunctionsWanted.hasValue()) { - if (It->first->getName() == "__STDC_WANT_LIB_EXT1__") { - const auto *MI = PP->getMacroInfo(It->first); - const auto &T = MI->tokens().back(); - StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength()); - llvm::APInt IntValue; - ValueStr.getAsInteger(10, IntValue); - AreSafeFunctionsWanted = IntValue.getZExtValue(); - } - - ++It; - } - - if (AreSafeFunctionsWanted.hasValue()) - UseSafeFunctions = AreSafeFunctionsWanted.getValue(); - } - - StringRef Name = FuncExpr->getDirectCallee()->getName(); - if (Name.startswith("mem") || Name.startswith("wmem")) - memoryHandlerFunctionFix(Name, Result); - else if (Name == "strerror_s") - strerror_sFix(Result); - else if (Name.endswith("ncmp")) - ncmpFix(Name, Result); - else if (Name.endswith("xfrm")) - xfrmFix(Name, Result); -} - -void NotNullTerminatedResultCheck::memoryHandlerFunctionFix( - StringRef Name, const MatchFinder::MatchResult &Result) { - if (isCorrectGivenLength(Result)) - return; - - if (Name.endswith("chr")) { - memchrFix(Name, Result); - return; - } - - if ((Name.contains("cpy") || Name.contains("move")) && - isDestAndSrcEquals(Result)) - return; - - auto Diag = - diag(Result.Nodes.getNodeAs(FuncExprName)->getBeginLoc(), - "the result from calling '%0' is not null-terminated") - << Name; - - if (Name.endswith("cpy")) - memcpyFix(Name, Result, Diag); - else if (Name.endswith("cpy_s")) - memcpy_sFix(Name, Result, Diag); - else if (Name.endswith("move")) - memmoveFix(Name, Result, Diag); - else if (Name.endswith("move_s")) { - destCapacityFix(Result, Diag); - lengthArgHandle(LengthHandleKind::Increase, 3, Result, Diag); - } else if (Name.endswith("set")) { - lengthArgHandle(LengthHandleKind::Decrease, 2, Result, Diag); - } -} - -void NotNullTerminatedResultCheck::memcpyFix( - StringRef Name, const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - bool IsOverflows = destCapacityFix(Result, Diag); - - // If it cannot be rewritten to string handler function. - if (Result.Nodes.getNodeAs(NotJustCharTyName)) { - if (UseSafeFunctions && isKnownDest(Result)) { - renameFunc((Name[0] != 'w') ? "memcpy_s" : "wmemcpy_s", Result, Diag); - insertDestCapacityArg(IsOverflows, Name, Result, Diag); - } - - lengthArgHandle(LengthHandleKind::Increase, 2, Result, Diag); - return; - } - - bool IsCopy = - isLengthEqualToSrcLength(Result) || isDestBasedOnGivenLength(Result); - - bool IsSafe = UseSafeFunctions && IsOverflows && isKnownDest(Result) && - !isDestBasedOnGivenLength(Result); - - bool IsDestLengthNotRequired = - IsSafe && getLangOpts().CPlusPlus && - Result.Nodes.getNodeAs(DestArrayTyName); - - renameMemcpy(Name, IsCopy, IsSafe, Result, Diag); - - if (IsSafe && !IsDestLengthNotRequired) - insertDestCapacityArg(IsOverflows, Name, Result, Diag); - - if (IsCopy) - removeArg(2, Result, Diag); - - if (!IsCopy && !IsSafe) - insertNullTerminatorExpr(Name, Result, Diag); -} - -void NotNullTerminatedResultCheck::memcpy_sFix( - StringRef Name, const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - bool IsOverflows = destCapacityFix(Result, Diag); - - if (Result.Nodes.getNodeAs(NotJustCharTyName)) { - lengthArgHandle(LengthHandleKind::Increase, 3, Result, Diag); - return; - } - - bool RemoveDestLength = getLangOpts().CPlusPlus && - Result.Nodes.getNodeAs(DestArrayTyName); - bool IsCopy = isLengthEqualToSrcLength(Result); - bool IsSafe = IsOverflows; - - renameMemcpy(Name, IsCopy, IsSafe, Result, Diag); - - if (!IsSafe || (IsSafe && RemoveDestLength)) - removeArg(1, Result, Diag); - else if (IsOverflows && isKnownDest(Result)) - lengthArgHandle(LengthHandleKind::Increase, 1, Result, Diag); - - if (IsCopy) - removeArg(3, Result, Diag); - - if (!IsCopy && !IsSafe) - insertNullTerminatorExpr(Name, Result, Diag); -} - -void NotNullTerminatedResultCheck::memchrFix( - StringRef Name, const MatchFinder::MatchResult &Result) { - const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); - if (const auto GivenCL = - dyn_cast_or_null(FuncExpr->getArg(1))) - if (GivenCL->getValue() != 0) - return; - - auto Diag = diag(FuncExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(), - "the length is too short to include the null terminator"); - - if (const auto *CastExpr = Result.Nodes.getNodeAs(CastExprName)) { - const auto CastRemoveFix = FixItHint::CreateRemoval(SourceRange( - CastExpr->getBeginLoc(), FuncExpr->getBeginLoc().getLocWithOffset(-1))); - Diag << CastRemoveFix; - } - StringRef NewFuncName = (Name[0] != 'w') ? "strchr" : "wcschr"; - renameFunc(NewFuncName, Result, Diag); - removeArg(2, Result, Diag); -} - -void NotNullTerminatedResultCheck::memmoveFix( - StringRef Name, const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - bool IsOverflows = destCapacityFix(Result, Diag); - - if (UseSafeFunctions && isKnownDest(Result)) { - renameFunc((Name[0] != 'w') ? "memmove_s" : "wmemmove_s", Result, Diag); - insertDestCapacityArg(IsOverflows, Name, Result, Diag); - } - - lengthArgHandle(LengthHandleKind::Increase, 2, Result, Diag); -} - -void NotNullTerminatedResultCheck::strerror_sFix( - const MatchFinder::MatchResult &Result) { - StringRef Name = "strerror_s"; - auto Diag = - diag(Result.Nodes.getNodeAs(FuncExprName)->getBeginLoc(), - "the result from calling '%0' is not null-terminated and " - "missing the last character of the error message") - << Name; - - destCapacityFix(Result, Diag); - lengthArgHandle(LengthHandleKind::Increase, 1, Result, Diag); -} - -void NotNullTerminatedResultCheck::ncmpFix( - StringRef Name, const MatchFinder::MatchResult &Result) { - const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); - const Expr *FirstArgExpr = FuncExpr->getArg(0)->IgnoreImpCasts(); - const Expr *SecondArgExpr = FuncExpr->getArg(1)->IgnoreImpCasts(); - bool IsLengthTooLong = false; - - if (const auto *LengthExpr = - Result.Nodes.getNodeAs(WrongLengthExprName)) { - const Expr *LengthExprArg = LengthExpr->getArg(0); - StringRef FirstExprStr = exprToStr(FirstArgExpr, Result).trim(' '); - StringRef SecondExprStr = exprToStr(SecondArgExpr, Result).trim(' '); - StringRef LengthArgStr = exprToStr(LengthExprArg, Result).trim(' '); - IsLengthTooLong = - LengthArgStr == FirstExprStr || LengthArgStr == SecondExprStr; - } else { - int SrcLength = - getLength(Result.Nodes.getNodeAs(SrcExprName), Result); - int GivenLength = getGivenLength(Result); - IsLengthTooLong = GivenLength - 1 == SrcLength; - } - - if (!IsLengthTooLong && !isStringDataAndLength(Result)) - return; - - auto Diag = diag(FuncExpr->getArg(2)->IgnoreParenCasts()->getBeginLoc(), - "comparison length is too long and might lead to a " - "buffer overflow"); - - lengthArgHandle(LengthHandleKind::Decrease, 2, Result, Diag); -} - -void NotNullTerminatedResultCheck::xfrmFix( - StringRef Name, const MatchFinder::MatchResult &Result) { - if (!isDestCapacityOverflows(Result)) - return; - - auto Diag = - diag(Result.Nodes.getNodeAs(FuncExprName)->getBeginLoc(), - "the result from calling '%0' is not null-terminated") - << Name; - - destCapacityFix(Result, Diag); - lengthArgHandle(LengthHandleKind::Increase, 2, Result, Diag); -} - -//===---------------------------------------------------------------------===// -// All the helper functions. -//===---------------------------------------------------------------------===// - -static StringRef exprToStr(const Expr *E, - const MatchFinder::MatchResult &Result) { - if (!E) - return ""; - - return Lexer::getSourceText( - CharSourceRange::getTokenRange(E->getSourceRange()), - *Result.SourceManager, Result.Context->getLangOpts(), 0); -} - -static bool isDestAndSrcEquals(const MatchFinder::MatchResult &Result) { - if (const auto *DestVD = Result.Nodes.getNodeAs(DestVarDeclName)) - if (const auto *SrcVD = Result.Nodes.getNodeAs(SrcVarDeclName)) - return DestVD->getCanonicalDecl() == SrcVD->getCanonicalDecl(); - - return false; -} - -static bool isCorrectGivenLength(const MatchFinder::MatchResult &Result) { - if (Result.Nodes.getNodeAs(WrongLengthExprName)) - return !isLengthEqualToSrcLength(Result); - - return false; -} - -static const Expr *getDestCapacityExpr(const MatchFinder::MatchResult &Result) { - if (const auto *DestMalloc = Result.Nodes.getNodeAs(DestMallocExprName)) - return DestMalloc; - - if (const auto *DestTy = Result.Nodes.getNodeAs(DestArrayTyName)) - if (const auto *DestVAT = dyn_cast_or_null(DestTy)) - return DestVAT->getSizeExpr(); - - if (const auto *DestVD = Result.Nodes.getNodeAs(DestVarDeclName)) - if (const auto DestTL = DestVD->getTypeSourceInfo()->getTypeLoc()) - if (const auto DestCTL = DestTL.getAs()) - return DestCTL.getSizeExpr(); - - return nullptr; -} - -static int getLength(const Expr *E, const MatchFinder::MatchResult &Result) { - llvm::APSInt Length; - - if (const auto *LengthDRE = dyn_cast_or_null(E)) - if (const auto *LengthVD = dyn_cast_or_null(LengthDRE->getDecl())) - if (!isa(LengthVD)) - if (const Expr *LengthInit = LengthVD->getInit()) - if (LengthInit->EvaluateAsInt(Length, *Result.Context)) - return Length.getZExtValue(); - - if (const auto *LengthIL = dyn_cast_or_null(E)) - return LengthIL->getValue().getZExtValue(); - - if (const auto *StrDRE = dyn_cast_or_null(E)) - if (const auto *StrVD = dyn_cast_or_null(StrDRE->getDecl())) - if (const Expr *StrInit = StrVD->getInit()) - if (const auto *StrSL = - dyn_cast_or_null(StrInit->IgnoreImpCasts())) - return StrSL->getLength(); - - if (const auto *SrcSL = dyn_cast_or_null(E)) - return SrcSL->getLength(); - - return 0; -} - -static int getDestCapacity(const MatchFinder::MatchResult &Result) { - if (const auto *DestCapacityExpr = getDestCapacityExpr(Result)) - return getLength(DestCapacityExpr, Result); - - return 0; -} - -static int getGivenLength(const MatchFinder::MatchResult &Result) { - const auto *LengthExpr = Result.Nodes.getNodeAs(LengthExprName); - if (int Length = getLength(LengthExpr, Result)) - return Length; - - if (const auto *StrlenExpr = dyn_cast_or_null(LengthExpr)) - if (StrlenExpr->getNumArgs() > 0) - if (const Expr *StrlenArg = StrlenExpr->getArg(0)->IgnoreImpCasts()) - if (int StrlenArgLength = getLength(StrlenArg, Result)) - return StrlenArgLength; - - return 0; -} - -static bool isStringDataAndLength(const MatchFinder::MatchResult &Result) { - StringRef DestStr = - exprToStr(Result.Nodes.getNodeAs(DestExprName), Result); - StringRef SrcStr = - exprToStr(Result.Nodes.getNodeAs(SrcExprName), Result); - StringRef GivenLengthStr = - exprToStr(Result.Nodes.getNodeAs(LengthExprName), Result); - - bool ProblematicLength = - GivenLengthStr.contains(".size") || GivenLengthStr.contains(".length"); - - return ProblematicLength && - (SrcStr.contains(".data") || DestStr.contains(".data")); -} - -static bool isLengthEqualToSrcLength(const MatchFinder::MatchResult &Result) { - if (isStringDataAndLength(Result)) - return true; - - int GivenLength = getGivenLength(Result); - - // It is the length without the null terminator. - int SrcLength = getLength(Result.Nodes.getNodeAs(SrcExprName), Result); - - if (GivenLength != 0 && GivenLength == SrcLength) - return true; - - // If 'strlen()' check the VarDecl of the argument is equal to source VarDecl. - if (const auto *StrlenExpr = Result.Nodes.getNodeAs(LengthExprName)) - if (StrlenExpr->getNumArgs() > 0) - if (const auto *StrlenDRE = dyn_cast_or_null( - StrlenExpr->getArg(0)->IgnoreImpCasts())) - if (const auto *SrcVD = Result.Nodes.getNodeAs(SrcVarDeclName)) - return dyn_cast_or_null(StrlenDRE->getDecl()) == SrcVD; - - return false; -} - -static bool isDestCapacityOverflows(const MatchFinder::MatchResult &Result) { - if (!isKnownDest(Result)) - return true; - - const auto *DestCapacityExpr = getDestCapacityExpr(Result); - const auto *LengthExpr = Result.Nodes.getNodeAs(LengthExprName); - int DestCapacity = getLength(DestCapacityExpr, Result); - int GivenLength = getGivenLength(Result); - - if (GivenLength != 0 && DestCapacity != 0) - return isLengthEqualToSrcLength(Result) && DestCapacity == GivenLength; - - StringRef DestCapacityExprStr = exprToStr(DestCapacityExpr, Result); - StringRef LengthExprStr = exprToStr(LengthExpr, Result); - - // Assume that it cannot overflow if the expression of the destination - // capacity contains '+ 1'. - if (DestCapacityExprStr.contains("+1") || DestCapacityExprStr.contains("+ 1")) - return false; - - if (DestCapacityExprStr != "" && DestCapacityExprStr == LengthExprStr) - return true; - - return true; -} - -static bool isDestBasedOnGivenLength(const MatchFinder::MatchResult &Result) { - StringRef DestCapacityExprStr = - exprToStr(getDestCapacityExpr(Result), Result).trim(' '); - StringRef LengthExprStr = - exprToStr(Result.Nodes.getNodeAs(LengthExprName), Result).trim(' '); - - return DestCapacityExprStr != "" && LengthExprStr != "" && - DestCapacityExprStr.contains(LengthExprStr); -} - -static void lengthDecrease(const Expr *LengthExpr, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - // This is the following structure: ((strlen(src) * 2) + 1) - // InnerOpExpr: ~~~~~~~~~~~~^~~ - // OuterOpExpr: ~~~~~~~~~~~~~~~~~~^~~ - if (const auto *OuterOpExpr = - dyn_cast_or_null(LengthExpr->IgnoreParenCasts())) { - const Expr *LHSExpr = OuterOpExpr->getLHS(); - const Expr *RHSExpr = OuterOpExpr->getRHS(); - const auto *InnerOpExpr = - isa(RHSExpr->IgnoreCasts()) ? LHSExpr : RHSExpr; - - // This is the following structure: ((strlen(src) * 2) + 1) - // LHSRemoveRange: ~~ - // RHSRemoveRange: ~~~~~~ - SourceRange LHSRemoveRange(LengthExpr->getBeginLoc(), - InnerOpExpr->getBeginLoc().getLocWithOffset(-1)); - SourceRange RHSRemoveRange(exprLocEnd(InnerOpExpr, Result), - LengthExpr->getEndLoc()); - const auto LHSRemoveFix = FixItHint::CreateRemoval(LHSRemoveRange); - const auto RHSRemoveFix = FixItHint::CreateRemoval(RHSRemoveRange); - - if (LengthExpr->getBeginLoc() == InnerOpExpr->getBeginLoc()) - Diag << RHSRemoveFix; - else if (LengthExpr->getEndLoc() == InnerOpExpr->getEndLoc()) - Diag << LHSRemoveFix; - else - Diag << LHSRemoveFix << RHSRemoveFix; - } else { - const auto InsertDecreaseFix = - FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), " - 1"); - Diag << InsertDecreaseFix; - } -} - -static void lengthIncrease(const Expr *LengthExpr, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - bool NeedInnerParen = dyn_cast_or_null(LengthExpr) && - cast(LengthExpr)->getOpcode() != BO_Add; - - if (NeedInnerParen) { - const auto InsertFirstParenFix = - FixItHint::CreateInsertion(LengthExpr->getBeginLoc(), "("); - const auto InsertPlusOneAndSecondParenFix = - FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), - !isInjectUL(Result) ? ") + 1" : ") + 1UL"); - Diag << InsertFirstParenFix << InsertPlusOneAndSecondParenFix; - } else { - const auto InsertPlusOneFix = - FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), - !isInjectUL(Result) ? " + 1" : " + 1UL"); - Diag << InsertPlusOneFix; - } -} - -static void lengthExprHandle(LengthHandleKind LengthHandle, - const Expr *LengthExpr, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - if (!LengthExpr) - return; - - bool IsMacroDefinition = false; - StringRef LengthExprStr = exprToStr(LengthExpr, Result); - - Preprocessor::macro_iterator It = PP->macro_begin(); - while (It != PP->macro_end() && !IsMacroDefinition) { - if (It->first->getName() == LengthExprStr) - IsMacroDefinition = true; - - ++It; - } - - if (!IsMacroDefinition) { - if (const auto *LengthIL = dyn_cast_or_null(LengthExpr)) { - const size_t NewLength = LengthIL->getValue().getZExtValue() + - (LengthHandle == LengthHandleKind::Increase - ? (isInjectUL(Result) ? 1UL : 1) - : -1); - const auto NewLengthFix = FixItHint::CreateReplacement( - LengthIL->getSourceRange(), - (Twine(NewLength) + (isInjectUL(Result) ? "UL" : "")).str()); - Diag << NewLengthFix; - return; - } - - if (LengthHandle == LengthHandleKind::Increase) - lengthIncrease(LengthExpr, Result, Diag); - else - lengthDecrease(LengthExpr, Result, Diag); - } else { - if (LengthHandle == LengthHandleKind::Increase) { - const auto InsertPlusOneFix = - FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), - !isInjectUL(Result) ? " + 1" : " + 1UL"); - Diag << InsertPlusOneFix; - } else { - const auto InsertMinusOneFix = - FixItHint::CreateInsertion(exprLocEnd(LengthExpr, Result), " - 1"); - Diag << InsertMinusOneFix; - } - } -} - -static void lengthArgHandle(LengthHandleKind LengthHandle, int ArgPos, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); - const Expr *LengthExpr = FuncExpr->getArg(ArgPos)->IgnoreImpCasts(); - lengthExprHandle(LengthHandle, LengthExpr, Result, Diag); -} - -static bool destCapacityFix(const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - bool IsOverflows = isDestCapacityOverflows(Result); - if (IsOverflows) - lengthExprHandle(LengthHandleKind::Increase, getDestCapacityExpr(Result), - Result, Diag); - - return IsOverflows; -} - -static void removeArg(int ArgPos, const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - // This is the following structure: (src, '\0', strlen(src)) - // ArgToRemove: ~~~~~~~~~~~ - // LHSArg: ~~~~ - // RemoveArgFix: ~~~~~~~~~~~~~ - const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); - const Expr *ArgToRemove = FuncExpr->getArg(ArgPos); - const Expr *LHSArg = FuncExpr->getArg(ArgPos - 1); - const auto RemoveArgFix = FixItHint::CreateRemoval( - SourceRange(exprLocEnd(LHSArg, Result), - exprLocEnd(ArgToRemove, Result).getLocWithOffset(-1))); - Diag << RemoveArgFix; -} - -static void renameFunc(StringRef NewFuncName, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); - int FuncNameLength = - FuncExpr->getDirectCallee()->getIdentifier()->getLength(); - SourceRange FuncNameRange( - FuncExpr->getBeginLoc(), - FuncExpr->getBeginLoc().getLocWithOffset(FuncNameLength - 1)); - - const auto FuncNameFix = - FixItHint::CreateReplacement(FuncNameRange, NewFuncName); - Diag << FuncNameFix; -} - -static void renameMemcpy(StringRef Name, bool IsCopy, bool IsSafe, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - SmallString<10> NewFuncName; - NewFuncName = (Name[0] != 'w') ? "str" : "wcs"; - NewFuncName += IsCopy ? "cpy" : "ncpy"; - NewFuncName += IsSafe ? "_s" : ""; - renameFunc(NewFuncName, Result, Diag); -} - -static void insertDestCapacityArg(bool IsOverflows, StringRef Name, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); - SmallString<64> NewSecondArg; - - if (int DestLength = getDestCapacity(Result)) { - NewSecondArg = Twine(IsOverflows ? DestLength + 1 : DestLength).str(); - } else { - NewSecondArg = - (Twine(exprToStr(getDestCapacityExpr(Result), Result)) + - (IsOverflows ? (!isInjectUL(Result) ? " + 1" : " + 1UL") : "")) - .str(); - } - - NewSecondArg += ", "; - const auto InsertNewArgFix = FixItHint::CreateInsertion( - FuncExpr->getArg(1)->getBeginLoc(), NewSecondArg); - Diag << InsertNewArgFix; -} - -static void insertNullTerminatorExpr(StringRef Name, - const MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag) { - const auto *FuncExpr = Result.Nodes.getNodeAs(FuncExprName); - int FuncLocStartColumn = - Result.SourceManager->getPresumedColumnNumber(FuncExpr->getBeginLoc()); - SourceRange SpaceRange( - FuncExpr->getBeginLoc().getLocWithOffset(-FuncLocStartColumn + 1), - FuncExpr->getBeginLoc()); - StringRef SpaceBeforeStmtStr = Lexer::getSourceText( - CharSourceRange::getCharRange(SpaceRange), *Result.SourceManager, - Result.Context->getLangOpts(), 0); - - SmallString<128> NewAddNullTermExprStr; - NewAddNullTermExprStr = - (Twine('\n') + SpaceBeforeStmtStr + - exprToStr(Result.Nodes.getNodeAs(DestExprName), Result) + "[" + - exprToStr(Result.Nodes.getNodeAs(LengthExprName), Result) + - "] = " + ((Name[0] != 'w') ? "\'\\0\';" : "L\'\\0\';")) - .str(); - - const auto AddNullTerminatorExprFix = FixItHint::CreateInsertion( - exprLocEnd(FuncExpr, Result).getLocWithOffset(1), NewAddNullTermExprStr); - Diag << AddNullTerminatorExprFix; -} - -} // namespace bugprone -} // namespace tidy -} // namespace clang diff --git a/clang-tidy/bugprone/NotNullTerminatedResultCheck.h b/clang-tidy/bugprone/NotNullTerminatedResultCheck.h deleted file mode 100644 index 762203e19..000000000 --- a/clang-tidy/bugprone/NotNullTerminatedResultCheck.h +++ /dev/null @@ -1,67 +0,0 @@ -//===--- NotNullTerminatedResultCheck.h - clang-tidy ------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NOT_NULL_TERMINATED_RESULT_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NOT_NULL_TERMINATED_RESULT_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace bugprone { - -/// Finds function calls where it is possible to cause a not null-terminated -/// result. Usually the proper length of a string is ``strlen(src) + 1`` or -/// equal length of this expression, because the null terminator needs an extra -/// space. Without the null terminator it can result in undefined behaviour -/// when the string is read. -/// -/// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-not-null-terminated-result.html -class NotNullTerminatedResultCheck : public ClangTidyCheck { -public: - NotNullTerminatedResultCheck(StringRef Name, ClangTidyContext *Context); - void storeOptions(ClangTidyOptions::OptionMap &Opts) override; - void registerMatchers(ast_matchers::MatchFinder *Finder) override; - void check(const ast_matchers::MatchFinder::MatchResult &Result) override; - void registerPPCallbacks(CompilerInstance &Compiler) override; - -private: - // If non-zero it is specifying if the target environment is considered to - // implement '_s' suffixed memory and string handler functions which are safer - // than older version (e.g. 'memcpy_s()'). The default value is ``1``. - const int WantToUseSafeFunctions; - - bool UseSafeFunctions = false; - - void memoryHandlerFunctionFix( - StringRef Name, const ast_matchers::MatchFinder::MatchResult &Result); - void memcpyFix(StringRef Name, - const ast_matchers::MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); - void memcpy_sFix(StringRef Name, - const ast_matchers::MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); - void memchrFix(StringRef Name, - const ast_matchers::MatchFinder::MatchResult &Result); - void memmoveFix(StringRef Name, - const ast_matchers::MatchFinder::MatchResult &Result, - DiagnosticBuilder &Diag); - void strerror_sFix(const ast_matchers::MatchFinder::MatchResult &Result); - void ncmpFix(StringRef Name, - const ast_matchers::MatchFinder::MatchResult &Result); - void xfrmFix(StringRef Name, - const ast_matchers::MatchFinder::MatchResult &Result); -}; - -} // namespace bugprone -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_NOT_NULL_TERMINATED_RESULT_H diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst index 4ea031490..5adb42871 100644 --- a/docs/ReleaseNotes.rst +++ b/docs/ReleaseNotes.rst @@ -116,15 +116,6 @@ Improvements to clang-tidy Detects usage of the deprecated member types of ``std::ios_base`` and replaces those that have a non-deprecated equivalent. -- New :doc:`bugprone-not-null-terminated-result - ` check - - Finds function calls where it is possible to cause a not null-terminated - result. Usually the proper length of a string is ``strlen(src) + 1`` or equal - length of this expression, because the null terminator needs an extra space. - Without the null terminator it can result in undefined behaviour when the - string is read. - - New :doc:`readability-magic-numbers ` check. diff --git a/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst b/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst deleted file mode 100644 index 72f465819..000000000 --- a/docs/clang-tidy/checks/bugprone-not-null-terminated-result.rst +++ /dev/null @@ -1,132 +0,0 @@ -.. title:: clang-tidy - bugprone-not-null-terminated-result - -bugprone-not-null-terminated-result -=================================== - -Finds function calls where it is possible to cause a not null-terminated result. -Usually the proper length of a string is ``strlen(src) + 1`` or equal length of -this expression, because the null terminator needs an extra space. Without the -null terminator it can result in undefined behaviour when the string is read. - -The following function calls are checked: - -``memcpy``, ``wmemcpy``, ``memcpy_s``, ``wmemcpy_s``, ``memchr``, ``wmemchr``, -``memmove``, ``wmemmove``, ``memmove_s``, ``wmemmove_s``, ``memset``, -``wmemset``, ``strerror_s``, ``strncmp``, ``wcsncmp``, ``strxfrm``, ``wcsxfrm`` - -The following is a real-world example where the programmer forgot to increase -the passed third argument, which is ``size_t length``. That is why the length -of the allocated memory is problematic too. - - .. code-block:: c - - static char *StringCpy(const std::string &str) { - char *result = reinterpret_cast(malloc(str.size())); - memcpy(result, str.data(), str.size()); - return result; - } - -In addition to issuing warnings, fix-it rewrites all the necessary code. If it -is necessary, the buffer size will be increased to hold the null terminator. - - .. code-block:: c - - static char *StringCpy(const std::string &str) { - char *result = reinterpret_cast(malloc(str.size() + 1)); - strcpy(result, str.data()); - return result; - } - -.. _MemcpyTransformation: - -Transformation rules of 'memcpy()' ----------------------------------- - -It is possible to rewrite the ``memcpy()`` and ``memcpy_s()`` calls as the -following four functions: ``strcpy()``, ``strncpy()``, ``strcpy_s()``, -``strncpy_s()``, where the latter two are the safer versions of the former two. -Respectively it is possible to rewrite ``wmemcpy()`` functions in the same way. - -Rewrite to a string handler function is not possible: - -- If the type of the destination array is not just ``char`` (``unsigned char`` - or ``signed char``), that means the new function is cannot be any string - handler function. Fix-it adds ``+ 1`` to the given length of copy function. - -Rewrite based on the destination array: - -- If copy to the destination array cannot *overflow then the new function should - be the older copy function (ending with ``cpy``), because it is more - efficient than the safe version. - -- If copy to the destination array can *overflow and - ``AreSafeFunctionsAvailable`` is set to ``Yes``, ``y`` or non-zero and it is - possible to obtain the capacity of the destination array then the new function - could be the safe version (ending with ``cpy_s``). - -- If the new function is could be safe version and C++ files are analysed then - the length of the destination array can be omitted. - -- *It is possible to overflow: - - Unknown the capacity of the destination array. - - If the given length is equal to the destination capacity. - -Rewrite based on the length of the source string: - -- If the given length is ``strlen(source)`` or equal length of this expression - then the new function should be the older copy function (ending with ``cpy``), - as it is more efficient than the safe version. - -- Otherwise we assume that the programmer wanted to copy `n` characters, so the - new function is ``ncpy``-like which is could be safe. - -Transformations with 'strlen()' or equal length of this expression ------------------------------------------------------------------- - -In general, the following transformations are could happen: - -(Note: If a wide-character handler function exists of the following functions -it handled in the same way.) - -Memory handler functions -^^^^^^^^^^^^^^^^^^^^^^^^ - -- ``memcpy``: See in the - :ref:`Transformation rules of 'memcpy()'` section. - -- ``memchr``: - - Usually there is a C-style cast, and it is needed to be removed, because the - new function ``strchr``'s return type is correct. - - Also the given length is not needed in the new function. - -- ``memmove``: - - If safe functions are available the new function is ``memmove_s``, it has - four arguments: - - destination array, - - length of the destination array, - - source string, - - length of the source string which is incremented by one. - - If safe functions are not available the given length is incremented by one. - -- ``memmove_s``: given length is incremented by one. - -- ``memset``: given length has to be truncated without the ``+ 1``. - -String handler functions -^^^^^^^^^^^^^^^^^^^^^^^^ - -- ``strerror_s``: given length is incremented by one. - -- ``strncmp``: If the third argument is the first or the second argument's - ``length + 1``, then it has to be truncated without the ``+ 1`` operation. - -- ``strxfrm``: given length is incremented by one. - -Options -------- - -.. option:: WantToUseSafeFunctions - - An integer non-zero value specifying if the target environment is considered - to implement '_s' suffixed memory and string handler functions which are - safer than older version (e.g. 'memcpy_s()'). The default value is ``1``. diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index 90079f01c..cc966a9b0 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -44,7 +44,6 @@ Clang-Tidy Checks bugprone-misplaced-widening-cast bugprone-move-forwarding-reference bugprone-multiple-statement-macro - bugprone-not-null-terminated-result bugprone-parent-virtual-call bugprone-sizeof-container bugprone-sizeof-expression diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c b/test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c deleted file mode 100644 index 0b5669ec1..000000000 --- a/test/clang-tidy/bugprone-not-null-terminated-result-in-initialization-strlen.c +++ /dev/null @@ -1,106 +0,0 @@ -// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ -// RUN: -- -std=c11 - -typedef unsigned int size_t; -typedef int errno_t; -size_t strlen(const char *); -char *strerror(int); -char *strchr(const char *, int); -errno_t *strncpy_s(char *, const char *, size_t); -errno_t strerror_s(char *, size_t, int); -int strncmp(const char *, const char *, size_t); -size_t strxfrm(char *, const char *, size_t); - -void *memchr(const void *, int, size_t); -void *memset(void *, int, size_t); - -int getLengthWithInc(const char *str) { - return strlen(str) + 1; -} - - -void bad_memchr(char *position, const char *src) { - int length = strlen(src); - position = (char *)memchr(src, '\0', length); - // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result] - // CHECK-FIXES: position = strchr(src, '\0'); -} - -void good_memchr(char *pos, const char *src) { - pos = strchr(src, '\0'); -} - -void bad_memset_1(const char *src) { - char dest[13]; - memset(dest, '-', getLengthWithInc(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memset' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: memset(dest, '-', getLengthWithInc(src) - 1); -} - -void good_memset1(const char *src) { - char dst[13]; - memset(dst, '-', getLengthWithInc(src) - 1); -} - -void bad_strerror_s(int errno) { - char dest[13]; - int length = strlen(strerror(errno)); - strerror_s(dest, length, errno); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'strerror_s' is not null-terminated and missing the last character of the error message [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest[14]; - // CHECK-FIXES-NEXT: int length = strlen(strerror(errno)); - // CHECK-FIXES-NEXT: strerror_s(dest, length + 1, errno); -} - -void good_strerror_s(int errno) { - char dst[14]; - int length = strlen(strerror(errno)); - strerror_s(dst, length + 1, errno); -} - -int bad_strncmp_1(char *str1, const char *str2) { - int length = strlen(str1) + 1; - return strncmp(str1, str2, length); - // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] - // CHECK-FIXES: strncmp(str1, str2, length - 1); -} - -int good_strncmp_1(char *str1, const char *str2) { - int length = strlen(str1) + 1; - return strncmp(str1, str2, length - 1); -} - -int bad_strncmp_2(char *str2) { - return strncmp(str2, "foobar", (strlen("foobar") + 1)); - // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] - // CHECK-FIXES: strncmp(str2, "foobar", strlen("foobar")); -} - -int bad_strncmp_3(char *str3) { - return strncmp(str3, "foobar", 1 + strlen("foobar")); - // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] - // CHECK-FIXES: strncmp(str3, "foobar", strlen("foobar")); -} - -int good_strncmp_2_3(char *str) { - return strncmp(str, "foobar", strlen("foobar")); -} - -void bad_strxfrm(const char *long_source_name) { - char long_destination_name[13]; - int very_long_length_definition_name = strlen(long_source_name); - strxfrm(long_destination_name, long_source_name, - very_long_length_definition_name); - // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: the result from calling 'strxfrm' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char long_destination_name[14]; - // CHECK-FIXES-NEXT: int very_long_length_definition_name = strlen(long_source_name); - // CHECK-FIXES-NEXT: strxfrm(long_destination_name, long_source_name, - // CHECK-FIXES-NEXT: very_long_length_definition_name + 1); -} - -void good_strxfrm(const char *long_source_name) { - char long_destination_name[14]; - int very_long_length_definition_name = strlen(long_source_name); - strxfrm(long_destination_name, long_source_name, - very_long_length_definition_name + 1); -} diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c b/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c deleted file mode 100644 index 4f9117471..000000000 --- a/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-before-safe.c +++ /dev/null @@ -1,78 +0,0 @@ -// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ -// RUN: -config="{CheckOptions: \ -// RUN: [{key: bugprone-not-null-terminated-result.WantToUseSafeFunctions, \ -// RUN: value: 1}]}" \ -// RUN: -- -std=c11 - -// It is not defined therefore the safe functions are unavailable. -// #define __STDC_LIB_EXT1__ 1 - -#define __STDC_WANT_LIB_EXT1__ 1 - -typedef unsigned int size_t; -typedef int errno_t; -size_t strlen(const char *); -void *malloc(size_t); - -char *strcpy(char *, const char *); -void *memcpy(void *, const void *, size_t); - - -//===----------------------------------------------------------------------===// -// memcpy() - destination array tests -//===----------------------------------------------------------------------===// - -void bad_memcpy_not_just_char_dest(const char *src) { - unsigned char dest00[13]; - memcpy(dest00, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: memcpy(dest00, src, strlen(src) + 1); -} - -void good_memcpy_not_just_char_dest(const char *src) { - unsigned char dst00[13]; - memcpy(dst00, src, strlen(src) + 1); -} - -void bad_memcpy_known_dest(const char *src) { - char dest01[13]; - memcpy(dest01, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: strcpy(dest01, src); -} - -void good_memcpy_known_dest(const char *src) { - char dst01[13]; - strcpy(dst01, src); -} - -//===----------------------------------------------------------------------===// -// memcpy() - length tests -//===----------------------------------------------------------------------===// - -void bad_memcpy_full_source_length(const char *src) { - char dest20[13]; - memcpy(dest20, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: strcpy(dest20, src); -} - -void good_memcpy_full_source_length(const char *src) { - char dst20[13]; - strcpy(dst20, src); -} - -void bad_memcpy_partial_source_length(const char *src) { - char dest21[13]; - memcpy(dest21, src, strlen(src) - 1); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: strncpy(dest21, src, strlen(src) - 1); - // CHECK-FIXES-NEXT: dest21[strlen(src) - 1] = '\0'; -} - -void good_memcpy_partial_source_length(const char *src) { - char dst21[13]; - strncpy(dst21, src, strlen(src) - 1); - dst21[strlen(src) - 1] = '\0'; -} - diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp b/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp deleted file mode 100644 index e5a73791c..000000000 --- a/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-cxx.cpp +++ /dev/null @@ -1,160 +0,0 @@ -// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ -// RUN: -- -std=c++11 - -#define __STDC_LIB_EXT1__ 1 -#define __STDC_WANT_LIB_EXT1__ 1 - -namespace std { -template -struct basic_string { - basic_string(); - const T *data() const; - unsigned long size() const; -}; -typedef basic_string string; -} -typedef unsigned int size_t; -typedef int errno_t; -size_t strlen(const char *); -void *malloc(size_t); -void *realloc(void *, size_t); - -template -errno_t strncpy_s(char (&dest)[size], const char *src, size_t length); -errno_t strncpy_s(char *, size_t, const char *, size_t); - -template -char *strncpy(char (&dest)[size], const char *src, size_t length); -char *strncpy(char *, const char *, size_t); - -template -errno_t strcpy_s(char (&dest)[size], const char *); -errno_t strcpy_s(char *, size_t, const char *); - -template -char *strcpy(char (&dest)[size], const char *); -char *strcpy(char *, const char *); - -errno_t memcpy_s(void *, size_t, const void *, size_t); -void *memcpy(void *, const void *, size_t); - - -//===----------------------------------------------------------------------===// -// memcpy() - destination array tests -//===----------------------------------------------------------------------===// - -void bad_memcpy_not_just_char_dest(const char *src) { - unsigned char dest00[13]; - memcpy(dest00, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: unsigned char dest00[14]; - // CHECK-FIXES-NEXT: memcpy_s(dest00, 14, src, strlen(src) + 1); -} - -void good_memcpy_not_just_char_dest(const char *src) { - unsigned char dst00[14]; - memcpy_s(dst00, 14, src, strlen(src) + 1); -} - -void bad_memcpy_known_dest(const char *src) { - char dest01[13]; - memcpy(dest01, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: dest01[14]; - // CHECK-FIXES-NEXT: strcpy_s(dest01, src); -} - -void good_memcpy_known_dest(const char *src) { - char dst01[14]; - strcpy_s(dst01, src); -} - -//===----------------------------------------------------------------------===// -// memcpy() - length tests -//===----------------------------------------------------------------------===// - -void bad_memcpy_full_source_length(std::string src) { - char *dest20; - dest20 = reinterpret_cast(malloc(src.size())); - memcpy(dest20, src.data(), src.size()); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: dest20 = reinterpret_cast(malloc(src.size() + 1)); - // CHECK-FIXES-NEXT: strcpy(dest20, src.data()); -} - -void good_memcpy_full_source_length(std::string src) { - char dst20[14]; - strcpy_s(dst20, src.data()); -} - -void bad_memcpy_partial_source_length(const char *src) { - char dest21[13]; - memcpy(dest21, src, strlen(src) - 1); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest21[14]; - // CHECK-FIXES-NEXT: strncpy_s(dest21, src, strlen(src) - 1); -} - -void good_memcpy_partial_source_length(const char *src) { - char dst21[14]; - strncpy_s(dst21, src, strlen(src) - 1); -} - - -//===----------------------------------------------------------------------===// -// memcpy_s() - destination array tests -//===----------------------------------------------------------------------===// - -void bad_memcpy_s_unknown_dest(char *dest40, const char *src) { - memcpy_s(dest40, 13, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: strcpy_s(dest40, 13, src); -} - -void good_memcpy_s_unknown_dest(char *dst40, const char *src) { - strcpy_s(dst40, 13, src); -} - -void bad_memcpy_s_known_dest(const char *src) { - char dest41[13]; - memcpy_s(dest41, 13, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest41[14]; - // CHECK-FIXES: strcpy_s(dest41, src); -} - -void good_memcpy_s_known_dest(const char *src) { - char dst41[14]; - strcpy_s(dst41, src); -} - -//===----------------------------------------------------------------------===// -// memcpy_s() - length tests -//===----------------------------------------------------------------------===// - -void bad_memcpy_s_full_source_length(const char *src) { - char dest60[13]; - memcpy_s(dest60, 13, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest60[14]; - // CHECK-FIXES-NEXT: strcpy_s(dest60, src); -} - -void good_memcpy_s_full_source_length(const char *src) { - char dst60[14]; - strcpy_s(dst60, src); -} - -void bad_memcpy_s_partial_source_length(const char *src) { - char dest61[13]; - memcpy_s(dest61, 13, src, strlen(src) - 1); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest61[14]; - // CHECK-FIXES-NEXT: strncpy_s(dest61, src, strlen(src) - 1); -} - -void good_memcpy_s_partial_source_length(const char *src) { - char dst61[14]; - strncpy_s(dst61, src, strlen(src) - 1); -} - diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c b/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c deleted file mode 100644 index 84152255a..000000000 --- a/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe-other.c +++ /dev/null @@ -1,115 +0,0 @@ -// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ -// RUN: -- -std=c11 - -#define __STDC_LIB_EXT1__ 1 -#define __STDC_WANT_LIB_EXT1__ 1 - -typedef unsigned int size_t; -typedef int errno_t; -size_t strlen(const char *); -void *malloc(size_t); -void *realloc(void *, size_t); - -errno_t strncpy_s(char *, size_t, const char *, size_t); -errno_t strcpy_s(char *, size_t, const char *); -char *strcpy(char *, const char *); - -errno_t memcpy_s(void *, size_t, const void *, size_t); -void *memcpy(void *, const void *, size_t); - -#define SRC_LENGTH 3 -#define SRC "foo" - - -void good_memcpy_known_src() { - char dest[13]; - char src[] = "foobar"; - memcpy(dest, src, sizeof(src)); -} - -void good_memcpy_null_terminated(const char *src) { - char dest[13]; - const int length = strlen(src); - memcpy(dest, src, length); - dest[length] = '\0'; -} - -void good_memcpy_proper_length(const char *src) { - char *dest = 0; - int length = strlen(src) + 1; - dest = (char *)malloc(length); - memcpy(dest, src, length); -} - -void may_bad_memcpy_unknown_length(const char *src, int length) { - char dest[13]; - memcpy(dest, src, length); -} - -void may_bad_memcpy_const_length(const char *src) { - char dest[13]; - memcpy(dest, src, 12); -} - -void bad_memcpy_unknown_dest(char *dest01, const char *src) { - memcpy(dest01, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: strcpy(dest01, src); -} - -void good_memcpy_unknown_dest(char *dst01, const char *src) { - strcpy(dst01, src); -} - -void bad_memcpy_variable_array(int dest_length) { - char dest02[dest_length + 1]; - memcpy(dest02, "foobarbazqux", strlen("foobarbazqux")); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: strcpy(dest02, "foobarbazqux"); -} - -void good_memcpy_variable_array(int dest_length) { - char dst02[dest_length + 1]; - strcpy(dst02, "foobarbazqux"); -} - -void bad_memcpy_equal_src_length_and_length() { - char dest03[13]; - const char *src = "foobarbazqux"; - memcpy(dest03, src, 12); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: strcpy(dest03, src); -} - -void good_memcpy_equal_src_length_and_length() { - char dst03[13]; - const char *src = "foobarbazqux"; - strcpy(dst03, src); -} - -void bad_memcpy_dest_size_overflows(const char *src) { - const int length = strlen(src); - char *dest04 = (char *)malloc(length); - memcpy(dest04, src, length); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char *dest04 = (char *)malloc(length + 1); - // CHECK-FIXES-NEXT: strcpy(dest04, src); -} - -void good_memcpy_dest_size_overflows(const char *src) { - const int length = strlen(src); - char *dst04 = (char *)malloc(length + 1); - strcpy(dst04, src); -} - -void bad_memcpy_macro() { - unsigned char dest05[13]; - memcpy(dest05, SRC, SRC_LENGTH); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: memcpy_s(dest05, 13, SRC, SRC_LENGTH + 1); -} - -void good_memcpy_macro() { - unsigned char dst05[13]; - memcpy_s(dst05, 13, SRC, SRC_LENGTH + 1); -} diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c b/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c deleted file mode 100644 index dfbd5487a..000000000 --- a/test/clang-tidy/bugprone-not-null-terminated-result-memcpy-safe.c +++ /dev/null @@ -1,137 +0,0 @@ -// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ -// RUN: -- -std=c11 - -#define __STDC_LIB_EXT1__ 1 -#define __STDC_WANT_LIB_EXT1__ 1 - -typedef unsigned int size_t; -typedef int errno_t; -size_t strlen(const char *); -void *malloc(size_t); -void *realloc(void *, size_t); - -errno_t strncpy_s(char *, size_t, const char *, size_t); -errno_t strcpy_s(char *, size_t, const char *); -char *strcpy(char *, const char *); - -errno_t memcpy_s(void *, size_t, const void *, size_t); -void *memcpy(void *, const void *, size_t); - -//===----------------------------------------------------------------------===// -// memcpy() - destination array tests -//===----------------------------------------------------------------------===// - -void bad_memcpy_not_just_char_dest(const char *src) { - unsigned char dest00[13]; - memcpy(dest00, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: unsigned char dest00[14]; - // CHECK-FIXES-NEXT: memcpy_s(dest00, 14, src, strlen(src) + 1); -} - -void good_memcpy_not_just_char_dest(const char *src) { - unsigned char dst00[14]; - memcpy_s(dst00, 14, src, strlen(src) + 1); -} - -void bad_memcpy_known_dest(const char *src) { - char dest01[13]; - memcpy(dest01, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest01[14]; - // CHECK-FIXES: strcpy_s(dest01, 14, src); -} - -void good_memcpy_known_dest(const char *src) { - char dst01[14]; - strcpy_s(dst01, 14, src); -} - -//===----------------------------------------------------------------------===// -// memcpy() - length tests -//===----------------------------------------------------------------------===// - -void bad_memcpy_full_source_length(const char *src) { - char dest20[13]; - memcpy(dest20, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest20[14]; - // CHECK-FIXES-NEXT: strcpy_s(dest20, 14, src); -} - -void good_memcpy_full_source_length(const char *src) { - char dst20[14]; - strcpy_s(dst20, 14, src); -} - -void bad_memcpy_partial_source_length(const char *src) { - char dest21[13]; - memcpy(dest21, src, strlen(src) - 1); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest21[14]; - // CHECK-FIXES-NEXT: strncpy_s(dest21, 14, src, strlen(src) - 1); -} - -void good__memcpy_partial_source_length(const char *src) { - char dst21[14]; - strncpy_s(dst21, 14, src, strlen(src) - 1); -} - - -//===----------------------------------------------------------------------===// -// memcpy_s() - destination array tests -//===----------------------------------------------------------------------===// - -void bad_memcpy_s_unknown_dest(char *dest40, const char *src) { - memcpy_s(dest40, 13, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: strcpy_s(dest40, 13, src); -} - -void good_memcpy_s_unknown_dest(char *dst40, const char *src) { - strcpy_s(dst40, 13, src); -} - -void bad_memcpy_s_known_dest(const char *src) { - char dest41[13]; - memcpy_s(dest41, 13, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest41[14]; - // CHECK-FIXES-NEXT: strcpy_s(dest41, 14, src); -} - -void good_memcpy_s_known_dest(const char *src) { - char dst41[14]; - strcpy_s(dst41, 14, src); -} - -//===----------------------------------------------------------------------===// -// memcpy_s() - length tests -//===----------------------------------------------------------------------===// - -void bad_memcpy_s_full_source_length(const char *src) { - char dest60[13]; - memcpy_s(dest60, 13, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest60[14]; - // CHECK-FIXES-NEXT: strcpy_s(dest60, 14, src); -} - -void good_memcpy_s_full_source_length(const char *src) { - char dst60[14]; - strcpy_s(dst60, 14, src); -} - -void bad_memcpy_s_partial_source_length(const char *src) { - char dest61[13]; - memcpy_s(dest61, 13, src, strlen(src) - 1); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memcpy_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest61[14]; - // CHECK-FIXES-NEXT: strncpy_s(dest61, 14, src, strlen(src) - 1); -} - -void good_memcpy_s_partial_source_length(const char *src) { - char dst61[14]; - strncpy_s(dst61, 14, src, strlen(src) - 1); -} - diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-strlen.c b/test/clang-tidy/bugprone-not-null-terminated-result-strlen.c deleted file mode 100644 index a7d04f5e2..000000000 --- a/test/clang-tidy/bugprone-not-null-terminated-result-strlen.c +++ /dev/null @@ -1,146 +0,0 @@ -// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ -// RUN: -- -std=c11 - -#define __STDC_LIB_EXT1__ 1 -#define __STDC_WANT_LIB_EXT1__ 1 - -typedef unsigned int size_t; -typedef int errno_t; -size_t strlen(const char *); -char *strerror(int); - -char *strchr(const char *, int); -errno_t strncpy_s(char *, size_t, const char *, size_t); -errno_t strerror_s(char *, size_t, int); -int strncmp(const char *, const char *, size_t); -size_t strxfrm(char *, const char *, size_t); - -void *memchr(const void *, int, size_t); -void *memmove(void *, const void *, size_t); -errno_t memmove_s(void *, size_t, const void *, size_t); -void *memset(void *, int, size_t); - - -void bad_memchr_1(char *position, const char *src) { - position = (char *)memchr(src, '\0', strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result] - // CHECK-FIXES: position = strchr(src, '\0'); -} - -void good_memchr_1(char *pos, const char *src) { - pos = strchr(src, '\0'); -} - -void bad_memchr_2(char *position) { - position = (char *)memchr("foobar", '\0', 6); - // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result] - // CHECK-FIXES: position = strchr("foobar", '\0'); -} - -void good_memchr_2(char *pos) { - pos = strchr("foobar", '\0'); -} - - -void bad_memmove(const char *src) { - char dest[13]; - memmove(dest, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memmove' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest[14]; - // CHECK-FIXES-NEXT: memmove_s(dest, 14, src, strlen(src) + 1); -} - -void good_memmove(const char *src) { - char dst[14]; - memmove_s(dst, 13, src, strlen(src) + 1); -} - -void bad_memmove_s(char *dest, const char *src) { - memmove_s(dest, 13, src, strlen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memmove_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: memmove_s(dest, 13, src, strlen(src) + 1); -} - -void good_memmove_s_1(char *dest, const char *src) { - memmove_s(dest, 13, src, strlen(src) + 1); -} - -void bad_memset(const char *src) { - char dest[13]; - memset(dest, '-', strlen(src) + 1); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'memset' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: memset(dest, '-', strlen(src)); -} - -void good_memset(const char *src) { - char dst[13]; - memset(dst, '-', strlen(src)); -} - -void bad_strerror_s(int errno) { - char dest[13]; - strerror_s(dest, strlen(strerror(errno)), errno); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'strerror_s' is not null-terminated and missing the last character of the error message [bugprone-not-null-terminated-result] - // CHECK-FIXES: char dest[14]; - // CHECK-FIXES-NEXT: strerror_s(dest, strlen(strerror(errno)) + 1, errno); -} - -void good_strerror_s(int errno) { - char dst[14]; - strerror_s(dst, strlen(strerror(errno)) + 1, errno); -} - -int bad_strncmp_1(char *str0, const char *str1) { - return strncmp(str0, str1, (strlen(str0) + 1)); - // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] - // CHECK-FIXES: strncmp(str0, str1, strlen(str0)); -} - -int bad_strncmp_2(char *str2, const char *str3) { - return strncmp(str2, str3, 1 + strlen(str2)); - // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] - // CHECK-FIXES: strncmp(str2, str3, strlen(str2)); -} - -int good_strncmp_1_2(char *str4, const char *str5) { - return strncmp(str4, str5, strlen(str4)); -} - -int bad_strncmp_3(char *str6) { - return strncmp(str6, "string", 7); - // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] - // CHECK-FIXES: strncmp(str6, "string", 6); -} - -int good_strncmp_3(char *str7) { - return strncmp(str7, "string", 6); -} - -void bad_strxfrm_1(const char *long_source_name) { - char long_destination_array_name[13]; - strxfrm(long_destination_array_name, long_source_name, - strlen(long_source_name)); - // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: the result from calling 'strxfrm' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char long_destination_array_name[14]; - // CHECK-FIXES-NEXT: strxfrm(long_destination_array_name, long_source_name, - // CHECK-FIXES-NEXT: strlen(long_source_name) + 1); -} - -void good_strxfrm_1(const char *long_source_name) { - char long_destination_array_name[14]; - strxfrm(long_destination_array_name, long_source_name, - strlen(long_source_name) + 1); -} - -void bad_strxfrm_2() { - char long_destination_array_name1[16]; - strxfrm(long_destination_array_name1, "long_source_name", 16); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'strxfrm' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: char long_destination_array_name1[17]; - // CHECK-FIXES: strxfrm(long_destination_array_name1, "long_source_name", 17); -} - -void good_strxfrm_2() { - char long_destination_array_name2[17]; - strxfrm(long_destination_array_name2, "long_source_name", 17); -} diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp b/test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp deleted file mode 100644 index c25637b87..000000000 --- a/test/clang-tidy/bugprone-not-null-terminated-result-wcslen.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ -// RUN: -- -std=c++11 - -#define __STDC_LIB_EXT1__ 1 -#define __STDC_WANT_LIB_EXT1__ 1 - -typedef unsigned int size_t; -typedef int errno_t; -size_t wcslen(const wchar_t *); - -wchar_t *wcschr(const wchar_t *, int); -errno_t wcsncpy_s(wchar_t *, size_t, const wchar_t *, size_t); -int wcsncmp(const wchar_t *, const wchar_t *, size_t); -size_t wcsxfrm(wchar_t *, const wchar_t *, size_t); - -void *wmemchr(const void *, int, size_t); -void *wmemmove(void *, const void *, size_t); -errno_t wmemmove_s(void *, size_t, const void *, size_t); -void *wmemset(void *, int, size_t); - - -void bad_wmemchr_1(wchar_t *position, const wchar_t *src) { - position = (wchar_t *)wmemchr(src, L'\0', wcslen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result] - // CHECK-FIXES: position = wcschr(src, L'\0'); -} - -void good_wmemchr_1(wchar_t *pos, const wchar_t *src) { - pos = wcschr(src, L'\0'); -} - -void bad_wmemchr_2(wchar_t *position) { - position = (wchar_t *)wmemchr(L"foobar", L'\0', 6); - // CHECK-MESSAGES: :[[@LINE-1]]:51: warning: the length is too short to include the null terminator [bugprone-not-null-terminated-result] - // CHECK-FIXES: position = wcschr(L"foobar", L'\0'); -} - -void good_wmemchr_2(wchar_t *pos) { - pos = wcschr(L"foobar", L'\0'); -} - - -void bad_wmemmove(const wchar_t *src) { - wchar_t dest[13]; - wmemmove(dest, src, wcslen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemmove' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: wchar_t dest[14]; - // CHECK-FIXES-NEXT: wmemmove_s(dest, 14, src, wcslen(src) + 1); -} - -void good_wmemmove(const wchar_t *src) { - wchar_t dst[14]; - wmemmove_s(dst, 13, src, wcslen(src) + 1); -} - -void bad_wmemmove_s(wchar_t *dest, const wchar_t *src) { - wmemmove_s(dest, 13, src, wcslen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemmove_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: wmemmove_s(dest, 13, src, wcslen(src) + 1); -} - -void good_wmemmove_s_1(wchar_t *dest, const wchar_t *src) { - wmemmove_s(dest, 13, src, wcslen(src) + 1); -} - -void bad_wmemset(const wchar_t *src) { - wchar_t dest[13]; - wmemset(dest, L'-', wcslen(src) + 1); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemset' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: wmemset(dest, L'-', wcslen(src)); -} - -void good_wmemset(const wchar_t *src) { - wchar_t dst[13]; - wmemset(dst, L'-', wcslen(src)); -} - -int bad_wcsncmp_1(wchar_t *wcs0, const wchar_t *wcs1) { - return wcsncmp(wcs0, wcs1, (wcslen(wcs0) + 1)); - // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] - // CHECK-FIXES: wcsncmp(wcs0, wcs1, wcslen(wcs0)); -} - -int bad_wcsncmp_2(wchar_t *wcs2, const wchar_t *wcs3) { - return wcsncmp(wcs2, wcs3, 1 + wcslen(wcs2)); - // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] - // CHECK-FIXES: wcsncmp(wcs2, wcs3, wcslen(wcs2)); -} - -int good_wcsncmp_1_2(wchar_t *wcs4, const wchar_t *wcs5) { - return wcsncmp(wcs4, wcs5, wcslen(wcs4)); -} - -int bad_wcsncmp_3(wchar_t *wcs6) { - return wcsncmp(wcs6, L"string", 7); - // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: comparison length is too long and might lead to a buffer overflow [bugprone-not-null-terminated-result] - // CHECK-FIXES: wcsncmp(wcs6, L"string", 6); -} - -int good_wcsncmp_3(wchar_t *wcs7) { - return wcsncmp(wcs7, L"string", 6); -} - -void bad_wcsxfrm_1(const wchar_t *long_source_name) { - wchar_t long_destination_array_name[13]; - wcsxfrm(long_destination_array_name, long_source_name, - wcslen(long_source_name)); - // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: the result from calling 'wcsxfrm' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: wchar_t long_destination_array_name[14]; - // CHECK-FIXES-NEXT: wcsxfrm(long_destination_array_name, long_source_name, - // CHECK-FIXES-NEXT: wcslen(long_source_name) + 1); -} - -void good_wcsxfrm_1(const wchar_t *long_source_name) { - wchar_t long_destination_array_name[14]; - wcsxfrm(long_destination_array_name, long_source_name, - wcslen(long_source_name) + 1); -} - -void bad_wcsxfrm_2() { - wchar_t long_destination_array_name1[16]; - wcsxfrm(long_destination_array_name1, L"long_source_name", 16); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wcsxfrm' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: wchar_t long_destination_array_name1[17]; - // CHECK-FIXES: wcsxfrm(long_destination_array_name1, L"long_source_name", 17); -} - -void good_wcsxfrm_2() { - wchar_t long_destination_array_name2[17]; - wcsxfrm(long_destination_array_name2, L"long_source_name", 17); -} diff --git a/test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp b/test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp deleted file mode 100644 index 91867bd48..000000000 --- a/test/clang-tidy/bugprone-not-null-terminated-result-wmemcpy-safe-cxx.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// RUN: %check_clang_tidy %s bugprone-not-null-terminated-result %t -- \ -// RUN: -- -std=c++11 - -#define __STDC_LIB_EXT1__ 1 -#define __STDC_WANT_LIB_EXT1__ 1 - -typedef unsigned int size_t; -typedef int errno_t; -size_t wcslen(const wchar_t *); -void *malloc(size_t); -void *realloc(void *, size_t); - -template -errno_t wcsncpy_s(wchar_t (&dest)[size], const wchar_t *src, size_t length); -errno_t wcsncpy_s(wchar_t *, size_t, const wchar_t *, size_t); - -template -wchar_t *wcsncpy(wchar_t (&dest)[size], const wchar_t *src, size_t length); -wchar_t *wcsncpy(wchar_t *, const wchar_t *, size_t); - -template -errno_t wcscpy_s(wchar_t (&dest)[size], const wchar_t *); -errno_t wcscpy_s(wchar_t *, size_t, const wchar_t *); - -template -wchar_t *wcscpy(wchar_t (&dest)[size], const wchar_t *); -wchar_t *wcscpy(wchar_t *, const wchar_t *); - -errno_t wmemcpy_s(wchar_t *, size_t, const wchar_t *, size_t); -wchar_t *wmemcpy(wchar_t *, const wchar_t *, size_t); - - -//===----------------------------------------------------------------------===// -// wmemcpy() - destination array tests -//===----------------------------------------------------------------------===// - -void bad_wmemcpy_known_dest(const wchar_t *src) { - wchar_t dest01[13]; - wmemcpy(dest01, src, wcslen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: wchar_t dest01[14]; - // CHECK-FIXES-NEXT: wcscpy_s(dest01, src); -} - -void good_wmemcpy_known_dest(const wchar_t *src) { - wchar_t dst01[14]; - wcscpy_s(dst01, src); -} - -//===----------------------------------------------------------------------===// -// wmemcpy() - length tests -//===----------------------------------------------------------------------===// - -void bad_wmemcpy_full_source_length(const wchar_t *src) { - wchar_t dest20[13]; - wmemcpy(dest20, src, wcslen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: wchar_t dest20[14]; - // CHECK-FIXES-NEXT: wcscpy_s(dest20, src); -} - -void good_wmemcpy_full_source_length(const wchar_t *src) { - wchar_t dst20[14]; - wcscpy_s(dst20, src); -} - -void bad_wmemcpy_partial_source_length(const wchar_t *src) { - wchar_t dest21[13]; - wmemcpy(dest21, src, wcslen(src) - 1); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: wchar_t dest21[14]; - // CHECK-FIXES-NEXT: wcsncpy_s(dest21, src, wcslen(src) - 1); -} - -void good_wmemcpy_partial_source_length(const wchar_t *src) { - wchar_t dst21[14]; - wcsncpy_s(dst21, src, wcslen(src) - 1); -} - -//===----------------------------------------------------------------------===// -// wmemcpy_s() - destination array tests -//===----------------------------------------------------------------------===// - -void bad_wmemcpy_s_unknown_dest(wchar_t *dest40, const wchar_t *src) { - wmemcpy_s(dest40, 13, src, wcslen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: wcscpy_s(dest40, 13, src); -} - -void good_wmemcpy_s_unknown_dest(wchar_t *dst40, const wchar_t *src) { - wcscpy_s(dst40, 13, src); -} - -void bad_wmemcpy_s_known_dest(const wchar_t *src) { - wchar_t dest41[13]; - wmemcpy_s(dest41, 13, src, wcslen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: wchar_t dest41[14]; - // CHECK-FIXES-NEXT: wcscpy_s(dest41, src); -} - -void good_wmemcpy_s_known_dest(const wchar_t *src) { - wchar_t dst41[13]; - wcscpy_s(dst41, src); -} - -//===----------------------------------------------------------------------===// -// wmemcpy_s() - length tests -//===----------------------------------------------------------------------===// - -void bad_wmemcpy_s_full_source_length(const wchar_t *src) { - wchar_t dest60[13]; - wmemcpy_s(dest60, 13, src, wcslen(src)); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: wchar_t dest60[14]; - // CHECK-FIXES-NEXT: wcscpy_s(dest60, src); -} - -void good_wmemcpy_s_full_source_length(const wchar_t *src) { - wchar_t dst60[13]; - wcscpy_s(dst60, src); -} - -void bad_wmemcpy_s_partial_source_length(const wchar_t *src) { - wchar_t dest61[13]; - wmemcpy_s(dest61, 13, src, wcslen(src) - 1); - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the result from calling 'wmemcpy_s' is not null-terminated [bugprone-not-null-terminated-result] - // CHECK-FIXES: wchar_t dest61[14]; - // CHECK-FIXES-NEXT: wcsncpy_s(dest61, src, wcslen(src) - 1); -} - -void good_wmemcpy_s_partial_source_length(const wchar_t *src) { - wchar_t dst61[13]; - wcsncpy_s(dst61, src, wcslen(src) - 1); -} - From 54bab4639cfdcabee1f480f5f87fcee399ed5531 Mon Sep 17 00:00:00 2001 From: Adam Balogh Date: Sat, 13 Oct 2018 10:34:52 +0000 Subject: [PATCH 358/686] [clang-tidy] Optimize query in bugprone-exception-escape Checking whether a functions throws indirectly may be very expensive because it needs to visit its whole call graph. Therefore we should first check whether the function is forbidden to throw and only check whether it throws afterward. This also seems to solve bug https://bugs.llvm.org/show_bug.cgi?id=39167 where the execution time is so long that it seems to hang. Differential Revision: https://reviews.llvm.org/D53187 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344444 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-tidy/bugprone/ExceptionEscapeCheck.cpp | 6 ++--- .../checks/bugprone-exception-escape.rst | 2 ++ test/clang-tidy/bugprone-exception-escape.cpp | 25 +++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/clang-tidy/bugprone/ExceptionEscapeCheck.cpp b/clang-tidy/bugprone/ExceptionEscapeCheck.cpp index 6d1f7a782..c8af1abbb 100644 --- a/clang-tidy/bugprone/ExceptionEscapeCheck.cpp +++ b/clang-tidy/bugprone/ExceptionEscapeCheck.cpp @@ -190,12 +190,12 @@ void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) { return; Finder->addMatcher( - functionDecl(allOf(throws(unless(isIgnored(IgnoredExceptions))), - anyOf(isNoThrow(), cxxDestructorDecl(), + functionDecl(allOf(anyOf(isNoThrow(), cxxDestructorDecl(), cxxConstructorDecl(isMoveConstructor()), cxxMethodDecl(isMoveAssignmentOperator()), hasName("main"), hasName("swap"), - isEnabled(FunctionsThatShouldNotThrow)))) + isEnabled(FunctionsThatShouldNotThrow)), + throws(unless(isIgnored(IgnoredExceptions))))) .bind("thrower"), this); } diff --git a/docs/clang-tidy/checks/bugprone-exception-escape.rst b/docs/clang-tidy/checks/bugprone-exception-escape.rst index 3037f5e3e..e9653a7e5 100644 --- a/docs/clang-tidy/checks/bugprone-exception-escape.rst +++ b/docs/clang-tidy/checks/bugprone-exception-escape.rst @@ -21,6 +21,8 @@ are always possible to implement in a non throwing way. Non throwing ``swap()`` operations are also used to create move operations. A throwing ``main()`` function also results in unexpected termination. +WARNING! This check may be expensive on large source files. + Options ------- diff --git a/test/clang-tidy/bugprone-exception-escape.cpp b/test/clang-tidy/bugprone-exception-escape.cpp index af2c23d48..b9e6e4b60 100644 --- a/test/clang-tidy/bugprone-exception-escape.cpp +++ b/test/clang-tidy/bugprone-exception-escape.cpp @@ -258,6 +258,31 @@ void this_counts(int n) noexcept { throw ignored1(); } +void thrower(int n) { + throw n; +} + +int directly_recursive(int n) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in funcion 'directly_recursive' which should not throw exceptions + if (n == 0) + thrower(n); + return directly_recursive(n); +} + +int indirectly_recursive(int n) noexcept; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in functin 'indirectly_recursive' which should not throw exceptions + +int recursion_helper(int n) { + indirectly_recursive(n); +} + +int indirectly_recursive(int n) noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in funcion 'indirectly_recursive' which should not throw exceptions + if (n == 0) + thrower(n); + return recursion_helper(n); +} + int main() { // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'main' which should not throw exceptions throw 1; From 64d7862e9cd5f3f631de40c4b82c636adda2402c Mon Sep 17 00:00:00 2001 From: Adam Balogh Date: Sat, 13 Oct 2018 11:17:59 +0000 Subject: [PATCH 359/686] [clang-tidy] Fix for typos in the tests for `bugprone-exception-escape` git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344445 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/clang-tidy/bugprone-exception-escape.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/clang-tidy/bugprone-exception-escape.cpp b/test/clang-tidy/bugprone-exception-escape.cpp index b9e6e4b60..eba14bfcf 100644 --- a/test/clang-tidy/bugprone-exception-escape.cpp +++ b/test/clang-tidy/bugprone-exception-escape.cpp @@ -263,21 +263,21 @@ void thrower(int n) { } int directly_recursive(int n) noexcept { - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in funcion 'directly_recursive' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'directly_recursive' which should not throw exceptions if (n == 0) thrower(n); return directly_recursive(n); } int indirectly_recursive(int n) noexcept; -// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in functin 'indirectly_recursive' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'indirectly_recursive' which should not throw exceptions int recursion_helper(int n) { indirectly_recursive(n); } int indirectly_recursive(int n) noexcept { - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in funcion 'indirectly_recursive' which should not throw exceptions + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'indirectly_recursive' which should not throw exceptions if (n == 0) thrower(n); return recursion_helper(n); From a031e6447320bacdb8103b6c03697a2c70edbde4 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Sat, 13 Oct 2018 22:18:22 +0000 Subject: [PATCH 360/686] Move some helpers from the global namespace into anonymous ones. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344468 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index bdba66e0a..b870721c3 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -72,7 +72,8 @@ class RefactoringResultCollector final } // namespace // Returns callbacks that can be used to update the FileIndex with new ASTs. -std::unique_ptr makeUpdateCallbacks(FileIndex *FIndex) { +static std::unique_ptr +makeUpdateCallbacks(FileIndex *FIndex) { struct CB : public ParsingCallbacks { CB(FileIndex *FIndex) : FIndex(FIndex) {} FileIndex *FIndex; From 89fc20742d4ef9ccad32b01c44e09e375093143c Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Mon, 15 Oct 2018 11:46:26 +0000 Subject: [PATCH 361/686] [clangd] Fix some references missing in dynamic index. Summary: Previously, SymbolCollector postfilters all references at the end to find all references of interesting symbols. It was incorrect when indxing main AST where we don't see locations of symbol declarations and definitions in the main AST (as those are in preamble AST). The fix is to do earily check during collecting references. Reviewers: sammccall Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D53273 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344507 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/SymbolCollector.cpp | 36 ++++++++++---------- unittests/clangd/FileIndexTests.cpp | 51 +++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp index 26941d2dc..0d1281e46 100644 --- a/clangd/index/SymbolCollector.cpp +++ b/clangd/index/SymbolCollector.cpp @@ -345,16 +345,20 @@ bool SymbolCollector::handleDeclOccurence( SM.getFileID(SpellingLoc) == SM.getMainFileID()) ReferencedDecls.insert(ND); - if ((static_cast(Opts.RefFilter) & Roles) && - SM.getFileID(SpellingLoc) == SM.getMainFileID()) - DeclRefs[ND].emplace_back(SpellingLoc, Roles); + bool CollectRef = static_cast(Opts.RefFilter) & Roles; + bool IsOnlyRef = + !(Roles & (static_cast(index::SymbolRole::Declaration) | + static_cast(index::SymbolRole::Definition))); - // Don't continue indexing if this is a mere reference. - if (!(Roles & static_cast(index::SymbolRole::Declaration) || - Roles & static_cast(index::SymbolRole::Definition))) + if (IsOnlyRef && !CollectRef) return true; if (!shouldCollectSymbol(*ND, *ASTCtx, Opts)) return true; + if (CollectRef && SM.getFileID(SpellingLoc) == SM.getMainFileID()) + DeclRefs[ND].emplace_back(SpellingLoc, Roles); + // Don't continue indexing if this is a mere reference. + if (IsOnlyRef) + return true; auto ID = getSymbolID(ND); if (!ID) @@ -476,17 +480,15 @@ void SymbolCollector::finish() { std::string MainURI = *MainFileURI; for (const auto &It : DeclRefs) { if (auto ID = getSymbolID(It.first)) { - if (Symbols.find(*ID)) { - for (const auto &LocAndRole : It.second) { - Ref R; - auto Range = - getTokenRange(LocAndRole.first, SM, ASTCtx->getLangOpts()); - R.Location.Start = Range.first; - R.Location.End = Range.second; - R.Location.FileURI = MainURI; - R.Kind = toRefKind(LocAndRole.second); - Refs.insert(*ID, R); - } + for (const auto &LocAndRole : It.second) { + Ref R; + auto Range = + getTokenRange(LocAndRole.first, SM, ASTCtx->getLangOpts()); + R.Location.Start = Range.first; + R.Location.End = Range.second; + R.Location.FileURI = MainURI; + R.Kind = toRefKind(LocAndRole.second); + Refs.insert(*ID, R); } } } diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index f526ec71c..6f10953da 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "Annotations.h" +#include "AST.h" #include "ClangdUnit.h" #include "TestFS.h" #include "TestTU.h" @@ -15,6 +16,7 @@ #include "index/FileIndex.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/PCHContainerOperations.h" +#include "clang/Frontend/Utils.h" #include "clang/Index/IndexSymbol.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/CompilationDatabase.h" @@ -346,6 +348,55 @@ TEST(FileIndexTest, CollectMacros) { EXPECT_TRUE(SeenSymbol); } +TEST(FileIndexTest, ReferencesInMainFileWithPreamble) { + const std::string Header = R"cpp( + class Foo {}; + )cpp"; + Annotations Main(R"cpp( + #include "foo.h" + void f() { + [[Foo]] foo; + } + )cpp"); + auto MainFile = testPath("foo.cpp"); + auto HeaderFile = testPath("foo.h"); + std::vector Cmd = {"clang", "-xc++", MainFile.c_str()}; + // Preparse ParseInputs. + ParseInputs PI; + PI.CompileCommand.Directory = testRoot(); + PI.CompileCommand.Filename = MainFile; + PI.CompileCommand.CommandLine = {Cmd.begin(), Cmd.end()}; + PI.Contents = Main.code(); + PI.FS = buildTestFS({{MainFile, Main.code()}, {HeaderFile, Header}}); + + // Prepare preamble. + auto CI = buildCompilerInvocation(PI); + auto PreambleData = buildPreamble( + MainFile, + *buildCompilerInvocation(PI), /*OldPreamble=*/nullptr, + tooling::CompileCommand(), PI, + std::make_shared(), /*StoreInMemory=*/true, + [&](ASTContext &Ctx, std::shared_ptr PP) {}); + // Build AST for main file with preamble. + auto AST = ParsedAST::build( + createInvocationFromCommandLine(Cmd), PreambleData, + llvm::MemoryBuffer::getMemBufferCopy(Main.code()), + std::make_shared(), + PI.FS); + ASSERT_TRUE(AST); + FileIndex Index; + Index.updateMain(MainFile, *AST); + + auto Foo = + findSymbol(TestTU::withHeaderCode(Header).headerSymbols(), "Foo"); + RefsRequest Request; + Request.IDs.insert(Foo.ID); + + // Expect to see references in main file, references in headers are excluded + // because we only index main AST. + EXPECT_THAT(getRefs(Index, Foo.ID), RefsAre({RefRange(Main.range())})); +} + } // namespace } // namespace clangd } // namespace clang From 7dbee93fb50539bb4fa73025bedf549a8073af94 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Mon, 15 Oct 2018 12:32:49 +0000 Subject: [PATCH 362/686] [clangd] dump xrefs information in dexp tool. Reviewers: sammccall Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D53019 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344508 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/dexp/CMakeLists.txt | 2 + clangd/index/dex/dexp/Dexp.cpp | 100 ++++++++++++++++++++++++--- 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/clangd/index/dex/dexp/CMakeLists.txt b/clangd/index/dex/dexp/CMakeLists.txt index ece339d70..c73ac4d49 100644 --- a/clangd/index/dex/dexp/CMakeLists.txt +++ b/clangd/index/dex/dexp/CMakeLists.txt @@ -1,3 +1,5 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../..) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../) set(LLVM_LINK_COMPONENTS diff --git a/clangd/index/dex/dexp/Dexp.cpp b/clangd/index/dex/dexp/Dexp.cpp index a61e0d005..f02a03930 100644 --- a/clangd/index/dex/dexp/Dexp.cpp +++ b/clangd/index/dex/dexp/Dexp.cpp @@ -12,8 +12,9 @@ // //===----------------------------------------------------------------------===// -#include "../../Serialization.h" -#include "../Dex.h" +#include "Dex.h" +#include "Serialization.h" +#include "SourceCode.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" @@ -52,6 +53,26 @@ void reportTime(StringRef Name, llvm::function_ref F) { llvm::outs() << llvm::formatv("{0} took {1:ms+n}.\n", Name, Duration); } +std::vector +getSymbolIDsFromIndex(llvm::StringRef QualifiedName, const SymbolIndex *Index) { + FuzzyFindRequest Request; + // Remove leading "::" qualifier as FuzzyFind doesn't need leading "::" + // qualifier for global scope. + bool IsGlobalScope = QualifiedName.consume_front("::"); + auto Names = clang::clangd::splitQualifiedName(QualifiedName); + if (IsGlobalScope || !Names.first.empty()) + Request.Scopes = {Names.first}; + + Request.Query = Names.second; + std::vector SymIDs; + Index->fuzzyFind(Request, [&](const Symbol &Sym) { + std::string SymQualifiedName = (Sym.Scope + Sym.Name).str(); + if (QualifiedName == SymQualifiedName) + SymIDs.push_back(Sym.ID); + }); + return SymIDs; +} + // REPL commands inherit from Command and contain their options as members. // Creating a Command populates parser options, parseAndRun() resets them. class Command { @@ -88,7 +109,6 @@ class Command { }; // FIXME(kbobyrev): Ideas for more commands: -// * find symbol references: print set of reference locations // * load/swap/reload index: this would make it possible to get rid of llvm::cl // usages in the tool driver and actually use llvm::cl library in the REPL. // * show posting list density histogram (our dump data somewhere so that user @@ -139,19 +159,32 @@ class Lookup : public Command { cl::opt ID{ "id", cl::Positional, - cl::Required, cl::desc("Symbol ID to look up (hex)"), }; + cl::opt Name{ + "name", cl::desc("Qualified name to look up."), + }; void run() override { - auto SID = clang::clangd::SymbolID::fromStr(ID); - if (!SID) { - llvm::outs() << llvm::toString(SID.takeError()) << "\n"; + if (ID.getNumOccurrences() == 0 && Name.getNumOccurrences() == 0) { + llvm::outs() + << "Missing required argument: please provide id or -name.\n"; return; } + std::vector IDs; + if (ID.getNumOccurrences()) { + auto SID = clang::clangd::SymbolID::fromStr(ID); + if (!SID) { + llvm::outs() << llvm::toString(SID.takeError()) << "\n"; + return; + } + IDs.push_back(*SID); + } else { + IDs = getSymbolIDsFromIndex(Name, Index); + } clang::clangd::LookupRequest Request; - Request.IDs = {*SID}; + Request.IDs.insert(IDs.begin(), IDs.end()); bool FoundSymbol = false; Index->lookup(Request, [&](const Symbol &Sym) { FoundSymbol = true; @@ -162,13 +195,62 @@ class Lookup : public Command { } }; +class Refs : public Command { + cl::opt ID{ + "id", cl::Positional, + cl::desc("Symbol ID of the symbol being queried (hex)."), + }; + cl::opt Name{ + "name", cl::desc("Qualified name of the symbol being queried."), + }; + cl::opt Filter{ + "filter", cl::init(".*"), + cl::desc( + "Print all results from files matching this regular expression."), + }; + + void run() override { + if (ID.getNumOccurrences() == 0 && Name.getNumOccurrences() == 0) { + llvm::outs() + << "Missing required argument: please provide id or -name.\n"; + return; + } + std::vector IDs; + if (ID.getNumOccurrences()) { + auto SID = clang::clangd::SymbolID::fromStr(ID); + if (!SID) { + llvm::outs() << llvm::toString(SID.takeError()) << "\n"; + return; + } + IDs.push_back(*SID); + } else { + IDs = getSymbolIDsFromIndex(Name, Index); + } + clang::clangd::RefsRequest RefRequest; + RefRequest.IDs.insert(IDs.begin(), IDs.end()); + llvm::Regex RegexFilter(Filter); + Index->refs(RefRequest, [&RegexFilter](const clang::clangd::Ref &R) { + auto U = clang::clangd::URI::parse(R.Location.FileURI); + if (!U) { + llvm::outs() << U.takeError(); + return; + } + if (RegexFilter.match(U->body())) + llvm::outs() << R << "\n"; + }); + } +}; + struct { const char *Name; const char *Description; std::function()> Implementation; } CommandInfo[] = { {"find", "Search for symbols with fuzzyFind", llvm::make_unique}, - {"lookup", "Dump symbol details by ID", llvm::make_unique}, + {"lookup", "Dump symbol details by ID or qualified name", + llvm::make_unique}, + {"refs", "Find references by ID or qualified name", + llvm::make_unique}, }; } // namespace From 49f7f50ae5ce1995fbb4188a394543f76ba41ed6 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Mon, 15 Oct 2018 12:39:45 +0000 Subject: [PATCH 363/686] [clangd] Remove an unused include header, NFC. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344510 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Merge.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clangd/index/Merge.cpp b/clangd/index/Merge.cpp index 1dc2c9319..5e3598111 100644 --- a/clangd/index/Merge.cpp +++ b/clangd/index/Merge.cpp @@ -13,7 +13,6 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/raw_ostream.h" -#include namespace clang { namespace clangd { From b75674b479216c094231ff46eb1de4396b5bea3e Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Mon, 15 Oct 2018 13:34:10 +0000 Subject: [PATCH 364/686] [clangd] Minimal implementation of automatic static index (not enabled). Summary: See tinyurl.com/clangd-automatic-index for design and goals. Lots of limitations to keep this patch smallish, TODOs everywhere: - no serialization to disk - no changes to dynamic index, which now has a much simpler job - no partitioning of symbols by file to avoid duplication of header symbols - no reindexing of edited files - only a single worker thread - compilation database is slurped synchronously (doesn't scale) - uses memindex, rebuilds after every file (should be dex, periodically) It's not hooked up to ClangdServer/ClangdLSPServer yet: the layering isn't clear (it should really be in ClangdServer, but ClangdLSPServer has all the CDB interactions). Reviewers: ioeric Subscribers: mgorny, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, jfb, cfe-commits Differential Revision: https://reviews.llvm.org/D53032 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344513 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 1 + clangd/index/Background.cpp | 191 ++++++++++++++++++++++ clangd/index/Background.h | 79 +++++++++ unittests/clangd/BackgroundIndexTests.cpp | 37 +++++ unittests/clangd/CMakeLists.txt | 1 + unittests/clangd/SyncAPI.cpp | 12 ++ unittests/clangd/SyncAPI.h | 5 +- 7 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 clangd/index/Background.cpp create mode 100644 clangd/index/Background.h create mode 100644 unittests/clangd/BackgroundIndexTests.cpp diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index bbe8df007..4eeaffcbf 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -38,6 +38,7 @@ add_clang_library(clangDaemon URI.cpp XRefs.cpp + index/Background.cpp index/CanonicalIncludes.cpp index/FileIndex.cpp index/Index.cpp diff --git a/clangd/index/Background.cpp b/clangd/index/Background.cpp new file mode 100644 index 000000000..6e137950d --- /dev/null +++ b/clangd/index/Background.cpp @@ -0,0 +1,191 @@ +//===-- Background.cpp - Build an index in a background thread ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "index/Background.h" +#include "ClangdUnit.h" +#include "Compiler.h" +#include "Logger.h" +#include "Trace.h" +#include "index/IndexAction.h" +#include "index/MemIndex.h" +#include "index/Serialization.h" +#include "llvm/Support/SHA1.h" +#include + +using namespace llvm; +namespace clang { +namespace clangd { + +BackgroundIndex::BackgroundIndex(Context BackgroundContext, + StringRef ResourceDir, + const FileSystemProvider &FSProvider) + : SwapIndex(llvm::make_unique()), ResourceDir(ResourceDir), + FSProvider(FSProvider), BackgroundContext(std::move(BackgroundContext)), + Thread([this] { run(); }) {} + +BackgroundIndex::~BackgroundIndex() { + stop(); + Thread.join(); +} + +void BackgroundIndex::stop() { + { + std::lock_guard Lock(QueueMu); + ShouldStop = true; + } + QueueCV.notify_all(); +} + +void BackgroundIndex::run() { + WithContext Background(std::move(BackgroundContext)); + while (true) { + llvm::Optional Task; + { + std::unique_lock Lock(QueueMu); + QueueCV.wait(Lock, [&] { return ShouldStop || !Queue.empty(); }); + if (ShouldStop) { + Queue.clear(); + QueueCV.notify_all(); + return; + } + ++NumActiveTasks; + Task = std::move(Queue.front()); + Queue.pop_front(); + } + (*Task)(); + { + std::unique_lock Lock(QueueMu); + assert(NumActiveTasks > 0 && "before decrementing"); + --NumActiveTasks; + } + QueueCV.notify_all(); + } +} + +void BackgroundIndex::blockUntilIdleForTest() { + std::unique_lock Lock(QueueMu); + QueueCV.wait(Lock, [&] { return Queue.empty() && NumActiveTasks == 0; }); +} + +void BackgroundIndex::enqueue(StringRef Directory, + tooling::CompileCommand Cmd) { + std::lock_guard Lock(QueueMu); + enqueueLocked(std::move(Cmd)); +} + +void BackgroundIndex::enqueueAll(StringRef Directory, + const tooling::CompilationDatabase &CDB) { + trace::Span Tracer("BackgroundIndexEnqueueCDB"); + // FIXME: this function may be slow. Perhaps enqueue a task to re-read the CDB + // from disk and enqueue the commands asynchronously? + auto Cmds = CDB.getAllCompileCommands(); + SPAN_ATTACH(Tracer, "commands", int64_t(Cmds.size())); + std::mt19937 Generator(std::random_device{}()); + std::shuffle(Cmds.begin(), Cmds.end(), Generator); + log("Enqueueing {0} commands for indexing from {1}", Cmds.size(), Directory); + { + std::lock_guard Lock(QueueMu); + for (auto &Cmd : Cmds) + enqueueLocked(std::move(Cmd)); + } + QueueCV.notify_all(); +} + +void BackgroundIndex::enqueueLocked(tooling::CompileCommand Cmd) { + Queue.push_back(Bind( + [this](tooling::CompileCommand Cmd) { + std::string Filename = Cmd.Filename; + Cmd.CommandLine.push_back("-resource-dir=" + ResourceDir); + if (auto Error = index(std::move(Cmd))) + log("Indexing {0} failed: {1}", Filename, std::move(Error)); + }, + std::move(Cmd))); +} + +llvm::Error BackgroundIndex::index(tooling::CompileCommand Cmd) { + trace::Span Tracer("BackgroundIndex"); + SPAN_ATTACH(Tracer, "file", Cmd.Filename); + SmallString<128> AbsolutePath; + if (llvm::sys::path::is_absolute(Cmd.Filename)) { + AbsolutePath = Cmd.Filename; + } else { + AbsolutePath = Cmd.Directory; + llvm::sys::path::append(AbsolutePath, Cmd.Filename); + } + + auto FS = FSProvider.getFileSystem(); + auto Buf = FS->getBufferForFile(AbsolutePath); + if (!Buf) + return errorCodeToError(Buf.getError()); + StringRef Contents = Buf->get()->getBuffer(); + auto Hash = SHA1::hash({(const uint8_t *)Contents.data(), Contents.size()}); + + if (FileHash.lookup(AbsolutePath) == Hash) { + vlog("No need to index {0}, already up to date", AbsolutePath); + return Error::success(); + } + + log("Indexing {0}", Cmd.Filename, toHex(Hash)); + ParseInputs Inputs; + Inputs.FS = std::move(FS); + Inputs.FS->setCurrentWorkingDirectory(Cmd.Directory); + Inputs.CompileCommand = std::move(Cmd); + auto CI = buildCompilerInvocation(Inputs); + if (!CI) + return createStringError(llvm::inconvertibleErrorCode(), + "Couldn't build compiler invocation"); + IgnoreDiagnostics IgnoreDiags; + auto Clang = prepareCompilerInstance( + std::move(CI), /*Preamble=*/nullptr, std::move(*Buf), + std::make_shared(), Inputs.FS, IgnoreDiags); + if (!Clang) + return createStringError(llvm::inconvertibleErrorCode(), + "Couldn't build compiler instance"); + + SymbolCollector::Options IndexOpts; + SymbolSlab Symbols; + RefSlab Refs; + IndexFileIn IndexData; + auto Action = createStaticIndexingAction( + IndexOpts, [&](SymbolSlab S) { Symbols = std::move(S); }, + [&](RefSlab R) { Refs = std::move(R); }); + + // We're going to run clang here, and it could potentially crash. + // We could use CrashRecoveryContext to try to make indexing crashes nonfatal, + // but the leaky "recovery" is pretty scary too in a long-running process. + // If crashes are a real problem, maybe we should fork a child process. + + const FrontendInputFile &Input = Clang->getFrontendOpts().Inputs.front(); + if (!Action->BeginSourceFile(*Clang, Input)) + return createStringError(llvm::inconvertibleErrorCode(), + "BeginSourceFile() failed"); + if (!Action->Execute()) + return createStringError(llvm::inconvertibleErrorCode(), + "Execute() failed"); + Action->EndSourceFile(); + + log("Indexed {0} ({1} symbols, {2} refs)", Inputs.CompileCommand.Filename, + Symbols.size(), Refs.size()); + SPAN_ATTACH(Tracer, "symbols", int(Symbols.size())); + SPAN_ATTACH(Tracer, "refs", int(Refs.size())); + // FIXME: partition the symbols by file rather than TU, to avoid duplication. + IndexedSymbols.update(AbsolutePath, + llvm::make_unique(std::move(Symbols)), + llvm::make_unique(std::move(Refs))); + FileHash[AbsolutePath] = Hash; + + // FIXME: this should rebuild once-in-a-while, not after every file. + // At that point we should use Dex, too. + vlog("Rebuilding automatic index"); + reset(IndexedSymbols.buildMemIndex()); + return Error::success(); +} + +} // namespace clangd +} // namespace clang diff --git a/clangd/index/Background.h b/clangd/index/Background.h new file mode 100644 index 000000000..535e12d30 --- /dev/null +++ b/clangd/index/Background.h @@ -0,0 +1,79 @@ +//===--- Background.h - Build an index in a background thread ----*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_BACKGROUND_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_BACKGROUND_H + +#include "Context.h" +#include "FSProvider.h" +#include "index/FileIndex.h" +#include "index/Index.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "llvm/Support/SHA1.h" +#include +#include +#include + +namespace clang { +namespace clangd { + +// Builds an in-memory index by by running the static indexer action over +// all commands in a compilation database. Indexing happens in the background. +// FIXME: it should also persist its state on disk for fast start. +// FIXME: it should watch for changes to files on disk. +class BackgroundIndex : public SwapIndex { +public: + // FIXME: resource-dir injection should be hoisted somewhere common. + BackgroundIndex(Context BackgroundContext, StringRef ResourceDir, + const FileSystemProvider &); + ~BackgroundIndex(); // Blocks while the current task finishes. + + // Enqueue a translation unit for indexing. + // The indexing happens in a background thread, so the symbols will be + // available sometime later. + void enqueue(llvm::StringRef Directory, tooling::CompileCommand); + // Index all TUs described in the compilation database. + void enqueueAll(llvm::StringRef Directory, + const tooling::CompilationDatabase &); + + // Cause background threads to stop after ther current task, any remaining + // tasks will be discarded. + void stop(); + + // Wait until the queue is empty, to allow deterministic testing. + void blockUntilIdleForTest(); + +private: + // configuration + std::string ResourceDir; + const FileSystemProvider &FSProvider; + Context BackgroundContext; + + // index state + llvm::Error index(tooling::CompileCommand); + FileSymbols IndexedSymbols; // Index contents. + using Hash = decltype(llvm::SHA1::hash({})); + llvm::StringMap FileHash; // Digest of indexed file. + + // queue management + using Task = std::function; // FIXME: use multiple worker threads. + void run(); // Main loop executed by Thread. Runs tasks from Queue. + void enqueueLocked(tooling::CompileCommand Cmd); + std::thread Thread; + std::mutex QueueMu; + unsigned NumActiveTasks = 0; // Only idle when queue is empty *and* no tasks. + std::condition_variable QueueCV; + bool ShouldStop = false; + std::deque Queue; +}; + +} // namespace clangd +} // namespace clang + +#endif diff --git a/unittests/clangd/BackgroundIndexTests.cpp b/unittests/clangd/BackgroundIndexTests.cpp new file mode 100644 index 000000000..93bf62bba --- /dev/null +++ b/unittests/clangd/BackgroundIndexTests.cpp @@ -0,0 +1,37 @@ +#include "SyncAPI.h" +#include "TestFS.h" +#include "index/Background.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using testing::UnorderedElementsAre; + +namespace clang { +namespace clangd { + +MATCHER_P(Named, N, "") { return arg.Name == N; } + +TEST(BackgroundIndexTest, IndexTwoFiles) { + MockFSProvider FS; + // a.h yields different symbols when included by A.cc vs B.cc. + // Currently we store symbols for each TU, so we get both. + FS.Files[testPath("root/A.h")] = "void a_h(); void NAME(){}"; + FS.Files[testPath("root/A.cc")] = "#include \"A.h\""; + FS.Files[testPath("root/B.cc")] = "#define NAME bar\n#include \"A.h\""; + BackgroundIndex Idx(Context::empty(), "", FS); + + tooling::CompileCommand Cmd; + Cmd.Filename = testPath("root/A.cc"); + Cmd.Directory = testPath("root"); + Cmd.CommandLine = {"clang++", "-DNAME=foo", testPath("root/A.cc")}; + Idx.enqueue(testPath("root"), Cmd); + Cmd.CommandLine.back() = Cmd.Filename = testPath("root/B.cc"); + Idx.enqueue(testPath("root"), Cmd); + + Idx.blockUntilIdleForTest(); + EXPECT_THAT(runFuzzyFind(Idx, ""), + UnorderedElementsAre(Named("a_h"), Named("foo"), Named("bar"))); +} + +} // namespace clangd +} // namespace clang diff --git a/unittests/clangd/CMakeLists.txt b/unittests/clangd/CMakeLists.txt index 1c98347be..85eb8b290 100644 --- a/unittests/clangd/CMakeLists.txt +++ b/unittests/clangd/CMakeLists.txt @@ -10,6 +10,7 @@ include_directories( add_extra_unittest(ClangdTests Annotations.cpp + BackgroundIndexTests.cpp CancellationTests.cpp ClangdTests.cpp ClangdUnitTests.cpp diff --git a/unittests/clangd/SyncAPI.cpp b/unittests/clangd/SyncAPI.cpp index a033db930..0cc9ffba3 100644 --- a/unittests/clangd/SyncAPI.cpp +++ b/unittests/clangd/SyncAPI.cpp @@ -125,5 +125,17 @@ runDocumentSymbols(ClangdServer &Server, PathRef File) { return std::move(*Result); } +SymbolSlab runFuzzyFind(const SymbolIndex &Index, StringRef Query) { + FuzzyFindRequest Req; + Req.Query = Query; + return runFuzzyFind(Index, Req); +} + +SymbolSlab runFuzzyFind(const SymbolIndex &Index, const FuzzyFindRequest &Req) { + SymbolSlab::Builder Builder; + Index.fuzzyFind(Req, [&](const Symbol &Sym) { Builder.insert(Sym); }); + return std::move(Builder).build(); +} + } // namespace clangd } // namespace clang diff --git a/unittests/clangd/SyncAPI.h b/unittests/clangd/SyncAPI.h index 4bb65c670..7de4e22e6 100644 --- a/unittests/clangd/SyncAPI.h +++ b/unittests/clangd/SyncAPI.h @@ -17,7 +17,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_SYNCAPI_H #include "ClangdServer.h" -#include +#include "index/Index.h" namespace clang { namespace clangd { @@ -50,6 +50,9 @@ runWorkspaceSymbols(ClangdServer &Server, StringRef Query, int Limit); llvm::Expected> runDocumentSymbols(ClangdServer &Server, PathRef File); +SymbolSlab runFuzzyFind(const SymbolIndex &Index, StringRef Query); +SymbolSlab runFuzzyFind(const SymbolIndex &Index, const FuzzyFindRequest &Req); + } // namespace clangd } // namespace clang From 8ec5c3257ff67ace0fd07b4e3b6192cc4e7631ab Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Mon, 15 Oct 2018 15:04:03 +0000 Subject: [PATCH 365/686] [clangd] Use SyncAPI in more places in tests. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344520 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Index.h | 3 + unittests/clangd/FileIndexTests.cpp | 136 ++++++++++------------------ 2 files changed, 53 insertions(+), 86 deletions(-) diff --git a/clangd/index/Index.h b/clangd/index/Index.h index 06de5d445..9f0937581 100644 --- a/clangd/index/Index.h +++ b/clangd/index/Index.h @@ -286,6 +286,7 @@ class SymbolSlab { public: using const_iterator = std::vector::const_iterator; using iterator = const_iterator; + using value_type = Symbol; SymbolSlab() = default; @@ -294,6 +295,7 @@ class SymbolSlab { const_iterator find(const SymbolID &SymID) const; size_t size() const { return Symbols.size(); } + bool empty() const { return Symbols.empty(); } // Estimates the total memory usage. size_t bytes() const { return sizeof(*this) + Arena.getTotalMemory() + @@ -389,6 +391,7 @@ class RefSlab { const_iterator begin() const { return Refs.begin(); } const_iterator end() const { return Refs.end(); } size_t size() const { return Refs.size(); } + bool empty() const { return Refs.empty(); } size_t bytes() const { return sizeof(*this) + Arena.getTotalMemory() + diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index 6f10953da..288eab5f1 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -7,12 +7,12 @@ // //===----------------------------------------------------------------------===// -#include "Annotations.h" #include "AST.h" +#include "Annotations.h" #include "ClangdUnit.h" +#include "SyncAPI.h" #include "TestFS.h" #include "TestTU.h" -#include "gmock/gmock.h" #include "index/FileIndex.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/PCHContainerOperations.h" @@ -20,11 +20,14 @@ #include "clang/Index/IndexSymbol.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/CompilationDatabase.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" using testing::_; using testing::AllOf; +using testing::Contains; using testing::ElementsAre; +using testing::IsEmpty; using testing::Pair; using testing::UnorderedElementsAre; @@ -35,6 +38,8 @@ MATCHER_P(RefRange, Range, "") { Range.end.character); } MATCHER_P(FileURI, F, "") { return arg.Location.FileURI == F; } +MATCHER_P(DeclURI, U, "") { return arg.CanonicalDeclaration.FileURI == U; } +MATCHER_P(QName, N, "") { return (arg.Scope + arg.Name).str() == N; } namespace clang { namespace clangd { @@ -67,15 +72,6 @@ std::unique_ptr refSlab(const SymbolID &ID, llvm::StringRef Path) { return llvm::make_unique(std::move(Slab).build()); } -std::vector getSymbolNames(const SymbolIndex &I, - std::string Query = "") { - FuzzyFindRequest Req; - Req.Query = Query; - std::vector Names; - I.fuzzyFind(Req, [&](const Symbol &S) { Names.push_back(S.Name); }); - return Names; -} - RefSlab getRefs(const SymbolIndex &I, SymbolID ID) { RefsRequest Req; Req.IDs = {ID}; @@ -86,11 +82,11 @@ RefSlab getRefs(const SymbolIndex &I, SymbolID ID) { TEST(FileSymbolsTest, UpdateAndGet) { FileSymbols FS; - EXPECT_THAT(getSymbolNames(*FS.buildMemIndex()), UnorderedElementsAre()); + EXPECT_THAT(runFuzzyFind(*FS.buildMemIndex(), ""), IsEmpty()); FS.update("f1", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cc")); - EXPECT_THAT(getSymbolNames(*FS.buildMemIndex()), - UnorderedElementsAre("1", "2", "3")); + EXPECT_THAT(runFuzzyFind(*FS.buildMemIndex(), ""), + UnorderedElementsAre(QName("1"), QName("2"), QName("3"))); EXPECT_THAT(getRefs(*FS.buildMemIndex(), SymbolID("1")), RefsAre({FileURI("f1.cc")})); } @@ -99,8 +95,9 @@ TEST(FileSymbolsTest, Overlap) { FileSymbols FS; FS.update("f1", numSlab(1, 3), nullptr); FS.update("f2", numSlab(3, 5), nullptr); - EXPECT_THAT(getSymbolNames(*FS.buildMemIndex()), - UnorderedElementsAre("1", "2", "3", "4", "5")); + EXPECT_THAT(runFuzzyFind(*FS.buildMemIndex(), ""), + UnorderedElementsAre(QName("1"), QName("2"), QName("3"), + QName("4"), QName("5"))); } TEST(FileSymbolsTest, SnapshotAliveAfterRemove) { @@ -110,27 +107,20 @@ TEST(FileSymbolsTest, SnapshotAliveAfterRemove) { FS.update("f1", numSlab(1, 3), refSlab(ID, "f1.cc")); auto Symbols = FS.buildMemIndex(); - EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3")); + EXPECT_THAT(runFuzzyFind(*Symbols, ""), + UnorderedElementsAre(QName("1"), QName("2"), QName("3"))); EXPECT_THAT(getRefs(*Symbols, ID), RefsAre({FileURI("f1.cc")})); FS.update("f1", nullptr, nullptr); auto Empty = FS.buildMemIndex(); - EXPECT_THAT(getSymbolNames(*Empty), UnorderedElementsAre()); + EXPECT_THAT(runFuzzyFind(*Empty, ""), IsEmpty()); EXPECT_THAT(getRefs(*Empty, ID), ElementsAre()); - EXPECT_THAT(getSymbolNames(*Symbols), UnorderedElementsAre("1", "2", "3")); + EXPECT_THAT(runFuzzyFind(*Symbols, ""), + UnorderedElementsAre(QName("1"), QName("2"), QName("3"))); EXPECT_THAT(getRefs(*Symbols, ID), RefsAre({FileURI("f1.cc")})); } -std::vector match(const SymbolIndex &I, - const FuzzyFindRequest &Req) { - std::vector Matches; - I.fuzzyFind(Req, [&](const Symbol &Sym) { - Matches.push_back((Sym.Scope + Sym.Name).str()); - }); - return Matches; -} - // Adds Basename.cpp, which includes Basename.h, which contains Code. void update(FileIndex &M, llvm::StringRef Basename, llvm::StringRef Code) { TestTU File; @@ -146,14 +136,7 @@ TEST(FileIndexTest, CustomizedURIScheme) { FileIndex M({"unittest"}); update(M, "f", "class string {};"); - FuzzyFindRequest Req; - Req.Query = ""; - bool SeenSymbol = false; - M.fuzzyFind(Req, [&](const Symbol &Sym) { - EXPECT_EQ(Sym.CanonicalDeclaration.FileURI, "unittest:///f.h"); - SeenSymbol = true; - }); - EXPECT_TRUE(SeenSymbol); + EXPECT_THAT(runFuzzyFind(M, ""), ElementsAre(DeclURI("unittest:///f.h"))); } TEST(FileIndexTest, IndexAST) { @@ -163,16 +146,17 @@ TEST(FileIndexTest, IndexAST) { FuzzyFindRequest Req; Req.Query = ""; Req.Scopes = {"ns::"}; - EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns::f", "ns::X")); + EXPECT_THAT(runFuzzyFind(M, Req), + UnorderedElementsAre(QName("ns::f"), QName("ns::X"))); } TEST(FileIndexTest, NoLocal) { FileIndex M; update(M, "f1", "namespace ns { void f() { int local = 0; } class X {}; }"); - FuzzyFindRequest Req; - Req.Query = ""; - EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns", "ns::f", "ns::X")); + EXPECT_THAT( + runFuzzyFind(M, ""), + UnorderedElementsAre(QName("ns"), QName("ns::f"), QName("ns::X"))); } TEST(FileIndexTest, IndexMultiASTAndDeduplicate) { @@ -181,33 +165,28 @@ TEST(FileIndexTest, IndexMultiASTAndDeduplicate) { update(M, "f2", "namespace ns { void ff() {} class X {}; }"); FuzzyFindRequest Req; - Req.Query = ""; Req.Scopes = {"ns::"}; - EXPECT_THAT(match(M, Req), UnorderedElementsAre("ns::f", "ns::X", "ns::ff")); + EXPECT_THAT( + runFuzzyFind(M, Req), + UnorderedElementsAre(QName("ns::f"), QName("ns::X"), QName("ns::ff"))); } TEST(FileIndexTest, ClassMembers) { FileIndex M; update(M, "f1", "class X { static int m1; int m2; static void f(); };"); - FuzzyFindRequest Req; - Req.Query = ""; - EXPECT_THAT(match(M, Req), - UnorderedElementsAre("X", "X::m1", "X::m2", "X::f")); + EXPECT_THAT(runFuzzyFind(M, ""), + UnorderedElementsAre(QName("X"), QName("X::m1"), QName("X::m2"), + QName("X::f"))); } TEST(FileIndexTest, NoIncludeCollected) { FileIndex M; update(M, "f", "class string {};"); - FuzzyFindRequest Req; - Req.Query = ""; - bool SeenSymbol = false; - M.fuzzyFind(Req, [&](const Symbol &Sym) { - EXPECT_TRUE(Sym.IncludeHeaders.empty()); - SeenSymbol = true; - }); - EXPECT_TRUE(SeenSymbol); + auto Symbols = runFuzzyFind(M, ""); + EXPECT_THAT(Symbols, ElementsAre(_)); + EXPECT_THAT(Symbols.begin()->IncludeHeaders, IsEmpty()); } TEST(FileIndexTest, TemplateParamsInLabel) { @@ -223,26 +202,20 @@ vector make_vector(Arg A) {} FileIndex M; update(M, "f", Source); - FuzzyFindRequest Req; - Req.Query = ""; - bool SeenVector = false; - bool SeenMakeVector = false; - M.fuzzyFind(Req, [&](const Symbol &Sym) { - if (Sym.Name == "vector") { - EXPECT_EQ(Sym.Signature, ""); - EXPECT_EQ(Sym.CompletionSnippetSuffix, "<${1:class Ty}>"); - SeenVector = true; - return; - } + auto Symbols = runFuzzyFind(M, ""); + EXPECT_THAT(Symbols, + UnorderedElementsAre(QName("vector"), QName("make_vector"))); + auto It = Symbols.begin(); + Symbol Vector = *It++; + Symbol MakeVector = *It++; + if (MakeVector.Name == "vector") + std::swap(MakeVector, Vector); - if (Sym.Name == "make_vector") { - EXPECT_EQ(Sym.Signature, "(Arg A)"); - EXPECT_EQ(Sym.CompletionSnippetSuffix, "<${1:class Ty}>(${2:Arg A})"); - SeenMakeVector = true; - } - }); - EXPECT_TRUE(SeenVector); - EXPECT_TRUE(SeenMakeVector); + EXPECT_EQ(Vector.Signature, ""); + EXPECT_EQ(Vector.CompletionSnippetSuffix, "<${1:class Ty}>"); + + EXPECT_EQ(MakeVector.Signature, "(Arg A)"); + EXPECT_EQ(MakeVector.CompletionSnippetSuffix, "<${1:class Ty}>(${2:Arg A})"); } TEST(FileIndexTest, RebuildWithPreamble) { @@ -291,9 +264,9 @@ TEST(FileIndexTest, RebuildWithPreamble) { Req.Query = ""; Req.Scopes = {"", "ns_in_header::"}; - EXPECT_THAT( - match(Index, Req), - UnorderedElementsAre("ns_in_header", "ns_in_header::func_in_header")); + EXPECT_THAT(runFuzzyFind(Index, Req), + UnorderedElementsAre(QName("ns_in_header"), + QName("ns_in_header::func_in_header"))); } TEST(FileIndexTest, Refs) { @@ -336,16 +309,7 @@ TEST(FileIndexTest, Refs) { TEST(FileIndexTest, CollectMacros) { FileIndex M; update(M, "f", "#define CLANGD 1"); - - FuzzyFindRequest Req; - Req.Query = ""; - bool SeenSymbol = false; - M.fuzzyFind(Req, [&](const Symbol &Sym) { - EXPECT_EQ(Sym.Name, "CLANGD"); - EXPECT_EQ(Sym.SymInfo.Kind, index::SymbolKind::Macro); - SeenSymbol = true; - }); - EXPECT_TRUE(SeenSymbol); + EXPECT_THAT(runFuzzyFind(M, ""), Contains(QName("CLANGD"))); } TEST(FileIndexTest, ReferencesInMainFileWithPreamble) { From 06c813ccc59af181f31e77468b0cba0ebb8bb118 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Mon, 15 Oct 2018 15:12:40 +0000 Subject: [PATCH 366/686] [clangd] Add createIndex in dexp Summary: This would allow easily injecting our internal customization. Also updates the stale "symbol-collection-file" flag. Reviewers: sammccall Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D53292 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344521 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/dexp/Dexp.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/clangd/index/dex/dexp/Dexp.cpp b/clangd/index/dex/dexp/Dexp.cpp index f02a03930..72607b998 100644 --- a/clangd/index/dex/dexp/Dexp.cpp +++ b/clangd/index/dex/dexp/Dexp.cpp @@ -31,9 +31,9 @@ using namespace llvm; namespace { llvm::cl::opt - SymbolCollection("symbol-collection-file", - llvm::cl::desc("Path to the file with symbol collection"), - llvm::cl::Positional, llvm::cl::Required); + IndexPath("index-path", + llvm::cl::desc("Path to the index"), + llvm::cl::Positional, llvm::cl::Required); static const std::string Overview = R"( This is an **experimental** interactive tool to process user-provided search @@ -253,6 +253,10 @@ struct { llvm::make_unique}, }; +std::unique_ptr openIndex(llvm::StringRef Index) { + return loadIndex(Index, /*URISchemes=*/{}, /*UseDex=*/true); +} + } // namespace int main(int argc, const char *argv[]) { @@ -262,13 +266,11 @@ int main(int argc, const char *argv[]) { std::unique_ptr Index; reportTime("Dex build", [&]() { - Index = loadIndex(SymbolCollection, /*URISchemes=*/{}, - /*UseDex=*/true); + Index = openIndex(IndexPath); }); if (!Index) { - llvm::outs() - << "ERROR: Please provide a valid path to symbol collection file.\n"; + llvm::outs() << "Failed to open the index.\n"; return -1; } From e2f46822cef6c55dfa9285e4fdfa1c4613d020bf Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Mon, 15 Oct 2018 16:47:45 +0000 Subject: [PATCH 367/686] [clangd] Revert include path change in Dexp. NFC git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344533 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/dex/dexp/CMakeLists.txt | 2 -- clangd/index/dex/dexp/Dexp.cpp | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/clangd/index/dex/dexp/CMakeLists.txt b/clangd/index/dex/dexp/CMakeLists.txt index c73ac4d49..ece339d70 100644 --- a/clangd/index/dex/dexp/CMakeLists.txt +++ b/clangd/index/dex/dexp/CMakeLists.txt @@ -1,5 +1,3 @@ -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../..) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../) set(LLVM_LINK_COMPONENTS diff --git a/clangd/index/dex/dexp/Dexp.cpp b/clangd/index/dex/dexp/Dexp.cpp index 72607b998..4926d313d 100644 --- a/clangd/index/dex/dexp/Dexp.cpp +++ b/clangd/index/dex/dexp/Dexp.cpp @@ -12,9 +12,9 @@ // //===----------------------------------------------------------------------===// -#include "Dex.h" -#include "Serialization.h" #include "SourceCode.h" +#include "index/Serialization.h" +#include "index/dex/Dex.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" From 81a876562f2be770fbbc0ae7b153e18dea932eef Mon Sep 17 00:00:00 2001 From: Leonard Chan Date: Mon, 15 Oct 2018 19:59:52 +0000 Subject: [PATCH 368/686] added fix git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344548 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../modernize/UseDefaultMemberInitCheck.cpp | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp b/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp index 374fcb34d..0e0bd36a3 100644 --- a/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp +++ b/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp @@ -60,6 +60,47 @@ static StringRef getValueOfValueInit(const QualType InitType) { case Type::STK_IntegralComplex: return getValueOfValueInit( InitType->getAs()->getElementType()); + case Type::STK_FixedPoint: + switch (InitType->getAs()->getKind()) { + case BuiltinType::ShortAccum: + case BuiltinType::SatShortAccum: + return "0.0hk"; + case BuiltinType::Accum: + case BuiltinType::SatAccum: + return "0.0k"; + case BuiltinType::LongAccum: + case BuiltinType::SatLongAccum: + return "0.0lk"; + case BuiltinType::UShortAccum: + case BuiltinType::SatUShortAccum: + return "0.0uhk"; + case BuiltinType::UAccum: + case BuiltinType::SatUAccum: + return "0.0uk"; + case BuiltinType::ULongAccum: + case BuiltinType::SatULongAccum: + return "0.0ulk"; + case BuiltinType::ShortFract: + case BuiltinType::SatShortFract: + return "0.0hr"; + case BuiltinType::Fract: + case BuiltinType::SatFract: + return "0.0r"; + case BuiltinType::LongFract: + case BuiltinType::SatLongFract: + return "0.0lr"; + case BuiltinType::UShortFract: + case BuiltinType::SatUShortFract: + return "0.0uhr"; + case BuiltinType::UFract: + case BuiltinType::SatUFract: + return "0.0ur"; + case BuiltinType::ULongFract: + case BuiltinType::SatULongFract: + return "0.0ulr"; + default: + llvm_unreachable("Unhandled fixed point BuiltinType"); + } } llvm_unreachable("Invalid scalar type kind"); } From abaecff28370d7904676e52d5d0b5bb824328b7c Mon Sep 17 00:00:00 2001 From: Leonard Chan Date: Mon, 15 Oct 2018 20:00:03 +0000 Subject: [PATCH 369/686] [Fixed Point Arithmetic] Fix for clang-tools-extra warning Fix for warnings generated on unhandled enum value `STK_FixedPoint`. Differential Revision: https://reviews.llvm.org/D53299 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344549 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../modernize/UseDefaultMemberInitCheck.cpp | 77 ++++++++++--------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp b/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp index 0e0bd36a3..23a3e1fab 100644 --- a/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp +++ b/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp @@ -60,46 +60,47 @@ static StringRef getValueOfValueInit(const QualType InitType) { case Type::STK_IntegralComplex: return getValueOfValueInit( InitType->getAs()->getElementType()); + case Type::STK_FixedPoint: switch (InitType->getAs()->getKind()) { - case BuiltinType::ShortAccum: - case BuiltinType::SatShortAccum: - return "0.0hk"; - case BuiltinType::Accum: - case BuiltinType::SatAccum: - return "0.0k"; - case BuiltinType::LongAccum: - case BuiltinType::SatLongAccum: - return "0.0lk"; - case BuiltinType::UShortAccum: - case BuiltinType::SatUShortAccum: - return "0.0uhk"; - case BuiltinType::UAccum: - case BuiltinType::SatUAccum: - return "0.0uk"; - case BuiltinType::ULongAccum: - case BuiltinType::SatULongAccum: - return "0.0ulk"; - case BuiltinType::ShortFract: - case BuiltinType::SatShortFract: - return "0.0hr"; - case BuiltinType::Fract: - case BuiltinType::SatFract: - return "0.0r"; - case BuiltinType::LongFract: - case BuiltinType::SatLongFract: - return "0.0lr"; - case BuiltinType::UShortFract: - case BuiltinType::SatUShortFract: - return "0.0uhr"; - case BuiltinType::UFract: - case BuiltinType::SatUFract: - return "0.0ur"; - case BuiltinType::ULongFract: - case BuiltinType::SatULongFract: - return "0.0ulr"; - default: - llvm_unreachable("Unhandled fixed point BuiltinType"); + case BuiltinType::ShortAccum: + case BuiltinType::SatShortAccum: + return "0.0hk"; + case BuiltinType::Accum: + case BuiltinType::SatAccum: + return "0.0k"; + case BuiltinType::LongAccum: + case BuiltinType::SatLongAccum: + return "0.0lk"; + case BuiltinType::UShortAccum: + case BuiltinType::SatUShortAccum: + return "0.0uhk"; + case BuiltinType::UAccum: + case BuiltinType::SatUAccum: + return "0.0uk"; + case BuiltinType::ULongAccum: + case BuiltinType::SatULongAccum: + return "0.0ulk"; + case BuiltinType::ShortFract: + case BuiltinType::SatShortFract: + return "0.0hr"; + case BuiltinType::Fract: + case BuiltinType::SatFract: + return "0.0r"; + case BuiltinType::LongFract: + case BuiltinType::SatLongFract: + return "0.0lr"; + case BuiltinType::UShortFract: + case BuiltinType::SatUShortFract: + return "0.0uhr"; + case BuiltinType::UFract: + case BuiltinType::SatUFract: + return "0.0ur"; + case BuiltinType::ULongFract: + case BuiltinType::SatULongFract: + return "0.0ulr"; + default: + llvm_unreachable("Unhandled fixed point BuiltinType"); } } llvm_unreachable("Invalid scalar type kind"); From 2c593fc2a7fcf37f8fddca6b3fbb2d11ca8d0614 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 16 Oct 2018 06:32:14 +0000 Subject: [PATCH 370/686] [clangd] Disable timeouting test while investigating git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344586 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/clangd/BackgroundIndexTests.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/unittests/clangd/BackgroundIndexTests.cpp b/unittests/clangd/BackgroundIndexTests.cpp index 93bf62bba..1ea75fa5c 100644 --- a/unittests/clangd/BackgroundIndexTests.cpp +++ b/unittests/clangd/BackgroundIndexTests.cpp @@ -11,6 +11,8 @@ namespace clangd { MATCHER_P(Named, N, "") { return arg.Name == N; } +// Temporarily disabled: test timing out on buildbots. +#if 0 TEST(BackgroundIndexTest, IndexTwoFiles) { MockFSProvider FS; // a.h yields different symbols when included by A.cc vs B.cc. @@ -32,6 +34,7 @@ TEST(BackgroundIndexTest, IndexTwoFiles) { EXPECT_THAT(runFuzzyFind(Idx, ""), UnorderedElementsAre(Named("a_h"), Named("foo"), Named("bar"))); } +#endif } // namespace clangd } // namespace clang From 9fb1d212a956a1f79751afa0c9ff4eeaa0e2d998 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 16 Oct 2018 08:53:52 +0000 Subject: [PATCH 371/686] [clangd] Optionally use dex for the preamble parts of the dynamic index. Summary: Reuse the old -use-dex-index experiment flag for this. To avoid breaking the tests, make Dex deduplicate symbols, addressing an old FIXME. Reviewers: hokein Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D53288 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344594 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdServer.cpp | 6 +++-- clangd/ClangdServer.h | 3 +++ clangd/index/Background.cpp | 2 +- clangd/index/FileIndex.cpp | 36 +++++++++++++++++++---------- clangd/index/FileIndex.h | 14 +++++++++-- clangd/index/dex/Dex.h | 4 +++- clangd/tool/ClangdMain.cpp | 9 ++++---- unittests/clangd/DexTests.cpp | 8 +------ unittests/clangd/FileIndexTests.cpp | 17 +++++++------- unittests/clangd/TestTU.cpp | 3 ++- 10 files changed, 64 insertions(+), 38 deletions(-) diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp index b870721c3..0dff487ab 100644 --- a/clangd/ClangdServer.cpp +++ b/clangd/ClangdServer.cpp @@ -105,8 +105,10 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB, : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider), ResourceDir(Opts.ResourceDir ? Opts.ResourceDir->str() : getStandardResourceDir()), - DynamicIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex(Opts.URISchemes) - : nullptr), + DynamicIdx(Opts.BuildDynamicSymbolIndex + ? new FileIndex(Opts.URISchemes, + Opts.HeavyweightDynamicSymbolIndex) + : nullptr), PCHs(std::make_shared()), // Pass a callback into `WorkScheduler` to extract symbols from a newly // parsed file and rebuild the file index synchronously each time an AST diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h index 9f17e73c0..cf9236068 100644 --- a/clangd/ClangdServer.h +++ b/clangd/ClangdServer.h @@ -75,6 +75,9 @@ class ClangdServer { /// If true, ClangdServer builds a dynamic in-memory index for symbols in /// opened files and uses the index to augment code completion results. bool BuildDynamicSymbolIndex = false; + /// Use a heavier and faster in-memory index implementation. + /// FIXME: we should make this true if it isn't too slow to build!. + bool HeavyweightDynamicSymbolIndex = false; /// URI schemes to use when building the dynamic index. /// If empty, the default schemes in SymbolCollector will be used. diff --git a/clangd/index/Background.cpp b/clangd/index/Background.cpp index 6e137950d..dac8bb9f9 100644 --- a/clangd/index/Background.cpp +++ b/clangd/index/Background.cpp @@ -183,7 +183,7 @@ llvm::Error BackgroundIndex::index(tooling::CompileCommand Cmd) { // FIXME: this should rebuild once-in-a-while, not after every file. // At that point we should use Dex, too. vlog("Rebuilding automatic index"); - reset(IndexedSymbols.buildMemIndex()); + reset(IndexedSymbols.buildIndex(IndexType::Light)); return Error::success(); } diff --git a/clangd/index/FileIndex.cpp b/clangd/index/FileIndex.cpp index 59bc21ff1..79eb542fd 100644 --- a/clangd/index/FileIndex.cpp +++ b/clangd/index/FileIndex.cpp @@ -13,6 +13,7 @@ #include "SymbolCollector.h" #include "index/Index.h" #include "index/Merge.h" +#include "index/dex/Dex.h" #include "clang/Index/IndexingAction.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/Preprocessor.h" @@ -98,7 +99,8 @@ void FileSymbols::update(PathRef Path, std::unique_ptr Symbols, FileToRefs[Path] = std::move(Refs); } -std::unique_ptr FileSymbols::buildMemIndex() { +std::unique_ptr +FileSymbols::buildIndex(IndexType Type, ArrayRef URISchemes) { std::vector> SymbolSlabs; std::vector> RefSlabs; { @@ -144,18 +146,27 @@ std::unique_ptr FileSymbols::buildMemIndex() { StorageSize += RefSlab->bytes(); // Index must keep the slabs and contiguous ranges alive. - return llvm::make_unique( - llvm::make_pointee_range(AllSymbols), std::move(AllRefs), - std::make_tuple(std::move(SymbolSlabs), std::move(RefSlabs), - std::move(RefsStorage)), - StorageSize); + switch (Type) { + case IndexType::Light: + return llvm::make_unique( + llvm::make_pointee_range(AllSymbols), std::move(AllRefs), + std::make_tuple(std::move(SymbolSlabs), std::move(RefSlabs), + std::move(RefsStorage)), + StorageSize); + case IndexType::Heavy: + return llvm::make_unique( + llvm::make_pointee_range(AllSymbols), std::move(AllRefs), + std::make_tuple(std::move(SymbolSlabs), std::move(RefSlabs), + std::move(RefsStorage)), + StorageSize, std::move(URISchemes)); + } } -FileIndex::FileIndex(std::vector URISchemes) - : MergedIndex(&MainFileIndex, &PreambleIndex), +FileIndex::FileIndex(std::vector URISchemes, bool UseDex) + : MergedIndex(&MainFileIndex, &PreambleIndex), UseDex(UseDex), URISchemes(std::move(URISchemes)), - PreambleIndex(PreambleSymbols.buildMemIndex()), - MainFileIndex(MainFileSymbols.buildMemIndex()) {} + PreambleIndex(llvm::make_unique()), + MainFileIndex(llvm::make_unique()) {} void FileIndex::updatePreamble(PathRef Path, ASTContext &AST, std::shared_ptr PP) { @@ -163,7 +174,8 @@ void FileIndex::updatePreamble(PathRef Path, ASTContext &AST, PreambleSymbols.update(Path, llvm::make_unique(std::move(Symbols)), llvm::make_unique()); - PreambleIndex.reset(PreambleSymbols.buildMemIndex()); + PreambleIndex.reset(PreambleSymbols.buildIndex( + UseDex ? IndexType::Heavy : IndexType::Light, URISchemes)); } void FileIndex::updateMain(PathRef Path, ParsedAST &AST) { @@ -171,7 +183,7 @@ void FileIndex::updateMain(PathRef Path, ParsedAST &AST) { MainFileSymbols.update( Path, llvm::make_unique(std::move(Contents.first)), llvm::make_unique(std::move(Contents.second))); - MainFileIndex.reset(MainFileSymbols.buildMemIndex()); + MainFileIndex.reset(MainFileSymbols.buildIndex(IndexType::Light, URISchemes)); } } // namespace clangd diff --git a/clangd/index/FileIndex.h b/clangd/index/FileIndex.h index ea24efd1e..79fb5dcfa 100644 --- a/clangd/index/FileIndex.h +++ b/clangd/index/FileIndex.h @@ -26,6 +26,14 @@ namespace clang { namespace clangd { +/// Select between in-memory index implementations, which have tradeoffs. +enum class IndexType { + // MemIndex is trivially cheap to build, but has poor query performance. + Light, + // Dex is relatively expensive to build and uses more memory, but is fast. + Heavy, +}; + /// A container of Symbols from several source files. It can be updated /// at source-file granularity, replacing all symbols from one file with a new /// set. @@ -47,7 +55,8 @@ class FileSymbols { std::unique_ptr Refs); // The index keeps the symbols alive. - std::unique_ptr buildMemIndex(); + std::unique_ptr + buildIndex(IndexType, ArrayRef URISchemes = {}); private: mutable std::mutex Mutex; @@ -64,7 +73,7 @@ class FileIndex : public MergedIndex { public: /// If URISchemes is empty, the default schemes in SymbolCollector will be /// used. - FileIndex(std::vector URISchemes = {}); + FileIndex(std::vector URISchemes = {}, bool UseDex = true); /// Update preamble symbols of file \p Path with all declarations in \p AST /// and macros in \p PP. @@ -76,6 +85,7 @@ class FileIndex : public MergedIndex { void updateMain(PathRef Path, ParsedAST &AST); private: + bool UseDex; // FIXME: this should be always on. std::vector URISchemes; // Contains information from each file's preamble only. diff --git a/clangd/index/dex/Dex.h b/clangd/index/dex/Dex.h index 8364cb5d1..4b376da38 100644 --- a/clangd/index/dex/Dex.h +++ b/clangd/index/dex/Dex.h @@ -52,8 +52,10 @@ class Dex : public SymbolIndex { SymbolCollector::Options Opts; URISchemes = Opts.URISchemes; } + llvm::DenseSet SeenIDs; for (auto &&Sym : Symbols) - this->Symbols.push_back(&Sym); + if (SeenIDs.insert(Sym.ID).second) + this->Symbols.push_back(&Sym); for (auto &&Ref : Refs) this->Refs.try_emplace(Ref.first, Ref.second); buildIndex(); diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index a7a9fddcc..dcfc6f70f 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -29,11 +29,11 @@ using namespace clang; using namespace clang::clangd; -// FIXME: remove this option when Dex is stable enough. +// FIXME: remove this option when Dex is cheap enough. static llvm::cl::opt UseDex("use-dex-index", - llvm::cl::desc("Use experimental Dex static index."), - llvm::cl::init(true), llvm::cl::Hidden); + llvm::cl::desc("Use experimental Dex dynamic index."), + llvm::cl::init(false), llvm::cl::Hidden); static llvm::cl::opt CompileCommandsDir( "compile-commands-dir", @@ -286,6 +286,7 @@ int main(int argc, char *argv[]) { if (!ResourceDir.empty()) Opts.ResourceDir = ResourceDir; Opts.BuildDynamicSymbolIndex = EnableIndex; + Opts.HeavyweightDynamicSymbolIndex = UseDex; std::unique_ptr StaticIdx; std::future AsyncIndexLoad; // Block exit while loading the index. if (EnableIndex && !IndexFile.empty()) { @@ -293,7 +294,7 @@ int main(int argc, char *argv[]) { SwapIndex *Placeholder; StaticIdx.reset(Placeholder = new SwapIndex(llvm::make_unique())); AsyncIndexLoad = runAsync([Placeholder, &Opts] { - if (auto Idx = loadIndex(IndexFile, Opts.URISchemes, UseDex)) + if (auto Idx = loadIndex(IndexFile, Opts.URISchemes, /*UseDex=*/true)) Placeholder->reset(std::move(Idx)); }); if (RunSynchronously) diff --git a/unittests/clangd/DexTests.cpp b/unittests/clangd/DexTests.cpp index 130659247..0faea3b0f 100644 --- a/unittests/clangd/DexTests.cpp +++ b/unittests/clangd/DexTests.cpp @@ -492,19 +492,13 @@ TEST(Dex, FuzzyFind) { "other::A")); } -// FIXME(kbobyrev): This test is different for Dex and MemIndex: while -// MemIndex manages response deduplication, Dex simply returns all matched -// symbols which means there might be equivalent symbols in the response. -// Before drop-in replacement of MemIndex with Dex happens, FileIndex -// should handle deduplication instead. TEST(DexTest, DexDeduplicate) { std::vector Symbols = {symbol("1"), symbol("2"), symbol("3"), symbol("2") /* duplicate */}; FuzzyFindRequest Req; Req.Query = "2"; Dex I(Symbols, RefSlab(), URISchemes); - EXPECT_FALSE(Req.Limit); - EXPECT_THAT(match(I, Req), ElementsAre("2", "2")); + EXPECT_THAT(match(I, Req), ElementsAre("2")); } TEST(DexTest, DexLimitedNumMatches) { diff --git a/unittests/clangd/FileIndexTests.cpp b/unittests/clangd/FileIndexTests.cpp index 288eab5f1..1295a5c90 100644 --- a/unittests/clangd/FileIndexTests.cpp +++ b/unittests/clangd/FileIndexTests.cpp @@ -82,12 +82,12 @@ RefSlab getRefs(const SymbolIndex &I, SymbolID ID) { TEST(FileSymbolsTest, UpdateAndGet) { FileSymbols FS; - EXPECT_THAT(runFuzzyFind(*FS.buildMemIndex(), ""), IsEmpty()); + EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), IsEmpty()); FS.update("f1", numSlab(1, 3), refSlab(SymbolID("1"), "f1.cc")); - EXPECT_THAT(runFuzzyFind(*FS.buildMemIndex(), ""), + EXPECT_THAT(runFuzzyFind(*FS.buildIndex(IndexType::Light), ""), UnorderedElementsAre(QName("1"), QName("2"), QName("3"))); - EXPECT_THAT(getRefs(*FS.buildMemIndex(), SymbolID("1")), + EXPECT_THAT(getRefs(*FS.buildIndex(IndexType::Light), SymbolID("1")), RefsAre({FileURI("f1.cc")})); } @@ -95,9 +95,10 @@ TEST(FileSymbolsTest, Overlap) { FileSymbols FS; FS.update("f1", numSlab(1, 3), nullptr); FS.update("f2", numSlab(3, 5), nullptr); - EXPECT_THAT(runFuzzyFind(*FS.buildMemIndex(), ""), - UnorderedElementsAre(QName("1"), QName("2"), QName("3"), - QName("4"), QName("5"))); + for (auto Type : {IndexType::Light, IndexType::Heavy}) + EXPECT_THAT(runFuzzyFind(*FS.buildIndex(Type), ""), + UnorderedElementsAre(QName("1"), QName("2"), QName("3"), + QName("4"), QName("5"))); } TEST(FileSymbolsTest, SnapshotAliveAfterRemove) { @@ -106,13 +107,13 @@ TEST(FileSymbolsTest, SnapshotAliveAfterRemove) { SymbolID ID("1"); FS.update("f1", numSlab(1, 3), refSlab(ID, "f1.cc")); - auto Symbols = FS.buildMemIndex(); + auto Symbols = FS.buildIndex(IndexType::Light); EXPECT_THAT(runFuzzyFind(*Symbols, ""), UnorderedElementsAre(QName("1"), QName("2"), QName("3"))); EXPECT_THAT(getRefs(*Symbols, ID), RefsAre({FileURI("f1.cc")})); FS.update("f1", nullptr, nullptr); - auto Empty = FS.buildMemIndex(); + auto Empty = FS.buildIndex(IndexType::Light); EXPECT_THAT(runFuzzyFind(*Empty, ""), IsEmpty()); EXPECT_THAT(getRefs(*Empty, ID), ElementsAre()); diff --git a/unittests/clangd/TestTU.cpp b/unittests/clangd/TestTU.cpp index f63b738c9..0ced38474 100644 --- a/unittests/clangd/TestTU.cpp +++ b/unittests/clangd/TestTU.cpp @@ -50,7 +50,8 @@ SymbolSlab TestTU::headerSymbols() const { std::unique_ptr TestTU::index() const { auto AST = build(); - auto Idx = llvm::make_unique(); + auto Idx = llvm::make_unique( + /*URISchemes=*/std::vector{}, /*UseDex=*/true); Idx->updatePreamble(Filename, AST.getASTContext(), AST.getPreprocessorPtr()); Idx->updateMain(Filename, AST); return std::move(Idx); From de03416ead74522af052a2c392110217ab910d43 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 16 Oct 2018 09:05:13 +0000 Subject: [PATCH 372/686] [clangd] Fix threading bugs in (not-yet-used) BackgroundIndex, re-enable test. Summary: One relatively boring bug: forgot to notify the CV after enqueue. One much more fun bug: the thread member could access instance variables before they were initialized. Although the thread was last in the init list, QueueCV etc were listed after Thread in the class, so their default constructors raced with the thread itself. We have to get very unlucky to lose this race, I saw it 0.02% of the time. Reviewers: ioeric Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, jfb, cfe-commits Differential Revision: https://reviews.llvm.org/D53313 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344595 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/index/Background.cpp | 7 +++++-- clangd/index/Background.h | 2 +- unittests/clangd/BackgroundIndexTests.cpp | 3 --- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clangd/index/Background.cpp b/clangd/index/Background.cpp index dac8bb9f9..f6de39549 100644 --- a/clangd/index/Background.cpp +++ b/clangd/index/Background.cpp @@ -75,8 +75,11 @@ void BackgroundIndex::blockUntilIdleForTest() { void BackgroundIndex::enqueue(StringRef Directory, tooling::CompileCommand Cmd) { - std::lock_guard Lock(QueueMu); - enqueueLocked(std::move(Cmd)); + { + std::lock_guard Lock(QueueMu); + enqueueLocked(std::move(Cmd)); + } + QueueCV.notify_all(); } void BackgroundIndex::enqueueAll(StringRef Directory, diff --git a/clangd/index/Background.h b/clangd/index/Background.h index 535e12d30..3f52423fa 100644 --- a/clangd/index/Background.h +++ b/clangd/index/Background.h @@ -65,12 +65,12 @@ class BackgroundIndex : public SwapIndex { using Task = std::function; // FIXME: use multiple worker threads. void run(); // Main loop executed by Thread. Runs tasks from Queue. void enqueueLocked(tooling::CompileCommand Cmd); - std::thread Thread; std::mutex QueueMu; unsigned NumActiveTasks = 0; // Only idle when queue is empty *and* no tasks. std::condition_variable QueueCV; bool ShouldStop = false; std::deque Queue; + std::thread Thread; // Must be last, spawned thread reads instance vars. }; } // namespace clangd diff --git a/unittests/clangd/BackgroundIndexTests.cpp b/unittests/clangd/BackgroundIndexTests.cpp index 1ea75fa5c..93bf62bba 100644 --- a/unittests/clangd/BackgroundIndexTests.cpp +++ b/unittests/clangd/BackgroundIndexTests.cpp @@ -11,8 +11,6 @@ namespace clangd { MATCHER_P(Named, N, "") { return arg.Name == N; } -// Temporarily disabled: test timing out on buildbots. -#if 0 TEST(BackgroundIndexTest, IndexTwoFiles) { MockFSProvider FS; // a.h yields different symbols when included by A.cc vs B.cc. @@ -34,7 +32,6 @@ TEST(BackgroundIndexTest, IndexTwoFiles) { EXPECT_THAT(runFuzzyFind(Idx, ""), UnorderedElementsAre(Named("a_h"), Named("foo"), Named("bar"))); } -#endif } // namespace clangd } // namespace clang From cd983268f962a353bebf179e7338837433600787 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Tue, 16 Oct 2018 10:41:17 +0000 Subject: [PATCH 373/686] [clangd] Allow disble down traversals from root. Summary: This is useful for symbo scope proximity, where down traversals from the global scope if not desired. Reviewers: sammccall Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D53317 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344604 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/FileDistance.cpp | 23 ++++++++++++++++------- clangd/FileDistance.h | 2 ++ unittests/clangd/FileDistanceTests.cpp | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/clangd/FileDistance.cpp b/clangd/FileDistance.cpp index e530ad523..2ae9043a0 100644 --- a/clangd/FileDistance.cpp +++ b/clangd/FileDistance.cpp @@ -54,6 +54,7 @@ static SmallString<128> canonicalize(StringRef Path) { } constexpr const unsigned FileDistance::Unreachable; +const llvm::hash_code FileDistance::RootHash = hash_value(StringRef("/")); FileDistance::FileDistance(StringMap Sources, const FileDistanceOptions &Opts) @@ -99,15 +100,18 @@ FileDistance::FileDistance(StringMap Sources, for (auto Child : DownEdges.lookup(hash_value(llvm::StringRef("")))) Next.push(Child); while (!Next.empty()) { - auto ParentCost = Cache.lookup(Next.front()); - for (auto Child : DownEdges.lookup(Next.front())) { - auto &ChildCost = - Cache.try_emplace(Child, Unreachable).first->getSecond(); - if (ParentCost + Opts.DownCost < ChildCost) - ChildCost = ParentCost + Opts.DownCost; + auto Parent = Next.front(); + Next.pop(); + auto ParentCost = Cache.lookup(Parent); + for (auto Child : DownEdges.lookup(Parent)) { + if (Parent != RootHash || Opts.AllowDownTraversalFromRoot) { + auto &ChildCost = + Cache.try_emplace(Child, Unreachable).first->getSecond(); + if (ParentCost + Opts.DownCost < ChildCost) + ChildCost = ParentCost + Opts.DownCost; + } Next.push(Child); } - Next.pop(); } } @@ -119,6 +123,11 @@ unsigned FileDistance::distance(StringRef Path) { for (StringRef Rest = Canonical; !Rest.empty(); Rest = parent_path(Rest, sys::path::Style::posix)) { auto Hash = hash_value(Rest); + if (Hash == RootHash && !Ancestors.empty() && + !Opts.AllowDownTraversalFromRoot) { + Cost = Unreachable; + break; + } auto It = Cache.find(Hash); if (It != Cache.end()) { Cost = It->second; diff --git a/clangd/FileDistance.h b/clangd/FileDistance.h index f30f1aae8..520b78c36 100644 --- a/clangd/FileDistance.h +++ b/clangd/FileDistance.h @@ -56,6 +56,7 @@ struct FileDistanceOptions { unsigned UpCost = 2; // |foo/bar.h -> foo| unsigned DownCost = 1; // |foo -> foo/bar.h| unsigned IncludeCost = 2; // |foo.cc -> included_header.h| + bool AllowDownTraversalFromRoot = true; // | / -> /a | }; struct SourceParams { @@ -70,6 +71,7 @@ struct SourceParams { class FileDistance { public: static constexpr unsigned Unreachable = std::numeric_limits::max(); + static const llvm::hash_code RootHash; FileDistance(llvm::StringMap Sources, const FileDistanceOptions &Opts = {}); diff --git a/unittests/clangd/FileDistanceTests.cpp b/unittests/clangd/FileDistanceTests.cpp index acce245bf..bb5d3d2f0 100644 --- a/unittests/clangd/FileDistanceTests.cpp +++ b/unittests/clangd/FileDistanceTests.cpp @@ -95,6 +95,20 @@ TEST(FileDistance, LimitUpTraversals) { EXPECT_EQ(D.distance("/a/b/z"), 2u); } +TEST(FileDistance, DisallowDownTraversalsFromRoot) { + FileDistanceOptions Opts; + Opts.UpCost = Opts.DownCost = 1; + Opts.AllowDownTraversalFromRoot = false; + SourceParams CostLots; + CostLots.Cost = 100; + + FileDistance D({{"/", SourceParams()}, {"/a/b/c", CostLots}}, Opts); + EXPECT_EQ(D.distance("/"), 0u); + EXPECT_EQ(D.distance("/a"), 102u); + EXPECT_EQ(D.distance("/a/b"), 101u); + EXPECT_EQ(D.distance("/x"), FileDistance::Unreachable); +} + } // namespace } // namespace clangd } // namespace clang From 701a7f39e293285d46389c781569f58703986b25 Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Tue, 16 Oct 2018 15:55:03 +0000 Subject: [PATCH 374/686] Remove possibility to change compile database path at runtime Summary: This patch removes the possibility to change the compilation database path at runtime using the didChangeConfiguration request. Instead, it is suggested to use the setting on the initialize request, and clangd whenever the user wants to use a different build configuration. Subscribers: ilya-biryukov, ioeric, jkorous, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D53220 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344614 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 27 ++++++------ clangd/Protocol.cpp | 11 ++++- clangd/Protocol.h | 11 +++-- .../compile-commands-path-in-initialize.test | 11 +---- test/clangd/compile-commands-path.test | 42 ------------------- 5 files changed, 33 insertions(+), 69 deletions(-) delete mode 100644 test/clangd/compile-commands-path.test diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index 02496ad48..021c287bb 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -81,8 +81,16 @@ CompletionItemKindBitset defaultCompletionItemKinds() { } // namespace void ClangdLSPServer::onInitialize(InitializeParams &Params) { - if (Params.initializationOptions) - applyConfiguration(*Params.initializationOptions); + if (Params.initializationOptions) { + const ClangdInitializationOptions &Opts = *Params.initializationOptions; + + // Explicit compilation database path. + if (Opts.compilationDatabasePath.hasValue()) { + CDB.setCompileCommandsDir(Opts.compilationDatabasePath.getValue()); + } + + applyConfiguration(Opts.ParamsChange); + } if (Params.rootUri && *Params.rootUri) Server->setRootPath(Params.rootUri->file()); @@ -425,17 +433,10 @@ void ClangdLSPServer::onHover(TextDocumentPositionParams &Params) { } void ClangdLSPServer::applyConfiguration( - const ClangdConfigurationParamsChange &Settings) { - // Compilation database change. - if (Settings.compilationDatabasePath.hasValue()) { - CDB.setCompileCommandsDir(Settings.compilationDatabasePath.getValue()); - - reparseOpenedFiles(); - } - - // Update to the compilation database. - if (Settings.compilationDatabaseChanges) { - const auto &CompileCommandUpdates = *Settings.compilationDatabaseChanges; + const ClangdConfigurationParamsChange &Params) { + // Per-file update to the compilation database. + if (Params.compilationDatabaseChanges) { + const auto &CompileCommandUpdates = *Params.compilationDatabaseChanges; bool ShouldReparseOpenFiles = false; for (auto &Entry : CompileCommandUpdates) { /// The opened files need to be reparsed only when some existing diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp index 7a137eb6b..ced7cf264 100644 --- a/clangd/Protocol.cpp +++ b/clangd/Protocol.cpp @@ -665,10 +665,19 @@ bool fromJSON(const llvm::json::Value &Params, bool fromJSON(const json::Value &Params, ClangdConfigurationParamsChange &CCPC) { json::ObjectMapper O(Params); - return O && O.map("compilationDatabasePath", CCPC.compilationDatabasePath) && + return O && O.map("compilationDatabaseChanges", CCPC.compilationDatabaseChanges); } +bool fromJSON(const json::Value &Params, ClangdInitializationOptions &Opts) { + if (!fromJSON(Params, Opts.ParamsChange)) { + return false; + } + + json::ObjectMapper O(Params); + return O && O.map("compilationDatabasePath", Opts.compilationDatabasePath); +} + bool fromJSON(const json::Value &Params, ReferenceParams &R) { TextDocumentPositionParams &Base = R; return fromJSON(Params, Base); diff --git a/clangd/Protocol.h b/clangd/Protocol.h index bd0973f52..51eb707be 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -411,8 +411,6 @@ bool fromJSON(const llvm::json::Value &, ClangdCompileCommand &); /// "initialize" request and for the "workspace/didChangeConfiguration" /// notification since the data received is described as 'any' type in LSP. struct ClangdConfigurationParamsChange { - llvm::Optional compilationDatabasePath; - // The changes that happened to the compilation database. // The key of the map is a file name. llvm::Optional> @@ -420,7 +418,14 @@ struct ClangdConfigurationParamsChange { }; bool fromJSON(const llvm::json::Value &, ClangdConfigurationParamsChange &); -struct ClangdInitializationOptions : public ClangdConfigurationParamsChange {}; +struct ClangdInitializationOptions { + // What we can change throught the didChangeConfiguration request, we can + // also set through the initialize request (initializationOptions field). + ClangdConfigurationParamsChange ParamsChange; + + llvm::Optional compilationDatabasePath; +}; +bool fromJSON(const llvm::json::Value &, ClangdInitializationOptions &); struct InitializeParams { /// The process Id of the parent process that started diff --git a/test/clangd/compile-commands-path-in-initialize.test b/test/clangd/compile-commands-path-in-initialize.test index b34c59525..ef7cd4f48 100644 --- a/test/clangd/compile-commands-path-in-initialize.test +++ b/test/clangd/compile-commands-path-in-initialize.test @@ -3,10 +3,8 @@ # RUN: rm -rf %t.dir/* && mkdir -p %t.dir # RUN: mkdir %t.dir/build-1 -# RUN: mkdir %t.dir/build-2 # RUN: echo '[{"directory": "%/t.dir", "command": "c++ the-file.cpp", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json # RUN: echo '[{"directory": "%/t.dir/build-1", "command": "c++ -DMACRO=1 the-file.cpp", "file": "../the-file.cpp"}]' > %t.dir/build-1/compile_commands.json -# RUN: echo '[{"directory": "%/t.dir/build-2", "command": "c++ -DMACRO=2 the-file.cpp", "file": "../the-file.cpp"}]' > %t.dir/build-2/compile_commands.json # RUN: sed -e "s|INPUT_DIR|%/t.dir|g" %s > %t.test.1 @@ -18,18 +16,11 @@ {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"initializationOptions":{"compilationDatabasePath":"INPUT_DIR/build-1"}}} --- -{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file://INPUT_DIR/the-file.cpp","languageId":"cpp","version":1,"text":"#if !defined(MACRO)\n#pragma message (\"MACRO is not defined\")\n#elif MACRO == 1\n#pragma message (\"MACRO is one\")\n#elif MACRO == 2\n#pragma message (\"MACRO is two\")\n#else\n#pragma message (\"woops\")\n#endif\nint main() {}\n"}}} +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file://INPUT_DIR/the-file.cpp","languageId":"cpp","version":1,"text":"#if !defined(MACRO)\n#pragma message (\"MACRO is not defined\")\n#elif MACRO == 1\n#pragma message (\"MACRO is one\")\n#else\n#pragma message (\"woops\")\n#endif\nint main() {}\n"}}} # CHECK: "method": "textDocument/publishDiagnostics", # CHECK-NEXT: "params": { # CHECK-NEXT: "diagnostics": [ # CHECK-NEXT: { # CHECK-NEXT: "message": "MACRO is one", --- -{"jsonrpc":"2.0","id":0,"method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabasePath":"INPUT_DIR/build-2"}}} -# CHECK: "method": "textDocument/publishDiagnostics", -# CHECK-NEXT: "params": { -# CHECK-NEXT: "diagnostics": [ -# CHECK-NEXT: { -# CHECK-NEXT: "message": "MACRO is two", ---- {"jsonrpc":"2.0","id":10000,"method":"shutdown"} diff --git a/test/clangd/compile-commands-path.test b/test/clangd/compile-commands-path.test deleted file mode 100644 index f25d002f9..000000000 --- a/test/clangd/compile-commands-path.test +++ /dev/null @@ -1,42 +0,0 @@ -# Test that we can switch between configuration/build using the -# workspace/didChangeConfiguration notification. - -# RUN: rm -rf %t.dir/* && mkdir -p %t.dir -# RUN: mkdir %t.dir/build-1 -# RUN: mkdir %t.dir/build-2 -# RUN: echo '[{"directory": "%/t.dir", "command": "c++ the-file.cpp", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json -# RUN: echo '[{"directory": "%/t.dir/build-1", "command": "c++ -DMACRO=1 the-file.cpp", "file": "../the-file.cpp"}]' > %t.dir/build-1/compile_commands.json -# RUN: echo '[{"directory": "%/t.dir/build-2", "command": "c++ -DMACRO=2 the-file.cpp", "file": "../the-file.cpp"}]' > %t.dir/build-2/compile_commands.json - -# RUN: sed -e "s|INPUT_DIR|%/t.dir|g" %s > %t.test.1 - -# On Windows, we need the URI in didOpen to look like "uri":"file:///C:/..." -# (with the extra slash in the front), so we add it here. -# RUN: sed -e "s|file://\([A-Z]\):/|file:///\1:/|g" %t.test.1 > %t.test - -# RUN: clangd -lit-test < %t.test | FileCheck -strict-whitespace %t.test - -{"jsonrpc":"2.0","id":0,"method":"initialize","params":{}} ---- -{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file://INPUT_DIR/the-file.cpp","languageId":"cpp","version":1,"text":"#if !defined(MACRO)\n#pragma message (\"MACRO is not defined\")\n#elif MACRO == 1\n#pragma message (\"MACRO is one\")\n#elif MACRO == 2\n#pragma message (\"MACRO is two\")\n#else\n#pragma message (\"woops\")\n#endif\nint main() {}\n"}}} -# CHECK: "method": "textDocument/publishDiagnostics", -# CHECK-NEXT: "params": { -# CHECK-NEXT: "diagnostics": [ -# CHECK-NEXT: { -# CHECK-NEXT: "message": "MACRO is not defined", ---- -{"jsonrpc":"2.0","id":0,"method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabasePath":"INPUT_DIR/build-1"}}} -# CHECK: "method": "textDocument/publishDiagnostics", -# CHECK-NEXT: "params": { -# CHECK-NEXT: "diagnostics": [ -# CHECK-NEXT: { -# CHECK-NEXT: "message": "MACRO is one", ---- -{"jsonrpc":"2.0","id":0,"method":"workspace/didChangeConfiguration","params":{"settings":{"compilationDatabasePath":"INPUT_DIR/build-2"}}} -# CHECK: "method": "textDocument/publishDiagnostics", -# CHECK-NEXT: "params": { -# CHECK-NEXT: "diagnostics": [ -# CHECK-NEXT: { -# CHECK-NEXT: "message": "MACRO is two", ---- -{"jsonrpc":"2.0","id":10000,"method":"shutdown"} From b6363f57065ae0fe9f8301ea6b3e2d82b4dd0f62 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 16 Oct 2018 16:29:41 +0000 Subject: [PATCH 375/686] [clangd] Send CodeAction responses to textDocument/codeAction (LSP 3.8) Summary: I don't bother mirroring the full capabilities struct, just parse the bits we care about. I'll send a new patch to use this approach elsewhere too. Reviewers: kadircet Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, cfe-commits Differential Revision: https://reviews.llvm.org/D53213 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344617 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/ClangdLSPServer.cpp | 56 +++++--- clangd/ClangdLSPServer.h | 2 + clangd/Protocol.cpp | 29 ++++ clangd/Protocol.h | 31 +++++ test/clangd/fixits-codeaction.test | 126 ++++++++++++++++++ .../{fixits.test => fixits-command.test} | 0 6 files changed, 229 insertions(+), 15 deletions(-) create mode 100644 test/clangd/fixits-codeaction.test rename test/clangd/{fixits.test => fixits-command.test} (100%) diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index 021c287bb..b1863062d 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -103,6 +103,8 @@ void ClangdLSPServer::onInitialize(InitializeParams &Params) { Params.capabilities.textDocument.publishDiagnostics.clangdFixSupport; DiagOpts.SendDiagnosticCategory = Params.capabilities.textDocument.publishDiagnostics.categorySupport; + SupportsCodeAction = + Params.capabilities.textDocument.codeActionLiteralSupport; if (Params.capabilities.workspace && Params.capabilities.workspace->symbol && Params.capabilities.workspace->symbol->symbolKind && @@ -339,29 +341,53 @@ void ClangdLSPServer::onDocumentSymbol(DocumentSymbolParams &Params) { }); } +static Optional asCommand(const CodeAction &Action) { + Command Cmd; + if (Action.command && Action.edit) + return llvm::None; // Not representable. (We never emit these anyway). + if (Action.command) { + Cmd = *Action.command; + } else if (Action.edit) { + Cmd.command = Command::CLANGD_APPLY_FIX_COMMAND; + Cmd.workspaceEdit = *Action.edit; + } else { + return llvm::None; + } + Cmd.title = Action.title; + if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) + Cmd.title = "Apply fix: " + Cmd.title; + return Cmd; +} + void ClangdLSPServer::onCodeAction(CodeActionParams &Params) { - // We provide a code action for each diagnostic at the requested location - // which has FixIts available. - auto Code = DraftMgr.getDraft(Params.textDocument.uri.file()); - if (!Code) + // We provide a code action for Fixes on the specified diagnostics. + if (!DraftMgr.getDraft(Params.textDocument.uri.file())) return replyError(ErrorCode::InvalidParams, "onCodeAction called for non-added file"); - std::vector Commands; + std::vector Actions; for (Diagnostic &D : Params.context.diagnostics) { for (auto &F : getFixes(Params.textDocument.uri.file(), D)) { - WorkspaceEdit WE; - std::vector Edits(F.Edits.begin(), F.Edits.end()); - Commands.emplace_back(); - Commands.back().title = llvm::formatv("Apply fix: {0}", F.Message); - Commands.back().command = ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND; - Commands.back().workspaceEdit.emplace(); - Commands.back().workspaceEdit->changes = { - {Params.textDocument.uri.uri(), std::move(Edits)}, - }; + Actions.emplace_back(); + Actions.back().title = F.Message; + Actions.back().kind = CodeAction::QUICKFIX_KIND; + Actions.back().diagnostics = {D}; + Actions.back().edit.emplace(); + Actions.back().edit->changes.emplace(); + (*Actions.back().edit->changes)[Params.textDocument.uri.uri()] = { + F.Edits.begin(), F.Edits.end()}; } } - reply(json::Array(Commands)); + + if (SupportsCodeAction) + reply(json::Array(Actions)); + else { + std::vector Commands; + for (const auto &Action : Actions) + if (auto Command = asCommand(Action)) + Commands.push_back(std::move(*Command)); + reply(json::Array(Commands)); + } } void ClangdLSPServer::onCompletion(TextDocumentPositionParams &Params) { diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h index f77b24b9b..6fb524837 100644 --- a/clangd/ClangdLSPServer.h +++ b/clangd/ClangdLSPServer.h @@ -166,6 +166,8 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { SymbolKindBitset SupportedSymbolKinds; /// The supported completion item kinds of the client. CompletionItemKindBitset SupportedCompletionItemKinds; + // Whether the client supports CodeAction response objects. + bool SupportsCodeAction = false; // Store of the current versions of the open documents. DraftStore DraftMgr; diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp index ced7cf264..daab1328c 100644 --- a/clangd/Protocol.cpp +++ b/clangd/Protocol.cpp @@ -251,6 +251,9 @@ bool fromJSON(const json::Value &Params, TextDocumentClientCapabilities &R) { return false; O.map("completion", R.completion); O.map("publishDiagnostics", R.publishDiagnostics); + if (auto *CodeAction = Params.getAsObject()->getObject("codeAction")) + if (CodeAction->getObject("codeActionLiteralSupport")) + R.codeActionLiteralSupport = true; return true; } @@ -360,6 +363,17 @@ bool fromJSON(const json::Value &Params, DocumentSymbolParams &R) { return O && O.map("textDocument", R.textDocument); } +llvm::json::Value toJSON(const Diagnostic &D) { + json::Object Diag{ + {"range", D.range}, + {"severity", D.severity}, + {"message", D.message}, + }; + // FIXME: this should be used for publishDiagnostics. + // FIXME: send category and fixes when appropriate. + return std::move(Diag); +} + bool fromJSON(const json::Value &Params, Diagnostic &R) { json::ObjectMapper O(Params); if (!O || !O.map("range", R.range) || !O.map("message", R.message)) @@ -448,6 +462,21 @@ json::Value toJSON(const Command &C) { return std::move(Cmd); } +const llvm::StringLiteral CodeAction::QUICKFIX_KIND = "quickfix"; + +llvm::json::Value toJSON(const CodeAction &CA) { + auto CodeAction = json::Object{{"title", CA.title}}; + if (CA.kind) + CodeAction["kind"] = *CA.kind; + if (CA.diagnostics) + CodeAction["diagnostics"] = json::Array(*CA.diagnostics); + if (CA.edit) + CodeAction["edit"] = *CA.edit; + if (CA.command) + CodeAction["command"] = *CA.command; + return std::move(CodeAction); +} + json::Value toJSON(const WorkspaceEdit &WE) { if (!WE.changes) return json::Object{}; diff --git a/clangd/Protocol.h b/clangd/Protocol.h index 51eb707be..7026d47b9 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -385,6 +385,10 @@ struct TextDocumentClientCapabilities { /// Capabilities specific to the 'textDocument/publishDiagnostics' PublishDiagnosticsClientCapabilities publishDiagnostics; + + /// Flattened from codeAction.codeActionLiteralSupport. + // FIXME: flatten other properties in this way. + bool codeActionLiteralSupport = false; }; bool fromJSON(const llvm::json::Value &, TextDocumentClientCapabilities &); @@ -613,6 +617,7 @@ struct Diagnostic { /// which the issue was produced, e.g. "Semantic Issue" or "Parse Issue". std::string category; }; +llvm::json::Value toJSON(const Diagnostic &); /// A LSP-specific comparator used to find diagnostic in a container like /// std:map. @@ -680,6 +685,32 @@ struct Command : public ExecuteCommandParams { }; llvm::json::Value toJSON(const Command &C); +/// A code action represents a change that can be performed in code, e.g. to fix +/// a problem or to refactor code. +/// +/// A CodeAction must set either `edit` and/or a `command`. If both are +/// supplied, the `edit` is applied first, then the `command` is executed. +struct CodeAction { + /// A short, human-readable, title for this code action. + std::string title; + + /// The kind of the code action. + /// Used to filter code actions. + llvm::Optional kind; + const static llvm::StringLiteral QUICKFIX_KIND; + + /// The diagnostics that this code action resolves. + llvm::Optional> diagnostics; + + /// The workspace edit this code action performs. + llvm::Optional edit; + + /// A command this code action executes. If a code action provides an edit + /// and a command, first the edit is executed and then the command. + llvm::Optional command; +}; +llvm::json::Value toJSON(const CodeAction &); + /// Represents information about programming constructs like variables, classes, /// interfaces etc. struct SymbolInformation { diff --git a/test/clangd/fixits-codeaction.test b/test/clangd/fixits-codeaction.test new file mode 100644 index 000000000..97dd4ff23 --- /dev/null +++ b/test/clangd/fixits-codeaction.test @@ -0,0 +1,126 @@ +# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{"textDocument":{"codeAction":{"codeActionLiteralSupport":{}}}},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2 +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c" +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses"}]}}} +# CHECK: "id": 2, +# CHECK-NEXT: "jsonrpc": "2.0", +# CHECK-NEXT: "result": [ +# CHECK-NEXT: { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2 +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "edit": { +# CHECK-NEXT: "changes": { +# CHECK-NEXT: "file://{{.*}}/foo.c": [ +# CHECK-NEXT: { +# CHECK-NEXT: "newText": "(", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "newText": ")", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "kind": "quickfix", +# CHECK-NEXT: "title": "place parentheses around the assignment to silence this warning" +# CHECK-NEXT: }, +# CHECK-NEXT: { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "message": "Using the result of an assignment as a condition without parentheses", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 37, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 32, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2 +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "edit": { +# CHECK-NEXT: "changes": { +# CHECK-NEXT: "file://{{.*}}/foo.c": [ +# CHECK-NEXT: { +# CHECK-NEXT: "newText": "==", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 35, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 34, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "kind": "quickfix", +# CHECK-NEXT: "title": "use '==' to turn this assignment into an equality comparison" +# CHECK-NEXT: } +# CHECK-NEXT: ] +--- +{"jsonrpc":"2.0","id":4,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} + diff --git a/test/clangd/fixits.test b/test/clangd/fixits-command.test similarity index 100% rename from test/clangd/fixits.test rename to test/clangd/fixits-command.test From fb2fd434a9ce2fa5f42d7b8087d14a19afda848f Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Tue, 16 Oct 2018 16:48:06 +0000 Subject: [PATCH 376/686] [clangd] Refactor JSON-over-stdin/stdout code into Transport abstraction. Summary: This paves the way for alternative transports (mac XPC, maybe messagepack?), and also generally improves layering: testing ClangdLSPServer becomes less of a pipe dream, we split up the JSONOutput monolith, etc. This isn't a final state, much of what remains in JSONRPCDispatcher can go away, handlers can call reply() on the transport directly, JSONOutput can be renamed to StreamLogger and removed, etc. But this patch is sprawling already. The main observable change (see tests) is that hitting EOF on input is now an error: the client should send the 'exit' notification. This is defensible: the protocol doesn't spell this case out. Reproducing the current behavior for all combinations of shutdown/exit/EOF clutters interfaces. We can iterate on this if desired. Reviewers: jkorous, ioeric, hokein Subscribers: mgorny, ilya-biryukov, MaskRay, arphaman, kadircet, cfe-commits Differential Revision: https://reviews.llvm.org/D53286 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344620 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 1 + clangd/ClangdLSPServer.cpp | 43 +-- clangd/ClangdLSPServer.h | 21 +- clangd/JSONRPCDispatcher.cpp | 301 +++--------------- clangd/JSONRPCDispatcher.h | 67 ++-- clangd/JSONTransport.cpp | 298 +++++++++++++++++ clangd/Protocol.cpp | 2 + clangd/Protocol.h | 17 + clangd/ProtocolHandlers.cpp | 1 + clangd/Transport.h | 92 ++++++ clangd/tool/ClangdMain.cpp | 19 +- .../compile-commands-path-in-initialize.test | 2 + test/clangd/completion-snippets.test | 2 + test/clangd/completion.test | 2 + test/clangd/crash-non-added-files.test | 2 + test/clangd/execute-command.test | 2 + test/clangd/input-mirror.test | 3 + test/clangd/signature-help.test | 2 + test/clangd/textdocument-didchange-fail.test | 2 + test/clangd/trace.test | 2 + test/clangd/xrefs.test | 2 + unittests/clangd/CMakeLists.txt | 1 + unittests/clangd/JSONTransportTests.cpp | 199 ++++++++++++ 23 files changed, 742 insertions(+), 341 deletions(-) create mode 100644 clangd/JSONTransport.cpp create mode 100644 clangd/Transport.h create mode 100644 unittests/clangd/JSONTransportTests.cpp diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index 4eeaffcbf..2fa7cd2f2 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -26,6 +26,7 @@ add_clang_library(clangDaemon GlobalCompilationDatabase.cpp Headers.cpp JSONRPCDispatcher.cpp + JSONTransport.cpp Logger.cpp Protocol.cpp ProtocolHandlers.cpp diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index b1863062d..3b6724078 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -162,7 +162,10 @@ void ClangdLSPServer::onShutdown(ShutdownParams &Params) { reply(nullptr); } -void ClangdLSPServer::onExit(ExitParams &Params) { IsDone = true; } +void ClangdLSPServer::onExit(ExitParams &Params) { + // No work to do. + // JSONRPCDispatcher shuts down the transport after this notification. +} void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams &Params) { PathRef File = Params.textDocument.uri.file(); @@ -497,39 +500,41 @@ void ClangdLSPServer::onReference(ReferenceParams &Params) { }); } -ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, +ClangdLSPServer::ClangdLSPServer(class Transport &Transport, const clangd::CodeCompleteOptions &CCOpts, llvm::Optional CompileCommandsDir, bool ShouldUseInMemoryCDB, const ClangdServer::Options &Opts) - : Out(Out), CDB(ShouldUseInMemoryCDB ? CompilationDB::makeInMemory() - : CompilationDB::makeDirectoryBased( - std::move(CompileCommandsDir))), + : Transport(Transport), + CDB(ShouldUseInMemoryCDB ? CompilationDB::makeInMemory() + : CompilationDB::makeDirectoryBased( + std::move(CompileCommandsDir))), CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()), SupportedCompletionItemKinds(defaultCompletionItemKinds()), Server(new ClangdServer(CDB.getCDB(), FSProvider, /*DiagConsumer=*/*this, Opts)) {} -bool ClangdLSPServer::run(std::FILE *In, JSONStreamStyle InputStyle) { - assert(!IsDone && "Run was called before"); +bool ClangdLSPServer::run() { assert(Server); // Set up JSONRPCDispatcher. JSONRPCDispatcher Dispatcher([](const json::Value &Params) { replyError(ErrorCode::MethodNotFound, "method not found"); + return true; }); registerCallbackHandlers(Dispatcher, /*Callbacks=*/*this); // Run the Language Server loop. - runLanguageServerLoop(In, Out, InputStyle, Dispatcher, IsDone); + bool CleanExit = true; + if (auto Err = Dispatcher.runLanguageServerLoop(Transport)) { + elog("Transport error: {0}", std::move(Err)); + CleanExit = false; + } - // Make sure IsDone is set to true after this method exits to ensure assertion - // at the start of the method fires if it's ever executed again. - IsDone = true; // Destroy ClangdServer to ensure all worker threads finish. Server.reset(); - return ShutdownRequestReceived; + return CleanExit && ShutdownRequestReceived; } std::vector ClangdLSPServer::getFixes(StringRef File, @@ -589,15 +594,11 @@ void ClangdLSPServer::onDiagnosticsReady(PathRef File, } // Publish diagnostics. - Out.writeMessage(json::Object{ - {"jsonrpc", "2.0"}, - {"method", "textDocument/publishDiagnostics"}, - {"params", - json::Object{ - {"uri", URIForFile{File}}, - {"diagnostics", std::move(DiagnosticsJSON)}, - }}, - }); + Transport.notify("textDocument/publishDiagnostics", + json::Object{ + {"uri", URIForFile{File}}, + {"diagnostics", std::move(DiagnosticsJSON)}, + }); } void ClangdLSPServer::reparseOpenedFiles() { diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h index 6fb524837..3c7458d3e 100644 --- a/clangd/ClangdLSPServer.h +++ b/clangd/ClangdLSPServer.h @@ -37,18 +37,16 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { /// If \p CompileCommandsDir has a value, compile_commands.json will be /// loaded only from \p CompileCommandsDir. Otherwise, clangd will look /// for compile_commands.json in all parent directories of each file. - ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts, + ClangdLSPServer(Transport &Transport, + const clangd::CodeCompleteOptions &CCOpts, llvm::Optional CompileCommandsDir, bool ShouldUseInMemoryCDB, const ClangdServer::Options &Opts); - /// Run LSP server loop, receiving input for it from \p In. \p In must be - /// opened in binary mode. Output will be written using Out variable passed to - /// class constructor. This method must not be executed more than once for - /// each instance of ClangdLSPServer. + /// Run LSP server loop, communicating with the Transport provided in the + /// constructor. This method must not be executed more than once. /// - /// \return Whether we received a 'shutdown' request before an 'exit' request. - bool run(std::FILE *In, - JSONStreamStyle InputStyle = JSONStreamStyle::Standard); + /// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence. + bool run(); private: // Implement DiagnosticsConsumer. @@ -89,16 +87,10 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { void reparseOpenedFiles(); void applyConfiguration(const ClangdConfigurationParamsChange &Settings); - JSONOutput &Out; /// Used to indicate that the 'shutdown' request was received from the /// Language Server client. bool ShutdownRequestReceived = false; - /// Used to indicate that the 'exit' notification was received from the - /// Language Server client. - /// It's used to break out of the LSP parsing loop. - bool IsDone = false; - std::mutex FixItsMutex; typedef std::map, LSPDiagnosticCompare> DiagnosticToReplacementMap; @@ -153,6 +145,7 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { bool IsDirectoryBased; }; + clangd::Transport &Transport; // Various ClangdServer parameters go here. It's important they're created // before ClangdServer. CompilationDB CDB; diff --git a/clangd/JSONRPCDispatcher.cpp b/clangd/JSONRPCDispatcher.cpp index 4fe4f5596..20a2cbabe 100644 --- a/clangd/JSONRPCDispatcher.cpp +++ b/clangd/JSONRPCDispatcher.cpp @@ -11,6 +11,7 @@ #include "Cancellation.h" #include "ProtocolHandlers.h" #include "Trace.h" +#include "Transport.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" @@ -28,7 +29,7 @@ using namespace clangd; namespace { static Key RequestID; -static Key RequestOut; +static Key CurrentTransport; // When tracing, we trace a request and attach the response in reply(). // Because the Span isn't available, we find the current request using Context. @@ -58,23 +59,6 @@ class RequestSpan { Key> RequestSpan::RSKey; } // namespace -void JSONOutput::writeMessage(const json::Value &Message) { - std::string S; - llvm::raw_string_ostream OS(S); - if (Pretty) - OS << llvm::formatv("{0:2}", Message); - else - OS << Message; - OS.flush(); - - { - std::lock_guard Guard(StreamMutex); - Outs << "Content-Length: " << S.size() << "\r\n\r\n" << S; - Outs.flush(); - } - vlog(">>> {0}\n", S); -} - void JSONOutput::log(Logger::Level Level, const llvm::formatv_object_base &Message) { if (Level < MinLevel) @@ -87,14 +71,6 @@ void JSONOutput::log(Logger::Level Level, Logs.flush(); } -void JSONOutput::mirrorInput(const Twine &Message) { - if (!InputMirror) - return; - - *InputMirror << Message; - InputMirror->flush(); -} - void clangd::reply(json::Value &&Result) { auto ID = getRequestId(); if (!ID) { @@ -104,12 +80,8 @@ void clangd::reply(json::Value &&Result) { RequestSpan::attach([&](json::Object &Args) { Args["Reply"] = Result; }); log("--> reply({0})", *ID); Context::current() - .getExisting(RequestOut) - ->writeMessage(json::Object{ - {"jsonrpc", "2.0"}, - {"id", *ID}, - {"result", std::move(Result)}, - }); + .getExisting(CurrentTransport) + ->reply(std::move(*ID), std::move(Result)); } void clangd::replyError(ErrorCode Code, const llvm::StringRef &Message) { @@ -122,13 +94,8 @@ void clangd::replyError(ErrorCode Code, const llvm::StringRef &Message) { if (auto ID = getRequestId()) { log("--> reply({0}) error: {1}", *ID, Message); Context::current() - .getExisting(RequestOut) - ->writeMessage(json::Object{ - {"jsonrpc", "2.0"}, - {"id", *ID}, - {"error", json::Object{{"code", static_cast(Code)}, - {"message", Message}}}, - }); + .getExisting(CurrentTransport) + ->reply(std::move(*ID), make_error(Message, Code)); } } @@ -151,22 +118,20 @@ void clangd::call(StringRef Method, json::Value &&Params) { auto ID = 1; log("--> {0}({1})", Method, ID); Context::current() - .getExisting(RequestOut) - ->writeMessage(json::Object{ - {"jsonrpc", "2.0"}, - {"id", ID}, - {"method", Method}, - {"params", std::move(Params)}, - }); + .getExisting(CurrentTransport) + ->call(Method, std::move(Params), ID); } JSONRPCDispatcher::JSONRPCDispatcher(Handler UnknownHandler) : UnknownHandler(std::move(UnknownHandler)) { registerHandler("$/cancelRequest", [this](const json::Value &Params) { if (auto *O = Params.getAsObject()) - if (auto *ID = O->get("id")) - return cancelRequest(*ID); + if (auto *ID = O->get("id")) { + cancelRequest(*ID); + return true; + } log("Bad cancellation request: {0}", Params); + return true; }); } @@ -175,64 +140,48 @@ void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) { Handlers[Method] = std::move(H); } -static void logIncomingMessage(const llvm::Optional &ID, - llvm::Optional Method, - const json::Object *Error) { - if (Method) { // incoming request - if (ID) // call - log("<-- {0}({1})", *Method, *ID); - else // notification - log("<-- {0}", *Method); - } else if (ID) { // response, ID must be provided - if (Error) - log("<-- reply({0}) error: {1}", *ID, - Error->getString("message").getValueOr("")); - else - log("<-- reply({0})", *ID); - } -} - -bool JSONRPCDispatcher::call(const json::Value &Message, JSONOutput &Out) { - // Message must be an object with "jsonrpc":"2.0". - auto *Object = Message.getAsObject(); - if (!Object || Object->getString("jsonrpc") != Optional("2.0")) - return false; - // ID may be any JSON value. If absent, this is a notification. - llvm::Optional ID; - if (auto *I = Object->get("id")) - ID = std::move(*I); - auto Method = Object->getString("method"); - logIncomingMessage(ID, Method, Object->getObject("error")); - if (!Method) // We only handle incoming requests, and ignore responses. - return false; - // Params should be given, use null if not. - json::Value Params = nullptr; - if (auto *P = Object->get("params")) - Params = std::move(*P); - - auto I = Handlers.find(*Method); +bool JSONRPCDispatcher::onCall(StringRef Method, json::Value Params, + json::Value ID) { + log("<-- {0}({1})", Method, ID); + auto I = Handlers.find(Method); auto &Handler = I != Handlers.end() ? I->second : UnknownHandler; // Create a Context that contains request information. - WithContextValue WithRequestOut(RequestOut, &Out); - llvm::Optional WithID; - if (ID) - WithID.emplace(RequestID, *ID); + WithContextValue WithID(RequestID, ID); // Create a tracing Span covering the whole request lifetime. - trace::Span Tracer(*Method); - if (ID) - SPAN_ATTACH(Tracer, "ID", *ID); + trace::Span Tracer(Method); + SPAN_ATTACH(Tracer, "ID", ID); SPAN_ATTACH(Tracer, "Params", Params); - // Requests with IDs can be canceled by the client. Add cancellation context. - llvm::Optional WithCancel; - if (ID) - WithCancel.emplace(cancelableRequestContext(*ID)); + // Calls can be canceled by the client. Add cancellation context. + WithContext WithCancel(cancelableRequestContext(ID)); // Stash a reference to the span args, so later calls can add metadata. WithContext WithRequestSpan(RequestSpan::stash(Tracer)); - Handler(std::move(Params)); + return Handler(std::move(Params)); +} + +bool JSONRPCDispatcher::onNotify(StringRef Method, json::Value Params) { + log("<-- {0}", Method); + auto I = Handlers.find(Method); + auto &Handler = I != Handlers.end() ? I->second : UnknownHandler; + + // Create a tracing Span covering the whole request lifetime. + trace::Span Tracer(Method); + SPAN_ATTACH(Tracer, "Params", Params); + + // Stash a reference to the span args, so later calls can add metadata. + WithContext WithRequestSpan(RequestSpan::stash(Tracer)); + return Handler(std::move(Params)); +} + +bool JSONRPCDispatcher::onReply(json::Value ID, Expected Result) { + // We ignore replies, just log them. + if (Result) + log("<-- reply({0})", ID); + else + log("<-- reply({0}) error: {1}", ID, llvm::toString(Result.takeError())); return true; } @@ -266,162 +215,10 @@ void JSONRPCDispatcher::cancelRequest(const json::Value &ID) { It->second.first(); // Invoke the canceler. } -// Tries to read a line up to and including \n. -// If failing, feof() or ferror() will be set. -static bool readLine(std::FILE *In, std::string &Out) { - static constexpr int BufSize = 1024; - size_t Size = 0; - Out.clear(); - for (;;) { - Out.resize(Size + BufSize); - // Handle EINTR which is sent when a debugger attaches on some platforms. - if (!llvm::sys::RetryAfterSignal(nullptr, ::fgets, &Out[Size], BufSize, In)) - return false; - clearerr(In); - // If the line contained null bytes, anything after it (including \n) will - // be ignored. Fortunately this is not a legal header or JSON. - size_t Read = std::strlen(&Out[Size]); - if (Read > 0 && Out[Size + Read - 1] == '\n') { - Out.resize(Size + Read); - return true; - } - Size += Read; - } -} - -// Returns None when: -// - ferror() or feof() are set. -// - Content-Length is missing or empty (protocol error) -static llvm::Optional readStandardMessage(std::FILE *In, - JSONOutput &Out) { - // A Language Server Protocol message starts with a set of HTTP headers, - // delimited by \r\n, and terminated by an empty line (\r\n). - unsigned long long ContentLength = 0; - std::string Line; - while (true) { - if (feof(In) || ferror(In) || !readLine(In, Line)) - return llvm::None; - - Out.mirrorInput(Line); - llvm::StringRef LineRef(Line); - - // We allow comments in headers. Technically this isn't part - // of the LSP specification, but makes writing tests easier. - if (LineRef.startswith("#")) - continue; - - // Content-Length is a mandatory header, and the only one we handle. - if (LineRef.consume_front("Content-Length: ")) { - if (ContentLength != 0) { - elog("Warning: Duplicate Content-Length header received. " - "The previous value for this message ({0}) was ignored.", - ContentLength); - } - llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength); - continue; - } else if (!LineRef.trim().empty()) { - // It's another header, ignore it. - continue; - } else { - // An empty line indicates the end of headers. - // Go ahead and read the JSON. - break; - } - } - - // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999" - if (ContentLength > 1 << 30) { // 1024M - elog("Refusing to read message with long Content-Length: {0}. " - "Expect protocol errors", - ContentLength); - return llvm::None; - } - if (ContentLength == 0) { - log("Warning: Missing Content-Length header, or zero-length message."); - return llvm::None; - } - - std::string JSON(ContentLength, '\0'); - for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) { - // Handle EINTR which is sent when a debugger attaches on some platforms. - Read = llvm::sys::RetryAfterSignal(0u, ::fread, &JSON[Pos], 1, - ContentLength - Pos, In); - Out.mirrorInput(StringRef(&JSON[Pos], Read)); - if (Read == 0) { - elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos, - ContentLength); - return llvm::None; - } - clearerr(In); // If we're done, the error was transient. If we're not done, - // either it was transient or we'll see it again on retry. - Pos += Read; - } - return std::move(JSON); -} - -// For lit tests we support a simplified syntax: -// - messages are delimited by '---' on a line by itself -// - lines starting with # are ignored. -// This is a testing path, so favor simplicity over performance here. -// When returning None, feof() or ferror() will be set. -static llvm::Optional readDelimitedMessage(std::FILE *In, - JSONOutput &Out) { - std::string JSON; - std::string Line; - while (readLine(In, Line)) { - auto LineRef = llvm::StringRef(Line).trim(); - if (LineRef.startswith("#")) // comment - continue; - - // found a delimiter - if (LineRef.rtrim() == "---") - break; - - JSON += Line; - } - - if (ferror(In)) { - elog("Input error while reading message!"); - return llvm::None; - } else { // Including EOF - Out.mirrorInput( - llvm::formatv("Content-Length: {0}\r\n\r\n{1}", JSON.size(), JSON)); - return std::move(JSON); - } -} - -// The use of C-style std::FILE* IO deserves some explanation. -// Previously, std::istream was used. When a debugger attached on MacOS, the -// process received EINTR, the stream went bad, and clangd exited. -// A retry-on-EINTR loop around reads solved this problem, but caused clangd to -// sometimes hang rather than exit on other OSes. The interaction between -// istreams and signals isn't well-specified, so it's hard to get this right. -// The C APIs seem to be clearer in this respect. -void clangd::runLanguageServerLoop(std::FILE *In, JSONOutput &Out, - JSONStreamStyle InputStyle, - JSONRPCDispatcher &Dispatcher, - bool &IsDone) { - auto &ReadMessage = - (InputStyle == Delimited) ? readDelimitedMessage : readStandardMessage; - while (!IsDone && !feof(In)) { - if (ferror(In)) { - elog("IO error: {0}", llvm::sys::StrError()); - return; - } - if (auto JSON = ReadMessage(In, Out)) { - if (auto Doc = json::parse(*JSON)) { - // Log the formatted message. - vlog(Out.Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc); - // Finally, execute the action for this JSON message. - if (!Dispatcher.call(*Doc, Out)) - elog("JSON dispatch failed!"); - } else { - // Parse error. Log the raw message. - vlog("<<< {0}\n", *JSON); - elog("JSON parse error: {0}", llvm::toString(Doc.takeError())); - } - } - } +llvm::Error JSONRPCDispatcher::runLanguageServerLoop(Transport &Transport) { + // Propagate transport to all handlers so they can reply. + WithContextValue WithTransport(CurrentTransport, &Transport); + return Transport.loop(*this); } const json::Value *clangd::getRequestId() { diff --git a/clangd/JSONRPCDispatcher.h b/clangd/JSONRPCDispatcher.h index b223ceb5f..43c466f70 100644 --- a/clangd/JSONRPCDispatcher.h +++ b/clangd/JSONRPCDispatcher.h @@ -14,6 +14,7 @@ #include "Logger.h" #include "Protocol.h" #include "Trace.h" +#include "Transport.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" @@ -24,37 +25,19 @@ namespace clang { namespace clangd { -/// Encapsulates output and logs streams and provides thread-safe access to -/// them. +// Logs to an output stream, such as stderr. +// FIXME: Rename to StreamLogger or such, and move to Logger.h. class JSONOutput : public Logger { - // FIXME(ibiryukov): figure out if we can shrink the public interface of - // JSONOutput now that we pass Context everywhere. public: - JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs, - Logger::Level MinLevel, llvm::raw_ostream *InputMirror = nullptr, - bool Pretty = false) - : Pretty(Pretty), MinLevel(MinLevel), Outs(Outs), Logs(Logs), - InputMirror(InputMirror) {} - - /// Emit a JSONRPC message. - void writeMessage(const llvm::json::Value &Result); + JSONOutput(llvm::raw_ostream &Logs, Logger::Level MinLevel) + : MinLevel(MinLevel), Logs(Logs) {} /// Write a line to the logging stream. void log(Level, const llvm::formatv_object_base &Message) override; - /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is - /// null. - /// Unlike other methods of JSONOutput, mirrorInput is not thread-safe. - void mirrorInput(const Twine &Message); - - // Whether output should be pretty-printed. - const bool Pretty; - private: Logger::Level MinLevel; - llvm::raw_ostream &Outs; llvm::raw_ostream &Logs; - llvm::raw_ostream *InputMirror; std::mutex StreamMutex; }; @@ -81,14 +64,15 @@ void call(llvm::StringRef Method, llvm::json::Value &&Params); /// /// The `$/cancelRequest` notification is handled by the dispatcher itself. /// It marks the matching request as cancelled, if it's still running. -class JSONRPCDispatcher { +class JSONRPCDispatcher : private Transport::MessageHandler { public: /// A handler responds to requests for a particular method name. + /// It returns false if the server should now shut down. /// /// JSONRPCDispatcher will mark the handler's context as cancelled if a /// matching cancellation request is received. Handlers are encouraged to /// check for cancellation and fail quickly in this case. - using Handler = std::function; + using Handler = std::function; /// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown /// method is received. @@ -97,10 +81,22 @@ class JSONRPCDispatcher { /// Registers a Handler for the specified Method. void registerHandler(StringRef Method, Handler H); - /// Parses a JSONRPC message and calls the Handler for it. - bool call(const llvm::json::Value &Message, JSONOutput &Out); + /// Parses input queries from LSP client (coming from \p In) and runs call + /// method for each query. + /// + /// Input stream(\p In) must be opened in binary mode to avoid + /// preliminary replacements of \r\n with \n. We use C-style FILE* for reading + /// as std::istream has unclear interaction with signals, which are sent by + /// debuggers on some OSs. + llvm::Error runLanguageServerLoop(Transport &); private: + bool onReply(llvm::json::Value ID, + llvm::Expected Result) override; + bool onNotify(llvm::StringRef Method, llvm::json::Value Message) override; + bool onCall(llvm::StringRef Method, llvm::json::Value Message, + llvm::json::Value ID) override; + // Tracking cancellations needs a mutex: handlers may finish on a different // thread, and that's when we clean up entries in the map. mutable std::mutex RequestCancelersMutex; @@ -113,25 +109,6 @@ class JSONRPCDispatcher { Handler UnknownHandler; }; -/// Controls the way JSON-RPC messages are encoded (both input and output). -enum JSONStreamStyle { - /// Encoding per the LSP specification, with mandatory Content-Length header. - Standard, - /// Messages are delimited by a '---' line. Comment lines start with #. - Delimited -}; - -/// Parses input queries from LSP client (coming from \p In) and runs call -/// method of \p Dispatcher for each query. -/// After handling each query checks if \p IsDone is set true and exits the loop -/// if it is. -/// Input stream(\p In) must be opened in binary mode to avoid preliminary -/// replacements of \r\n with \n. -/// We use C-style FILE* for reading as std::istream has unclear interaction -/// with signals, which are sent by debuggers on some OSs. -void runLanguageServerLoop(std::FILE *In, JSONOutput &Out, - JSONStreamStyle InputStyle, - JSONRPCDispatcher &Dispatcher, bool &IsDone); } // namespace clangd } // namespace clang diff --git a/clangd/JSONTransport.cpp b/clangd/JSONTransport.cpp new file mode 100644 index 000000000..0d5f409ba --- /dev/null +++ b/clangd/JSONTransport.cpp @@ -0,0 +1,298 @@ +//===--- JSONTransport.cpp - sending and receiving LSP messages over JSON -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "Logger.h" +#include "Protocol.h" // For LSPError +#include "Transport.h" +#include "llvm/Support/Errno.h" + +using namespace llvm; +namespace clang { +namespace clangd { +namespace { + +json::Object encodeError(Error E) { + std::string Message; + ErrorCode Code = ErrorCode::UnknownErrorCode; + if (Error Unhandled = + handleErrors(std::move(E), [&](const LSPError &L) -> Error { + Message = L.Message; + Code = L.Code; + return Error::success(); + })) + Message = llvm::toString(std::move(Unhandled)); + + return json::Object{ + {"message", std::move(Message)}, + {"code", int64_t(Code)}, + }; +} + +Error decodeError(const json::Object &O) { + std::string Msg = O.getString("message").getValueOr("Unspecified error"); + if (auto Code = O.getInteger("code")) + return make_error(std::move(Msg), ErrorCode(*Code)); + return make_error(std::move(Msg), inconvertibleErrorCode()); +} + +class JSONTransport : public Transport { +public: + JSONTransport(std::FILE *In, llvm::raw_ostream &Out, + llvm::raw_ostream *InMirror, bool Pretty, JSONStreamStyle Style) + : In(In), Out(Out), InMirror(InMirror ? *InMirror : nulls()), + Pretty(Pretty), Style(Style) {} + + void notify(StringRef Method, json::Value Params) override { + sendMessage(json::Object{ + {"jsonrpc", "2.0"}, + {"method", Method}, + {"params", std::move(Params)}, + }); + } + void call(StringRef Method, json::Value Params, json::Value ID) override { + sendMessage(json::Object{ + {"jsonrpc", "2.0"}, + {"id", std::move(ID)}, + {"method", Method}, + {"params", std::move(Params)}, + }); + } + void reply(json::Value ID, Expected Result) override { + if (Result) { + sendMessage(json::Object{ + {"jsonrpc", "2.0"}, + {"id", std::move(ID)}, + {"result", std::move(*Result)}, + }); + } else { + sendMessage(json::Object{ + {"jsonrpc", "2.0"}, + {"id", std::move(ID)}, + {"error", encodeError(Result.takeError())}, + }); + } + } + + Error loop(MessageHandler &Handler) override { + while (!feof(In)) { + if (ferror(In)) + return errorCodeToError(std::error_code(errno, std::system_category())); + if (auto JSON = readRawMessage()) { + if (auto Doc = json::parse(*JSON)) { + vlog(Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc); + if (!handleMessage(std::move(*Doc), Handler)) + return Error::success(); // we saw the "exit" notification. + } else { + // Parse error. Log the raw message. + vlog("<<< {0}\n", *JSON); + elog("JSON parse error: {0}", llvm::toString(Doc.takeError())); + } + } + } + return errorCodeToError(std::make_error_code(std::errc::io_error)); + } + +private: + // Dispatches incoming message to Handler onNotify/onCall/onReply. + bool handleMessage(llvm::json::Value Message, MessageHandler &Handler); + // Writes outgoing message to Out stream. + void sendMessage(llvm::json::Value Message) { + std::string S; + llvm::raw_string_ostream OS(S); + OS << llvm::formatv(Pretty ? "{0:2}" : "{0}", Message); + OS.flush(); + Out << "Content-Length: " << S.size() << "\r\n\r\n" << S; + Out.flush(); + vlog(">>> {0}\n", S); + } + + // Read raw string messages from input stream. + llvm::Optional readRawMessage() { + return Style == JSONStreamStyle::Delimited ? readDelimitedMessage() + : readStandardMessage(); + } + llvm::Optional readDelimitedMessage(); + llvm::Optional readStandardMessage(); + + std::FILE *In; + llvm::raw_ostream &Out; + llvm::raw_ostream &InMirror; + bool Pretty; + JSONStreamStyle Style; +}; + +bool JSONTransport::handleMessage(llvm::json::Value Message, + MessageHandler &Handler) { + // Message must be an object with "jsonrpc":"2.0". + auto *Object = Message.getAsObject(); + if (!Object || Object->getString("jsonrpc") != Optional("2.0")) { + elog("Not a JSON-RPC 2.0 message: {0:2}", Message); + return false; + } + // ID may be any JSON value. If absent, this is a notification. + llvm::Optional ID; + if (auto *I = Object->get("id")) + ID = std::move(*I); + auto Method = Object->getString("method"); + if (!Method) { // This is a response. + if (!ID) { + elog("No method and no response ID: {0:2}", Message); + return false; + } + if (auto *Err = Object->getObject("error")) + return Handler.onReply(std::move(*ID), decodeError(*Err)); + // Result should be given, use null if not. + json::Value Result = nullptr; + if (auto *R = Object->get("result")) + Result = std::move(*R); + return Handler.onReply(std::move(*ID), std::move(Result)); + } + // Params should be given, use null if not. + json::Value Params = nullptr; + if (auto *P = Object->get("params")) + Params = std::move(*P); + + if (ID) + return Handler.onCall(*Method, std::move(Params), std::move(*ID)); + else + return Handler.onNotify(*Method, std::move(Params)); +} + +// Tries to read a line up to and including \n. +// If failing, feof() or ferror() will be set. +bool readLine(std::FILE *In, std::string &Out) { + static constexpr int BufSize = 1024; + size_t Size = 0; + Out.clear(); + for (;;) { + Out.resize(Size + BufSize); + // Handle EINTR which is sent when a debugger attaches on some platforms. + if (!llvm::sys::RetryAfterSignal(nullptr, ::fgets, &Out[Size], BufSize, In)) + return false; + clearerr(In); + // If the line contained null bytes, anything after it (including \n) will + // be ignored. Fortunately this is not a legal header or JSON. + size_t Read = std::strlen(&Out[Size]); + if (Read > 0 && Out[Size + Read - 1] == '\n') { + Out.resize(Size + Read); + return true; + } + Size += Read; + } +} + +// Returns None when: +// - ferror() or feof() are set. +// - Content-Length is missing or empty (protocol error) +llvm::Optional JSONTransport::readStandardMessage() { + // A Language Server Protocol message starts with a set of HTTP headers, + // delimited by \r\n, and terminated by an empty line (\r\n). + unsigned long long ContentLength = 0; + std::string Line; + while (true) { + if (feof(In) || ferror(In) || !readLine(In, Line)) + return llvm::None; + InMirror << Line; + + llvm::StringRef LineRef(Line); + + // We allow comments in headers. Technically this isn't part + + // of the LSP specification, but makes writing tests easier. + if (LineRef.startswith("#")) + continue; + + // Content-Length is a mandatory header, and the only one we handle. + if (LineRef.consume_front("Content-Length: ")) { + if (ContentLength != 0) { + elog("Warning: Duplicate Content-Length header received. " + "The previous value for this message ({0}) was ignored.", + ContentLength); + } + llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength); + continue; + } else if (!LineRef.trim().empty()) { + // It's another header, ignore it. + continue; + } else { + // An empty line indicates the end of headers. + // Go ahead and read the JSON. + break; + } + } + + // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999" + if (ContentLength > 1 << 30) { // 1024M + elog("Refusing to read message with long Content-Length: {0}. " + "Expect protocol errors", + ContentLength); + return llvm::None; + } + if (ContentLength == 0) { + log("Warning: Missing Content-Length header, or zero-length message."); + return llvm::None; + } + + std::string JSON(ContentLength, '\0'); + for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) { + // Handle EINTR which is sent when a debugger attaches on some platforms. + Read = llvm::sys::RetryAfterSignal(0u, ::fread, &JSON[Pos], 1, + ContentLength - Pos, In); + if (Read == 0) { + elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos, + ContentLength); + return llvm::None; + } + InMirror << StringRef(&JSON[Pos], Read); + clearerr(In); // If we're done, the error was transient. If we're not done, + // either it was transient or we'll see it again on retry. + Pos += Read; + } + return std::move(JSON); +} + +// For lit tests we support a simplified syntax: +// - messages are delimited by '---' on a line by itself +// - lines starting with # are ignored. +// This is a testing path, so favor simplicity over performance here. +// When returning None, feof() or ferror() will be set. +llvm::Optional JSONTransport::readDelimitedMessage() { + std::string JSON; + std::string Line; + while (readLine(In, Line)) { + InMirror << Line; + auto LineRef = llvm::StringRef(Line).trim(); + if (LineRef.startswith("#")) // comment + continue; + + // found a delimiter + if (LineRef.rtrim() == "---") + break; + + JSON += Line; + } + + if (ferror(In)) { + elog("Input error while reading message!"); + return llvm::None; + } + return std::move(JSON); // Including at EOF +} + +} // namespace + +std::unique_ptr newJSONTransport(std::FILE *In, + llvm::raw_ostream &Out, + llvm::raw_ostream *InMirror, + bool Pretty, + JSONStreamStyle Style) { + return llvm::make_unique(In, Out, InMirror, Pretty, Style); +} + +} // namespace clangd +} // namespace clang diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp index daab1328c..a1e8226b3 100644 --- a/clangd/Protocol.cpp +++ b/clangd/Protocol.cpp @@ -25,6 +25,8 @@ namespace clang { namespace clangd { using namespace llvm; +char LSPError::ID; + URIForFile::URIForFile(std::string AbsPath) { assert(llvm::sys::path::is_absolute(AbsPath) && "the path is relative"); File = std::move(AbsPath); diff --git a/clangd/Protocol.h b/clangd/Protocol.h index 7026d47b9..940c78f1d 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -48,6 +48,23 @@ enum class ErrorCode { // Defined by the protocol. RequestCancelled = -32800, }; +// Models an LSP error as an llvm::Error. +class LSPError : public llvm::ErrorInfo { +public: + std::string Message; + ErrorCode Code; + static char ID; + + LSPError(std::string Message, ErrorCode Code) + : Message(std::move(Message)), Code(Code) {} + + void log(llvm::raw_ostream &OS) const override { + OS << int(Code) << ": " << Message; + } + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } +}; struct URIForFile { URIForFile() = default; diff --git a/clangd/ProtocolHandlers.cpp b/clangd/ProtocolHandlers.cpp index 73da45eca..61efeb73e 100644 --- a/clangd/ProtocolHandlers.cpp +++ b/clangd/ProtocolHandlers.cpp @@ -35,6 +35,7 @@ struct HandlerRegisterer { } else { elog("Failed to decode {0} request.", Method); } + return Method != "exit"; // Shut down after exit notification. }); } diff --git a/clangd/Transport.h b/clangd/Transport.h new file mode 100644 index 000000000..2de95657a --- /dev/null +++ b/clangd/Transport.h @@ -0,0 +1,92 @@ +//===--- Transport.h - sending and receiving LSP messages -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The language server protocol is usually implemented by writing messages as +// JSON-RPC over the stdin/stdout of a subprocess. However other communications +// mechanisms are possible, such as XPC on mac. +// +// The Transport interface allows the mechanism to be replaced, and the JSONRPC +// Transport is the standard implementation. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_TRANSPORT_H_ +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_TRANSPORT_H_ + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace clangd { + +// A transport is responsible for maintaining the connection to a client +// application, and reading/writing structured messages to it. +// +// Transports have limited thread safety requirements: +// - messages will not be sent concurrently +// - messages MAY be sent while loop() is reading, or its callback is active +class Transport { +public: + virtual ~Transport() = default; + + // Called by Clangd to send messages to the client. + virtual void notify(llvm::StringRef Method, llvm::json::Value Params) = 0; + virtual void call(llvm::StringRef Method, llvm::json::Value Params, + llvm::json::Value ID) = 0; + virtual void reply(llvm::json::Value ID, + llvm::Expected Result) = 0; + + // Implemented by Clangd to handle incoming messages. (See loop() below). + class MessageHandler { + public: + virtual ~MessageHandler() = default; + // Handler returns true to keep processing messages, or false to shut down. + virtual bool onNotify(llvm::StringRef Method, llvm::json::Value) = 0; + virtual bool onCall(llvm::StringRef Method, llvm::json::Value Params, + llvm::json::Value ID) = 0; + virtual bool onReply(llvm::json::Value ID, + llvm::Expected Result) = 0; + }; + // Called by Clangd to receive messages from the client. + // The transport should in turn invoke the handler to process messages. + // If handler returns false, the transport should immedately exit the loop. + // (This is used to implement the `exit` notification). + // Otherwise, it returns an error when the transport becomes unusable. + virtual llvm::Error loop(MessageHandler &) = 0; +}; + +// Controls the way JSON-RPC messages are encoded (both input and output). +enum JSONStreamStyle { + // Encoding per the LSP specification, with mandatory Content-Length header. + Standard, + // Messages are delimited by a '---' line. Comment lines start with #. + Delimited +}; + +// Returns a Transport that speaks JSON-RPC over a pair of streams. +// The input stream must be opened in binary mode. +// If InMirror is set, data read will be echoed to it. +// +// The use of C-style std::FILE* input deserves some explanation. +// Previously, std::istream was used. When a debugger attached on MacOS, the +// process received EINTR, the stream went bad, and clangd exited. +// A retry-on-EINTR loop around reads solved this problem, but caused clangd to +// sometimes hang rather than exit on other OSes. The interaction between +// istreams and signals isn't well-specified, so it's hard to get this right. +// The C APIs seem to be clearer in this respect. +std::unique_ptr +newJSONTransport(std::FILE *In, llvm::raw_ostream &Out, + llvm::raw_ostream *InMirror, bool Pretty, + JSONStreamStyle = JSONStreamStyle::Standard); + +} // namespace clangd +} // namespace clang + +#endif diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index dcfc6f70f..8ec6716ca 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -253,11 +253,8 @@ int main(int argc, char *argv[]) { // Use buffered stream to stderr (we still flush each log message). Unbuffered // stream can cause significant (non-deterministic) latency for the logger. llvm::errs().SetBuffered(); - JSONOutput Out(llvm::outs(), llvm::errs(), LogLevel, - InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, - PrettyPrint); - - clangd::LoggingSession LoggingSession(Out); + JSONOutput Logger(llvm::errs(), LogLevel); + clangd::LoggingSession LoggingSession(Logger); // If --compile-commands-dir arg was invoked, check value and override default // path. @@ -317,12 +314,16 @@ int main(int argc, char *argv[]) { CCOpts.AllScopes = AllScopesCompletion; // Initialize and run ClangdLSPServer. + // Change stdin to binary to not lose \r\n on windows. + llvm::sys::ChangeStdinToBinary(); + auto Transport = newJSONTransport( + stdin, llvm::outs(), + InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, PrettyPrint, + InputStyle); ClangdLSPServer LSPServer( - Out, CCOpts, CompileCommandsDirPath, + *Transport, CCOpts, CompileCommandsDirPath, /*ShouldUseInMemoryCDB=*/CompileArgsFrom == LSPCompileArgs, Opts); constexpr int NoShutdownRequestErrorCode = 1; llvm::set_thread_name("clangd.main"); - // Change stdin to binary to not lose \r\n on windows. - llvm::sys::ChangeStdinToBinary(); - return LSPServer.run(stdin, InputStyle) ? 0 : NoShutdownRequestErrorCode; + return LSPServer.run() ? 0 : NoShutdownRequestErrorCode; } diff --git a/test/clangd/compile-commands-path-in-initialize.test b/test/clangd/compile-commands-path-in-initialize.test index ef7cd4f48..87086e3c3 100644 --- a/test/clangd/compile-commands-path-in-initialize.test +++ b/test/clangd/compile-commands-path-in-initialize.test @@ -24,3 +24,5 @@ # CHECK-NEXT: "message": "MACRO is one", --- {"jsonrpc":"2.0","id":10000,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/completion-snippets.test b/test/clangd/completion-snippets.test index 965ae587f..22cd0821b 100644 --- a/test/clangd/completion-snippets.test +++ b/test/clangd/completion-snippets.test @@ -52,3 +52,5 @@ # CHECK-NEXT: } --- {"jsonrpc":"2.0","id":4,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/completion.test b/test/clangd/completion.test index 2283059e4..0094d4740 100644 --- a/test/clangd/completion.test +++ b/test/clangd/completion.test @@ -68,3 +68,5 @@ # CHECK-NEXT: ] --- {"jsonrpc":"2.0","id":4,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/crash-non-added-files.test b/test/clangd/crash-non-added-files.test index d86f7d26d..023c4d68e 100644 --- a/test/clangd/crash-non-added-files.test +++ b/test/clangd/crash-non-added-files.test @@ -32,3 +32,5 @@ {"jsonrpc":"2.0","id":6,"method":"shutdown"} --- {"jsonrpc":"2.0","method":"exit"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/execute-command.test b/test/clangd/execute-command.test index 492006fdf..85d4b9b88 100644 --- a/test/clangd/execute-command.test +++ b/test/clangd/execute-command.test @@ -62,3 +62,5 @@ {"jsonrpc":"2.0","id":9,"method":"workspace/executeCommand","params":{"arguments":[{"custom":"foo", "changes":{"test:///foo.c":[{"range":{"start":{"line":0,"character":32},"end":{"line":0,"character":32}},"newText":"("},{"range":{"start":{"line":0,"character":37},"end":{"line":0,"character":37}},"newText":")"}]}}],"command":"clangd.applyFix"}} --- {"jsonrpc":"2.0","id":3,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/input-mirror.test b/test/clangd/input-mirror.test index 88409c4d7..52845621e 100644 --- a/test/clangd/input-mirror.test +++ b/test/clangd/input-mirror.test @@ -12,3 +12,6 @@ Content-Length: 172 Content-Length: 44 {"jsonrpc":"2.0","id":3,"method":"shutdown"} +Content-Length: 33 + +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/signature-help.test b/test/clangd/signature-help.test index f13f49c2a..37b8b500b 100644 --- a/test/clangd/signature-help.test +++ b/test/clangd/signature-help.test @@ -23,3 +23,5 @@ # CHECK-NEXT: } --- {"jsonrpc":"2.0","id":100000,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/textdocument-didchange-fail.test b/test/clangd/textdocument-didchange-fail.test index 3bd01e96b..33350c4a3 100644 --- a/test/clangd/textdocument-didchange-fail.test +++ b/test/clangd/textdocument-didchange-fail.test @@ -35,3 +35,5 @@ # CHECK-NEXT:} --- {"jsonrpc":"2.0","id":4,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/trace.test b/test/clangd/trace.test index 9036128d4..83b040a51 100644 --- a/test/clangd/trace.test +++ b/test/clangd/trace.test @@ -21,3 +21,5 @@ # CHECK: }, --- {"jsonrpc":"2.0","id":5,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/xrefs.test b/test/clangd/xrefs.test index f2e17c4b8..58ca44cb0 100644 --- a/test/clangd/xrefs.test +++ b/test/clangd/xrefs.test @@ -55,3 +55,5 @@ # CHECK-NEXT: ] --- {"jsonrpc":"2.0","id":10000,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/unittests/clangd/CMakeLists.txt b/unittests/clangd/CMakeLists.txt index 85eb8b290..07341c80a 100644 --- a/unittests/clangd/CMakeLists.txt +++ b/unittests/clangd/CMakeLists.txt @@ -27,6 +27,7 @@ add_extra_unittest(ClangdTests GlobalCompilationDatabaseTests.cpp HeadersTests.cpp IndexTests.cpp + JSONTransportTests.cpp QualityTests.cpp RIFFTests.cpp SerializationTests.cpp diff --git a/unittests/clangd/JSONTransportTests.cpp b/unittests/clangd/JSONTransportTests.cpp new file mode 100644 index 000000000..c00dcce81 --- /dev/null +++ b/unittests/clangd/JSONTransportTests.cpp @@ -0,0 +1,199 @@ +//===-- JSONTransportTests.cpp -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "Protocol.h" +#include "Transport.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; +namespace clang { +namespace clangd { +namespace { + +// No fmemopen on windows, so we can't easily run this test. +#ifndef WIN32 + +// Fixture takes care of managing the input/output buffers for the transport. +class JSONTransportTest : public ::testing::Test { + std::string InBuf, OutBuf, MirrorBuf; + llvm::raw_string_ostream Out, Mirror; + std::unique_ptr In; + +protected: + JSONTransportTest() : Out(OutBuf), Mirror(MirrorBuf), In(nullptr, nullptr) {} + + template + std::unique_ptr transport(std::string InData, bool Pretty, + JSONStreamStyle Style) { + InBuf = std::move(InData); + In = {fmemopen(&InBuf[0], InBuf.size(), "r"), &fclose}; + return newJSONTransport(In.get(), Out, &Mirror, Pretty, Style); + } + + std::string input() const { return InBuf; } + std::string output() { return Out.str(); } + std::string input_mirror() { return Mirror.str(); } +}; + +// Echo is a simple server running on a transport: +// - logs each message it gets. +// - when it gets a call, replies to it +// - when it gets a notification for method "call", makes a call on Target +// Hangs up when it gets an exit notification. +class Echo : public Transport::MessageHandler { + Transport &Target; + std::string LogBuf; + raw_string_ostream Log; + +public: + Echo(Transport &Target) : Target(Target), Log(LogBuf) {} + + std::string log() { return Log.str(); } + + bool onNotify(StringRef Method, json::Value Params) override { + Log << "Notification " << Method << ": " << Params << "\n"; + if (Method == "call") + Target.call("echo call", std::move(Params), 42); + return Method != "exit"; + } + + bool onCall(StringRef Method, json::Value Params, json::Value ID) override { + Log << "Call " << Method << "(" << ID << "): " << Params << "\n"; + if (Method == "err") + Target.reply(ID, make_error("trouble at mill", ErrorCode(88))); + else + Target.reply(ID, std::move(Params)); + return true; + } + + bool onReply(json::Value ID, Expected Params) override { + if (Params) + Log << "Reply(" << ID << "): " << *Params << "\n"; + else + Log << "Reply(" << ID + << "): error = " << llvm::toString(Params.takeError()) << "\n"; + return true; + } +}; + +std::string trim(StringRef S) { return S.trim().str(); } + +// Runs an Echo session using the standard JSON-RPC format we use in production. +TEST_F(JSONTransportTest, StandardDense) { + auto T = transport( + "Content-Length: 52\r\n\r\n" + R"({"jsonrpc": "2.0", "method": "call", "params": 1234})" + "Content-Length: 46\r\n\r\n" + R"({"jsonrpc": "2.0", "id": 1234, "result": 5678})" + "Content-Length: 67\r\n\r\n" + R"({"jsonrpc": "2.0", "method": "foo", "id": "abcd", "params": "efgh"})" + "Content-Length: 73\r\n\r\n" + R"({"jsonrpc": "2.0", "id": "xyz", "error": {"code": 99, "message": "bad!"}})" + "Content-Length: 68\r\n\r\n" + R"({"jsonrpc": "2.0", "method": "err", "id": "wxyz", "params": "boom!"})" + "Content-Length: 36\r\n\r\n" + R"({"jsonrpc": "2.0", "method": "exit"})", + /*Pretty=*/false, JSONStreamStyle::Standard); + Echo E(*T); + auto Err = T->loop(E); + EXPECT_FALSE(bool(Err)) << llvm::toString(std::move(Err)); + + EXPECT_EQ(trim(E.log()), trim(R"( +Notification call: 1234 +Reply(1234): 5678 +Call foo("abcd"): "efgh" +Reply("xyz"): error = 99: bad! +Call err("wxyz"): "boom!" +Notification exit: null + )")); + EXPECT_EQ( + trim(output()), + "Content-Length: 60\r\n\r\n" + R"({"id":42,"jsonrpc":"2.0","method":"echo call","params":1234})" + "Content-Length: 45\r\n\r\n" + R"({"id":"abcd","jsonrpc":"2.0","result":"efgh"})" + "Content-Length: 77\r\n\r\n" + R"({"error":{"code":88,"message":"trouble at mill"},"id":"wxyz","jsonrpc":"2.0"})"); + EXPECT_EQ(trim(input_mirror()), trim(input())); +} + +// Runs an Echo session using the "delimited" input and pretty-printed output +// that we use in lit tests. +TEST_F(JSONTransportTest, DelimitedPretty) { + auto T = transport(R"jsonrpc( +{"jsonrpc": "2.0", "method": "call", "params": 1234} +--- +{"jsonrpc": "2.0", "id": 1234, "result": 5678} +--- +{"jsonrpc": "2.0", "method": "foo", "id": "abcd", "params": "efgh"} +--- +{"jsonrpc": "2.0", "id": "xyz", "error": {"code": 99, "message": "bad!"}} +--- +{"jsonrpc": "2.0", "method": "err", "id": "wxyz", "params": "boom!"} +--- +{"jsonrpc": "2.0", "method": "exit"} + )jsonrpc", + /*Pretty=*/true, JSONStreamStyle::Delimited); + Echo E(*T); + auto Err = T->loop(E); + EXPECT_FALSE(bool(Err)) << llvm::toString(std::move(Err)); + + EXPECT_EQ(trim(E.log()), trim(R"( +Notification call: 1234 +Reply(1234): 5678 +Call foo("abcd"): "efgh" +Reply("xyz"): error = 99: bad! +Call err("wxyz"): "boom!" +Notification exit: null + )")); + EXPECT_EQ(trim(output()), "Content-Length: 77\r\n\r\n" + R"({ + "id": 42, + "jsonrpc": "2.0", + "method": "echo call", + "params": 1234 +})" + "Content-Length: 58\r\n\r\n" + R"({ + "id": "abcd", + "jsonrpc": "2.0", + "result": "efgh" +})" + "Content-Length: 105\r\n\r\n" + R"({ + "error": { + "code": 88, + "message": "trouble at mill" + }, + "id": "wxyz", + "jsonrpc": "2.0" +})"); + EXPECT_EQ(trim(input_mirror()), trim(input())); +} + +// IO errors such as EOF ane reported. +// The only successful return from loop() is if a handler returned false. +TEST_F(JSONTransportTest, EndOfFile) { + auto T = transport("Content-Length: 52\r\n\r\n" + R"({"jsonrpc": "2.0", "method": "call", "params": 1234})", + /*Pretty=*/false, JSONStreamStyle::Standard); + Echo E(*T); + auto Err = T->loop(E); + EXPECT_EQ(trim(E.log()), "Notification call: 1234"); + EXPECT_TRUE(bool(Err)); // Ran into EOF with no handler signalling done. + consumeError(std::move(Err)); + EXPECT_EQ(trim(input_mirror()), trim(input())); +} + +#endif + +} // namespace +} // namespace clangd +} // namespace clang From 8c67700635a95fffe9cc74414452512a091244ea Mon Sep 17 00:00:00 2001 From: Krasimir Georgiev Date: Tue, 16 Oct 2018 18:44:41 +0000 Subject: [PATCH 377/686] Revert "[clangd] Refactor JSON-over-stdin/stdout code into Transport abstraction." This reverts commit r344620. Breaks upstream bots. git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344637 91177308-0d34-0410-b5e6-96231b3b80d8 --- clangd/CMakeLists.txt | 1 - clangd/ClangdLSPServer.cpp | 43 ++- clangd/ClangdLSPServer.h | 21 +- clangd/JSONRPCDispatcher.cpp | 301 +++++++++++++++--- clangd/JSONRPCDispatcher.h | 67 ++-- clangd/JSONTransport.cpp | 298 ----------------- clangd/Protocol.cpp | 2 - clangd/Protocol.h | 17 - clangd/ProtocolHandlers.cpp | 1 - clangd/Transport.h | 92 ------ clangd/tool/ClangdMain.cpp | 19 +- .../compile-commands-path-in-initialize.test | 2 - test/clangd/completion-snippets.test | 2 - test/clangd/completion.test | 2 - test/clangd/crash-non-added-files.test | 2 - test/clangd/execute-command.test | 2 - test/clangd/input-mirror.test | 3 - test/clangd/signature-help.test | 2 - test/clangd/textdocument-didchange-fail.test | 2 - test/clangd/trace.test | 2 - test/clangd/xrefs.test | 2 - unittests/clangd/CMakeLists.txt | 1 - unittests/clangd/JSONTransportTests.cpp | 199 ------------ 23 files changed, 341 insertions(+), 742 deletions(-) delete mode 100644 clangd/JSONTransport.cpp delete mode 100644 clangd/Transport.h delete mode 100644 unittests/clangd/JSONTransportTests.cpp diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt index 2fa7cd2f2..4eeaffcbf 100644 --- a/clangd/CMakeLists.txt +++ b/clangd/CMakeLists.txt @@ -26,7 +26,6 @@ add_clang_library(clangDaemon GlobalCompilationDatabase.cpp Headers.cpp JSONRPCDispatcher.cpp - JSONTransport.cpp Logger.cpp Protocol.cpp ProtocolHandlers.cpp diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp index 3b6724078..b1863062d 100644 --- a/clangd/ClangdLSPServer.cpp +++ b/clangd/ClangdLSPServer.cpp @@ -162,10 +162,7 @@ void ClangdLSPServer::onShutdown(ShutdownParams &Params) { reply(nullptr); } -void ClangdLSPServer::onExit(ExitParams &Params) { - // No work to do. - // JSONRPCDispatcher shuts down the transport after this notification. -} +void ClangdLSPServer::onExit(ExitParams &Params) { IsDone = true; } void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams &Params) { PathRef File = Params.textDocument.uri.file(); @@ -500,41 +497,39 @@ void ClangdLSPServer::onReference(ReferenceParams &Params) { }); } -ClangdLSPServer::ClangdLSPServer(class Transport &Transport, +ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts, llvm::Optional CompileCommandsDir, bool ShouldUseInMemoryCDB, const ClangdServer::Options &Opts) - : Transport(Transport), - CDB(ShouldUseInMemoryCDB ? CompilationDB::makeInMemory() - : CompilationDB::makeDirectoryBased( - std::move(CompileCommandsDir))), + : Out(Out), CDB(ShouldUseInMemoryCDB ? CompilationDB::makeInMemory() + : CompilationDB::makeDirectoryBased( + std::move(CompileCommandsDir))), CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()), SupportedCompletionItemKinds(defaultCompletionItemKinds()), Server(new ClangdServer(CDB.getCDB(), FSProvider, /*DiagConsumer=*/*this, Opts)) {} -bool ClangdLSPServer::run() { +bool ClangdLSPServer::run(std::FILE *In, JSONStreamStyle InputStyle) { + assert(!IsDone && "Run was called before"); assert(Server); // Set up JSONRPCDispatcher. JSONRPCDispatcher Dispatcher([](const json::Value &Params) { replyError(ErrorCode::MethodNotFound, "method not found"); - return true; }); registerCallbackHandlers(Dispatcher, /*Callbacks=*/*this); // Run the Language Server loop. - bool CleanExit = true; - if (auto Err = Dispatcher.runLanguageServerLoop(Transport)) { - elog("Transport error: {0}", std::move(Err)); - CleanExit = false; - } + runLanguageServerLoop(In, Out, InputStyle, Dispatcher, IsDone); + // Make sure IsDone is set to true after this method exits to ensure assertion + // at the start of the method fires if it's ever executed again. + IsDone = true; // Destroy ClangdServer to ensure all worker threads finish. Server.reset(); - return CleanExit && ShutdownRequestReceived; + return ShutdownRequestReceived; } std::vector ClangdLSPServer::getFixes(StringRef File, @@ -594,11 +589,15 @@ void ClangdLSPServer::onDiagnosticsReady(PathRef File, } // Publish diagnostics. - Transport.notify("textDocument/publishDiagnostics", - json::Object{ - {"uri", URIForFile{File}}, - {"diagnostics", std::move(DiagnosticsJSON)}, - }); + Out.writeMessage(json::Object{ + {"jsonrpc", "2.0"}, + {"method", "textDocument/publishDiagnostics"}, + {"params", + json::Object{ + {"uri", URIForFile{File}}, + {"diagnostics", std::move(DiagnosticsJSON)}, + }}, + }); } void ClangdLSPServer::reparseOpenedFiles() { diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h index 3c7458d3e..6fb524837 100644 --- a/clangd/ClangdLSPServer.h +++ b/clangd/ClangdLSPServer.h @@ -37,16 +37,18 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { /// If \p CompileCommandsDir has a value, compile_commands.json will be /// loaded only from \p CompileCommandsDir. Otherwise, clangd will look /// for compile_commands.json in all parent directories of each file. - ClangdLSPServer(Transport &Transport, - const clangd::CodeCompleteOptions &CCOpts, + ClangdLSPServer(JSONOutput &Out, const clangd::CodeCompleteOptions &CCOpts, llvm::Optional CompileCommandsDir, bool ShouldUseInMemoryCDB, const ClangdServer::Options &Opts); - /// Run LSP server loop, communicating with the Transport provided in the - /// constructor. This method must not be executed more than once. + /// Run LSP server loop, receiving input for it from \p In. \p In must be + /// opened in binary mode. Output will be written using Out variable passed to + /// class constructor. This method must not be executed more than once for + /// each instance of ClangdLSPServer. /// - /// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence. - bool run(); + /// \return Whether we received a 'shutdown' request before an 'exit' request. + bool run(std::FILE *In, + JSONStreamStyle InputStyle = JSONStreamStyle::Standard); private: // Implement DiagnosticsConsumer. @@ -87,10 +89,16 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { void reparseOpenedFiles(); void applyConfiguration(const ClangdConfigurationParamsChange &Settings); + JSONOutput &Out; /// Used to indicate that the 'shutdown' request was received from the /// Language Server client. bool ShutdownRequestReceived = false; + /// Used to indicate that the 'exit' notification was received from the + /// Language Server client. + /// It's used to break out of the LSP parsing loop. + bool IsDone = false; + std::mutex FixItsMutex; typedef std::map, LSPDiagnosticCompare> DiagnosticToReplacementMap; @@ -145,7 +153,6 @@ class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks { bool IsDirectoryBased; }; - clangd::Transport &Transport; // Various ClangdServer parameters go here. It's important they're created // before ClangdServer. CompilationDB CDB; diff --git a/clangd/JSONRPCDispatcher.cpp b/clangd/JSONRPCDispatcher.cpp index 20a2cbabe..4fe4f5596 100644 --- a/clangd/JSONRPCDispatcher.cpp +++ b/clangd/JSONRPCDispatcher.cpp @@ -11,7 +11,6 @@ #include "Cancellation.h" #include "ProtocolHandlers.h" #include "Trace.h" -#include "Transport.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" @@ -29,7 +28,7 @@ using namespace clangd; namespace { static Key RequestID; -static Key CurrentTransport; +static Key RequestOut; // When tracing, we trace a request and attach the response in reply(). // Because the Span isn't available, we find the current request using Context. @@ -59,6 +58,23 @@ class RequestSpan { Key> RequestSpan::RSKey; } // namespace +void JSONOutput::writeMessage(const json::Value &Message) { + std::string S; + llvm::raw_string_ostream OS(S); + if (Pretty) + OS << llvm::formatv("{0:2}", Message); + else + OS << Message; + OS.flush(); + + { + std::lock_guard Guard(StreamMutex); + Outs << "Content-Length: " << S.size() << "\r\n\r\n" << S; + Outs.flush(); + } + vlog(">>> {0}\n", S); +} + void JSONOutput::log(Logger::Level Level, const llvm::formatv_object_base &Message) { if (Level < MinLevel) @@ -71,6 +87,14 @@ void JSONOutput::log(Logger::Level Level, Logs.flush(); } +void JSONOutput::mirrorInput(const Twine &Message) { + if (!InputMirror) + return; + + *InputMirror << Message; + InputMirror->flush(); +} + void clangd::reply(json::Value &&Result) { auto ID = getRequestId(); if (!ID) { @@ -80,8 +104,12 @@ void clangd::reply(json::Value &&Result) { RequestSpan::attach([&](json::Object &Args) { Args["Reply"] = Result; }); log("--> reply({0})", *ID); Context::current() - .getExisting(CurrentTransport) - ->reply(std::move(*ID), std::move(Result)); + .getExisting(RequestOut) + ->writeMessage(json::Object{ + {"jsonrpc", "2.0"}, + {"id", *ID}, + {"result", std::move(Result)}, + }); } void clangd::replyError(ErrorCode Code, const llvm::StringRef &Message) { @@ -94,8 +122,13 @@ void clangd::replyError(ErrorCode Code, const llvm::StringRef &Message) { if (auto ID = getRequestId()) { log("--> reply({0}) error: {1}", *ID, Message); Context::current() - .getExisting(CurrentTransport) - ->reply(std::move(*ID), make_error(Message, Code)); + .getExisting(RequestOut) + ->writeMessage(json::Object{ + {"jsonrpc", "2.0"}, + {"id", *ID}, + {"error", json::Object{{"code", static_cast(Code)}, + {"message", Message}}}, + }); } } @@ -118,20 +151,22 @@ void clangd::call(StringRef Method, json::Value &&Params) { auto ID = 1; log("--> {0}({1})", Method, ID); Context::current() - .getExisting(CurrentTransport) - ->call(Method, std::move(Params), ID); + .getExisting(RequestOut) + ->writeMessage(json::Object{ + {"jsonrpc", "2.0"}, + {"id", ID}, + {"method", Method}, + {"params", std::move(Params)}, + }); } JSONRPCDispatcher::JSONRPCDispatcher(Handler UnknownHandler) : UnknownHandler(std::move(UnknownHandler)) { registerHandler("$/cancelRequest", [this](const json::Value &Params) { if (auto *O = Params.getAsObject()) - if (auto *ID = O->get("id")) { - cancelRequest(*ID); - return true; - } + if (auto *ID = O->get("id")) + return cancelRequest(*ID); log("Bad cancellation request: {0}", Params); - return true; }); } @@ -140,48 +175,64 @@ void JSONRPCDispatcher::registerHandler(StringRef Method, Handler H) { Handlers[Method] = std::move(H); } -bool JSONRPCDispatcher::onCall(StringRef Method, json::Value Params, - json::Value ID) { - log("<-- {0}({1})", Method, ID); - auto I = Handlers.find(Method); +static void logIncomingMessage(const llvm::Optional &ID, + llvm::Optional Method, + const json::Object *Error) { + if (Method) { // incoming request + if (ID) // call + log("<-- {0}({1})", *Method, *ID); + else // notification + log("<-- {0}", *Method); + } else if (ID) { // response, ID must be provided + if (Error) + log("<-- reply({0}) error: {1}", *ID, + Error->getString("message").getValueOr("")); + else + log("<-- reply({0})", *ID); + } +} + +bool JSONRPCDispatcher::call(const json::Value &Message, JSONOutput &Out) { + // Message must be an object with "jsonrpc":"2.0". + auto *Object = Message.getAsObject(); + if (!Object || Object->getString("jsonrpc") != Optional("2.0")) + return false; + // ID may be any JSON value. If absent, this is a notification. + llvm::Optional ID; + if (auto *I = Object->get("id")) + ID = std::move(*I); + auto Method = Object->getString("method"); + logIncomingMessage(ID, Method, Object->getObject("error")); + if (!Method) // We only handle incoming requests, and ignore responses. + return false; + // Params should be given, use null if not. + json::Value Params = nullptr; + if (auto *P = Object->get("params")) + Params = std::move(*P); + + auto I = Handlers.find(*Method); auto &Handler = I != Handlers.end() ? I->second : UnknownHandler; // Create a Context that contains request information. - WithContextValue WithID(RequestID, ID); + WithContextValue WithRequestOut(RequestOut, &Out); + llvm::Optional WithID; + if (ID) + WithID.emplace(RequestID, *ID); // Create a tracing Span covering the whole request lifetime. - trace::Span Tracer(Method); - SPAN_ATTACH(Tracer, "ID", ID); + trace::Span Tracer(*Method); + if (ID) + SPAN_ATTACH(Tracer, "ID", *ID); SPAN_ATTACH(Tracer, "Params", Params); - // Calls can be canceled by the client. Add cancellation context. - WithContext WithCancel(cancelableRequestContext(ID)); + // Requests with IDs can be canceled by the client. Add cancellation context. + llvm::Optional WithCancel; + if (ID) + WithCancel.emplace(cancelableRequestContext(*ID)); // Stash a reference to the span args, so later calls can add metadata. WithContext WithRequestSpan(RequestSpan::stash(Tracer)); - return Handler(std::move(Params)); -} - -bool JSONRPCDispatcher::onNotify(StringRef Method, json::Value Params) { - log("<-- {0}", Method); - auto I = Handlers.find(Method); - auto &Handler = I != Handlers.end() ? I->second : UnknownHandler; - - // Create a tracing Span covering the whole request lifetime. - trace::Span Tracer(Method); - SPAN_ATTACH(Tracer, "Params", Params); - - // Stash a reference to the span args, so later calls can add metadata. - WithContext WithRequestSpan(RequestSpan::stash(Tracer)); - return Handler(std::move(Params)); -} - -bool JSONRPCDispatcher::onReply(json::Value ID, Expected Result) { - // We ignore replies, just log them. - if (Result) - log("<-- reply({0})", ID); - else - log("<-- reply({0}) error: {1}", ID, llvm::toString(Result.takeError())); + Handler(std::move(Params)); return true; } @@ -215,10 +266,162 @@ void JSONRPCDispatcher::cancelRequest(const json::Value &ID) { It->second.first(); // Invoke the canceler. } -llvm::Error JSONRPCDispatcher::runLanguageServerLoop(Transport &Transport) { - // Propagate transport to all handlers so they can reply. - WithContextValue WithTransport(CurrentTransport, &Transport); - return Transport.loop(*this); +// Tries to read a line up to and including \n. +// If failing, feof() or ferror() will be set. +static bool readLine(std::FILE *In, std::string &Out) { + static constexpr int BufSize = 1024; + size_t Size = 0; + Out.clear(); + for (;;) { + Out.resize(Size + BufSize); + // Handle EINTR which is sent when a debugger attaches on some platforms. + if (!llvm::sys::RetryAfterSignal(nullptr, ::fgets, &Out[Size], BufSize, In)) + return false; + clearerr(In); + // If the line contained null bytes, anything after it (including \n) will + // be ignored. Fortunately this is not a legal header or JSON. + size_t Read = std::strlen(&Out[Size]); + if (Read > 0 && Out[Size + Read - 1] == '\n') { + Out.resize(Size + Read); + return true; + } + Size += Read; + } +} + +// Returns None when: +// - ferror() or feof() are set. +// - Content-Length is missing or empty (protocol error) +static llvm::Optional readStandardMessage(std::FILE *In, + JSONOutput &Out) { + // A Language Server Protocol message starts with a set of HTTP headers, + // delimited by \r\n, and terminated by an empty line (\r\n). + unsigned long long ContentLength = 0; + std::string Line; + while (true) { + if (feof(In) || ferror(In) || !readLine(In, Line)) + return llvm::None; + + Out.mirrorInput(Line); + llvm::StringRef LineRef(Line); + + // We allow comments in headers. Technically this isn't part + // of the LSP specification, but makes writing tests easier. + if (LineRef.startswith("#")) + continue; + + // Content-Length is a mandatory header, and the only one we handle. + if (LineRef.consume_front("Content-Length: ")) { + if (ContentLength != 0) { + elog("Warning: Duplicate Content-Length header received. " + "The previous value for this message ({0}) was ignored.", + ContentLength); + } + llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength); + continue; + } else if (!LineRef.trim().empty()) { + // It's another header, ignore it. + continue; + } else { + // An empty line indicates the end of headers. + // Go ahead and read the JSON. + break; + } + } + + // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999" + if (ContentLength > 1 << 30) { // 1024M + elog("Refusing to read message with long Content-Length: {0}. " + "Expect protocol errors", + ContentLength); + return llvm::None; + } + if (ContentLength == 0) { + log("Warning: Missing Content-Length header, or zero-length message."); + return llvm::None; + } + + std::string JSON(ContentLength, '\0'); + for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) { + // Handle EINTR which is sent when a debugger attaches on some platforms. + Read = llvm::sys::RetryAfterSignal(0u, ::fread, &JSON[Pos], 1, + ContentLength - Pos, In); + Out.mirrorInput(StringRef(&JSON[Pos], Read)); + if (Read == 0) { + elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos, + ContentLength); + return llvm::None; + } + clearerr(In); // If we're done, the error was transient. If we're not done, + // either it was transient or we'll see it again on retry. + Pos += Read; + } + return std::move(JSON); +} + +// For lit tests we support a simplified syntax: +// - messages are delimited by '---' on a line by itself +// - lines starting with # are ignored. +// This is a testing path, so favor simplicity over performance here. +// When returning None, feof() or ferror() will be set. +static llvm::Optional readDelimitedMessage(std::FILE *In, + JSONOutput &Out) { + std::string JSON; + std::string Line; + while (readLine(In, Line)) { + auto LineRef = llvm::StringRef(Line).trim(); + if (LineRef.startswith("#")) // comment + continue; + + // found a delimiter + if (LineRef.rtrim() == "---") + break; + + JSON += Line; + } + + if (ferror(In)) { + elog("Input error while reading message!"); + return llvm::None; + } else { // Including EOF + Out.mirrorInput( + llvm::formatv("Content-Length: {0}\r\n\r\n{1}", JSON.size(), JSON)); + return std::move(JSON); + } +} + +// The use of C-style std::FILE* IO deserves some explanation. +// Previously, std::istream was used. When a debugger attached on MacOS, the +// process received EINTR, the stream went bad, and clangd exited. +// A retry-on-EINTR loop around reads solved this problem, but caused clangd to +// sometimes hang rather than exit on other OSes. The interaction between +// istreams and signals isn't well-specified, so it's hard to get this right. +// The C APIs seem to be clearer in this respect. +void clangd::runLanguageServerLoop(std::FILE *In, JSONOutput &Out, + JSONStreamStyle InputStyle, + JSONRPCDispatcher &Dispatcher, + bool &IsDone) { + auto &ReadMessage = + (InputStyle == Delimited) ? readDelimitedMessage : readStandardMessage; + while (!IsDone && !feof(In)) { + if (ferror(In)) { + elog("IO error: {0}", llvm::sys::StrError()); + return; + } + if (auto JSON = ReadMessage(In, Out)) { + if (auto Doc = json::parse(*JSON)) { + // Log the formatted message. + vlog(Out.Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc); + // Finally, execute the action for this JSON message. + if (!Dispatcher.call(*Doc, Out)) + elog("JSON dispatch failed!"); + } else { + // Parse error. Log the raw message. + vlog("<<< {0}\n", *JSON); + elog("JSON parse error: {0}", llvm::toString(Doc.takeError())); + } + } + } } const json::Value *clangd::getRequestId() { diff --git a/clangd/JSONRPCDispatcher.h b/clangd/JSONRPCDispatcher.h index 43c466f70..b223ceb5f 100644 --- a/clangd/JSONRPCDispatcher.h +++ b/clangd/JSONRPCDispatcher.h @@ -14,7 +14,6 @@ #include "Logger.h" #include "Protocol.h" #include "Trace.h" -#include "Transport.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" @@ -25,19 +24,37 @@ namespace clang { namespace clangd { -// Logs to an output stream, such as stderr. -// FIXME: Rename to StreamLogger or such, and move to Logger.h. +/// Encapsulates output and logs streams and provides thread-safe access to +/// them. class JSONOutput : public Logger { + // FIXME(ibiryukov): figure out if we can shrink the public interface of + // JSONOutput now that we pass Context everywhere. public: - JSONOutput(llvm::raw_ostream &Logs, Logger::Level MinLevel) - : MinLevel(MinLevel), Logs(Logs) {} + JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs, + Logger::Level MinLevel, llvm::raw_ostream *InputMirror = nullptr, + bool Pretty = false) + : Pretty(Pretty), MinLevel(MinLevel), Outs(Outs), Logs(Logs), + InputMirror(InputMirror) {} + + /// Emit a JSONRPC message. + void writeMessage(const llvm::json::Value &Result); /// Write a line to the logging stream. void log(Level, const llvm::formatv_object_base &Message) override; + /// Mirror \p Message into InputMirror stream. Does nothing if InputMirror is + /// null. + /// Unlike other methods of JSONOutput, mirrorInput is not thread-safe. + void mirrorInput(const Twine &Message); + + // Whether output should be pretty-printed. + const bool Pretty; + private: Logger::Level MinLevel; + llvm::raw_ostream &Outs; llvm::raw_ostream &Logs; + llvm::raw_ostream *InputMirror; std::mutex StreamMutex; }; @@ -64,15 +81,14 @@ void call(llvm::StringRef Method, llvm::json::Value &&Params); /// /// The `$/cancelRequest` notification is handled by the dispatcher itself. /// It marks the matching request as cancelled, if it's still running. -class JSONRPCDispatcher : private Transport::MessageHandler { +class JSONRPCDispatcher { public: /// A handler responds to requests for a particular method name. - /// It returns false if the server should now shut down. /// /// JSONRPCDispatcher will mark the handler's context as cancelled if a /// matching cancellation request is received. Handlers are encouraged to /// check for cancellation and fail quickly in this case. - using Handler = std::function; + using Handler = std::function; /// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown /// method is received. @@ -81,22 +97,10 @@ class JSONRPCDispatcher : private Transport::MessageHandler { /// Registers a Handler for the specified Method. void registerHandler(StringRef Method, Handler H); - /// Parses input queries from LSP client (coming from \p In) and runs call - /// method for each query. - /// - /// Input stream(\p In) must be opened in binary mode to avoid - /// preliminary replacements of \r\n with \n. We use C-style FILE* for reading - /// as std::istream has unclear interaction with signals, which are sent by - /// debuggers on some OSs. - llvm::Error runLanguageServerLoop(Transport &); + /// Parses a JSONRPC message and calls the Handler for it. + bool call(const llvm::json::Value &Message, JSONOutput &Out); private: - bool onReply(llvm::json::Value ID, - llvm::Expected Result) override; - bool onNotify(llvm::StringRef Method, llvm::json::Value Message) override; - bool onCall(llvm::StringRef Method, llvm::json::Value Message, - llvm::json::Value ID) override; - // Tracking cancellations needs a mutex: handlers may finish on a different // thread, and that's when we clean up entries in the map. mutable std::mutex RequestCancelersMutex; @@ -109,6 +113,25 @@ class JSONRPCDispatcher : private Transport::MessageHandler { Handler UnknownHandler; }; +/// Controls the way JSON-RPC messages are encoded (both input and output). +enum JSONStreamStyle { + /// Encoding per the LSP specification, with mandatory Content-Length header. + Standard, + /// Messages are delimited by a '---' line. Comment lines start with #. + Delimited +}; + +/// Parses input queries from LSP client (coming from \p In) and runs call +/// method of \p Dispatcher for each query. +/// After handling each query checks if \p IsDone is set true and exits the loop +/// if it is. +/// Input stream(\p In) must be opened in binary mode to avoid preliminary +/// replacements of \r\n with \n. +/// We use C-style FILE* for reading as std::istream has unclear interaction +/// with signals, which are sent by debuggers on some OSs. +void runLanguageServerLoop(std::FILE *In, JSONOutput &Out, + JSONStreamStyle InputStyle, + JSONRPCDispatcher &Dispatcher, bool &IsDone); } // namespace clangd } // namespace clang diff --git a/clangd/JSONTransport.cpp b/clangd/JSONTransport.cpp deleted file mode 100644 index 0d5f409ba..000000000 --- a/clangd/JSONTransport.cpp +++ /dev/null @@ -1,298 +0,0 @@ -//===--- JSONTransport.cpp - sending and receiving LSP messages over JSON -===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#include "Logger.h" -#include "Protocol.h" // For LSPError -#include "Transport.h" -#include "llvm/Support/Errno.h" - -using namespace llvm; -namespace clang { -namespace clangd { -namespace { - -json::Object encodeError(Error E) { - std::string Message; - ErrorCode Code = ErrorCode::UnknownErrorCode; - if (Error Unhandled = - handleErrors(std::move(E), [&](const LSPError &L) -> Error { - Message = L.Message; - Code = L.Code; - return Error::success(); - })) - Message = llvm::toString(std::move(Unhandled)); - - return json::Object{ - {"message", std::move(Message)}, - {"code", int64_t(Code)}, - }; -} - -Error decodeError(const json::Object &O) { - std::string Msg = O.getString("message").getValueOr("Unspecified error"); - if (auto Code = O.getInteger("code")) - return make_error(std::move(Msg), ErrorCode(*Code)); - return make_error(std::move(Msg), inconvertibleErrorCode()); -} - -class JSONTransport : public Transport { -public: - JSONTransport(std::FILE *In, llvm::raw_ostream &Out, - llvm::raw_ostream *InMirror, bool Pretty, JSONStreamStyle Style) - : In(In), Out(Out), InMirror(InMirror ? *InMirror : nulls()), - Pretty(Pretty), Style(Style) {} - - void notify(StringRef Method, json::Value Params) override { - sendMessage(json::Object{ - {"jsonrpc", "2.0"}, - {"method", Method}, - {"params", std::move(Params)}, - }); - } - void call(StringRef Method, json::Value Params, json::Value ID) override { - sendMessage(json::Object{ - {"jsonrpc", "2.0"}, - {"id", std::move(ID)}, - {"method", Method}, - {"params", std::move(Params)}, - }); - } - void reply(json::Value ID, Expected Result) override { - if (Result) { - sendMessage(json::Object{ - {"jsonrpc", "2.0"}, - {"id", std::move(ID)}, - {"result", std::move(*Result)}, - }); - } else { - sendMessage(json::Object{ - {"jsonrpc", "2.0"}, - {"id", std::move(ID)}, - {"error", encodeError(Result.takeError())}, - }); - } - } - - Error loop(MessageHandler &Handler) override { - while (!feof(In)) { - if (ferror(In)) - return errorCodeToError(std::error_code(errno, std::system_category())); - if (auto JSON = readRawMessage()) { - if (auto Doc = json::parse(*JSON)) { - vlog(Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc); - if (!handleMessage(std::move(*Doc), Handler)) - return Error::success(); // we saw the "exit" notification. - } else { - // Parse error. Log the raw message. - vlog("<<< {0}\n", *JSON); - elog("JSON parse error: {0}", llvm::toString(Doc.takeError())); - } - } - } - return errorCodeToError(std::make_error_code(std::errc::io_error)); - } - -private: - // Dispatches incoming message to Handler onNotify/onCall/onReply. - bool handleMessage(llvm::json::Value Message, MessageHandler &Handler); - // Writes outgoing message to Out stream. - void sendMessage(llvm::json::Value Message) { - std::string S; - llvm::raw_string_ostream OS(S); - OS << llvm::formatv(Pretty ? "{0:2}" : "{0}", Message); - OS.flush(); - Out << "Content-Length: " << S.size() << "\r\n\r\n" << S; - Out.flush(); - vlog(">>> {0}\n", S); - } - - // Read raw string messages from input stream. - llvm::Optional readRawMessage() { - return Style == JSONStreamStyle::Delimited ? readDelimitedMessage() - : readStandardMessage(); - } - llvm::Optional readDelimitedMessage(); - llvm::Optional readStandardMessage(); - - std::FILE *In; - llvm::raw_ostream &Out; - llvm::raw_ostream &InMirror; - bool Pretty; - JSONStreamStyle Style; -}; - -bool JSONTransport::handleMessage(llvm::json::Value Message, - MessageHandler &Handler) { - // Message must be an object with "jsonrpc":"2.0". - auto *Object = Message.getAsObject(); - if (!Object || Object->getString("jsonrpc") != Optional("2.0")) { - elog("Not a JSON-RPC 2.0 message: {0:2}", Message); - return false; - } - // ID may be any JSON value. If absent, this is a notification. - llvm::Optional ID; - if (auto *I = Object->get("id")) - ID = std::move(*I); - auto Method = Object->getString("method"); - if (!Method) { // This is a response. - if (!ID) { - elog("No method and no response ID: {0:2}", Message); - return false; - } - if (auto *Err = Object->getObject("error")) - return Handler.onReply(std::move(*ID), decodeError(*Err)); - // Result should be given, use null if not. - json::Value Result = nullptr; - if (auto *R = Object->get("result")) - Result = std::move(*R); - return Handler.onReply(std::move(*ID), std::move(Result)); - } - // Params should be given, use null if not. - json::Value Params = nullptr; - if (auto *P = Object->get("params")) - Params = std::move(*P); - - if (ID) - return Handler.onCall(*Method, std::move(Params), std::move(*ID)); - else - return Handler.onNotify(*Method, std::move(Params)); -} - -// Tries to read a line up to and including \n. -// If failing, feof() or ferror() will be set. -bool readLine(std::FILE *In, std::string &Out) { - static constexpr int BufSize = 1024; - size_t Size = 0; - Out.clear(); - for (;;) { - Out.resize(Size + BufSize); - // Handle EINTR which is sent when a debugger attaches on some platforms. - if (!llvm::sys::RetryAfterSignal(nullptr, ::fgets, &Out[Size], BufSize, In)) - return false; - clearerr(In); - // If the line contained null bytes, anything after it (including \n) will - // be ignored. Fortunately this is not a legal header or JSON. - size_t Read = std::strlen(&Out[Size]); - if (Read > 0 && Out[Size + Read - 1] == '\n') { - Out.resize(Size + Read); - return true; - } - Size += Read; - } -} - -// Returns None when: -// - ferror() or feof() are set. -// - Content-Length is missing or empty (protocol error) -llvm::Optional JSONTransport::readStandardMessage() { - // A Language Server Protocol message starts with a set of HTTP headers, - // delimited by \r\n, and terminated by an empty line (\r\n). - unsigned long long ContentLength = 0; - std::string Line; - while (true) { - if (feof(In) || ferror(In) || !readLine(In, Line)) - return llvm::None; - InMirror << Line; - - llvm::StringRef LineRef(Line); - - // We allow comments in headers. Technically this isn't part - - // of the LSP specification, but makes writing tests easier. - if (LineRef.startswith("#")) - continue; - - // Content-Length is a mandatory header, and the only one we handle. - if (LineRef.consume_front("Content-Length: ")) { - if (ContentLength != 0) { - elog("Warning: Duplicate Content-Length header received. " - "The previous value for this message ({0}) was ignored.", - ContentLength); - } - llvm::getAsUnsignedInteger(LineRef.trim(), 0, ContentLength); - continue; - } else if (!LineRef.trim().empty()) { - // It's another header, ignore it. - continue; - } else { - // An empty line indicates the end of headers. - // Go ahead and read the JSON. - break; - } - } - - // The fuzzer likes crashing us by sending "Content-Length: 9999999999999999" - if (ContentLength > 1 << 30) { // 1024M - elog("Refusing to read message with long Content-Length: {0}. " - "Expect protocol errors", - ContentLength); - return llvm::None; - } - if (ContentLength == 0) { - log("Warning: Missing Content-Length header, or zero-length message."); - return llvm::None; - } - - std::string JSON(ContentLength, '\0'); - for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) { - // Handle EINTR which is sent when a debugger attaches on some platforms. - Read = llvm::sys::RetryAfterSignal(0u, ::fread, &JSON[Pos], 1, - ContentLength - Pos, In); - if (Read == 0) { - elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos, - ContentLength); - return llvm::None; - } - InMirror << StringRef(&JSON[Pos], Read); - clearerr(In); // If we're done, the error was transient. If we're not done, - // either it was transient or we'll see it again on retry. - Pos += Read; - } - return std::move(JSON); -} - -// For lit tests we support a simplified syntax: -// - messages are delimited by '---' on a line by itself -// - lines starting with # are ignored. -// This is a testing path, so favor simplicity over performance here. -// When returning None, feof() or ferror() will be set. -llvm::Optional JSONTransport::readDelimitedMessage() { - std::string JSON; - std::string Line; - while (readLine(In, Line)) { - InMirror << Line; - auto LineRef = llvm::StringRef(Line).trim(); - if (LineRef.startswith("#")) // comment - continue; - - // found a delimiter - if (LineRef.rtrim() == "---") - break; - - JSON += Line; - } - - if (ferror(In)) { - elog("Input error while reading message!"); - return llvm::None; - } - return std::move(JSON); // Including at EOF -} - -} // namespace - -std::unique_ptr newJSONTransport(std::FILE *In, - llvm::raw_ostream &Out, - llvm::raw_ostream *InMirror, - bool Pretty, - JSONStreamStyle Style) { - return llvm::make_unique(In, Out, InMirror, Pretty, Style); -} - -} // namespace clangd -} // namespace clang diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp index a1e8226b3..daab1328c 100644 --- a/clangd/Protocol.cpp +++ b/clangd/Protocol.cpp @@ -25,8 +25,6 @@ namespace clang { namespace clangd { using namespace llvm; -char LSPError::ID; - URIForFile::URIForFile(std::string AbsPath) { assert(llvm::sys::path::is_absolute(AbsPath) && "the path is relative"); File = std::move(AbsPath); diff --git a/clangd/Protocol.h b/clangd/Protocol.h index 940c78f1d..7026d47b9 100644 --- a/clangd/Protocol.h +++ b/clangd/Protocol.h @@ -48,23 +48,6 @@ enum class ErrorCode { // Defined by the protocol. RequestCancelled = -32800, }; -// Models an LSP error as an llvm::Error. -class LSPError : public llvm::ErrorInfo { -public: - std::string Message; - ErrorCode Code; - static char ID; - - LSPError(std::string Message, ErrorCode Code) - : Message(std::move(Message)), Code(Code) {} - - void log(llvm::raw_ostream &OS) const override { - OS << int(Code) << ": " << Message; - } - std::error_code convertToErrorCode() const override { - return llvm::inconvertibleErrorCode(); - } -}; struct URIForFile { URIForFile() = default; diff --git a/clangd/ProtocolHandlers.cpp b/clangd/ProtocolHandlers.cpp index 61efeb73e..73da45eca 100644 --- a/clangd/ProtocolHandlers.cpp +++ b/clangd/ProtocolHandlers.cpp @@ -35,7 +35,6 @@ struct HandlerRegisterer { } else { elog("Failed to decode {0} request.", Method); } - return Method != "exit"; // Shut down after exit notification. }); } diff --git a/clangd/Transport.h b/clangd/Transport.h deleted file mode 100644 index 2de95657a..000000000 --- a/clangd/Transport.h +++ /dev/null @@ -1,92 +0,0 @@ -//===--- Transport.h - sending and receiving LSP messages -------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// The language server protocol is usually implemented by writing messages as -// JSON-RPC over the stdin/stdout of a subprocess. However other communications -// mechanisms are possible, such as XPC on mac. -// -// The Transport interface allows the mechanism to be replaced, and the JSONRPC -// Transport is the standard implementation. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_TRANSPORT_H_ -#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_TRANSPORT_H_ - -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/JSON.h" -#include "llvm/Support/raw_ostream.h" - -namespace clang { -namespace clangd { - -// A transport is responsible for maintaining the connection to a client -// application, and reading/writing structured messages to it. -// -// Transports have limited thread safety requirements: -// - messages will not be sent concurrently -// - messages MAY be sent while loop() is reading, or its callback is active -class Transport { -public: - virtual ~Transport() = default; - - // Called by Clangd to send messages to the client. - virtual void notify(llvm::StringRef Method, llvm::json::Value Params) = 0; - virtual void call(llvm::StringRef Method, llvm::json::Value Params, - llvm::json::Value ID) = 0; - virtual void reply(llvm::json::Value ID, - llvm::Expected Result) = 0; - - // Implemented by Clangd to handle incoming messages. (See loop() below). - class MessageHandler { - public: - virtual ~MessageHandler() = default; - // Handler returns true to keep processing messages, or false to shut down. - virtual bool onNotify(llvm::StringRef Method, llvm::json::Value) = 0; - virtual bool onCall(llvm::StringRef Method, llvm::json::Value Params, - llvm::json::Value ID) = 0; - virtual bool onReply(llvm::json::Value ID, - llvm::Expected Result) = 0; - }; - // Called by Clangd to receive messages from the client. - // The transport should in turn invoke the handler to process messages. - // If handler returns false, the transport should immedately exit the loop. - // (This is used to implement the `exit` notification). - // Otherwise, it returns an error when the transport becomes unusable. - virtual llvm::Error loop(MessageHandler &) = 0; -}; - -// Controls the way JSON-RPC messages are encoded (both input and output). -enum JSONStreamStyle { - // Encoding per the LSP specification, with mandatory Content-Length header. - Standard, - // Messages are delimited by a '---' line. Comment lines start with #. - Delimited -}; - -// Returns a Transport that speaks JSON-RPC over a pair of streams. -// The input stream must be opened in binary mode. -// If InMirror is set, data read will be echoed to it. -// -// The use of C-style std::FILE* input deserves some explanation. -// Previously, std::istream was used. When a debugger attached on MacOS, the -// process received EINTR, the stream went bad, and clangd exited. -// A retry-on-EINTR loop around reads solved this problem, but caused clangd to -// sometimes hang rather than exit on other OSes. The interaction between -// istreams and signals isn't well-specified, so it's hard to get this right. -// The C APIs seem to be clearer in this respect. -std::unique_ptr -newJSONTransport(std::FILE *In, llvm::raw_ostream &Out, - llvm::raw_ostream *InMirror, bool Pretty, - JSONStreamStyle = JSONStreamStyle::Standard); - -} // namespace clangd -} // namespace clang - -#endif diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 8ec6716ca..dcfc6f70f 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -253,8 +253,11 @@ int main(int argc, char *argv[]) { // Use buffered stream to stderr (we still flush each log message). Unbuffered // stream can cause significant (non-deterministic) latency for the logger. llvm::errs().SetBuffered(); - JSONOutput Logger(llvm::errs(), LogLevel); - clangd::LoggingSession LoggingSession(Logger); + JSONOutput Out(llvm::outs(), llvm::errs(), LogLevel, + InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, + PrettyPrint); + + clangd::LoggingSession LoggingSession(Out); // If --compile-commands-dir arg was invoked, check value and override default // path. @@ -314,16 +317,12 @@ int main(int argc, char *argv[]) { CCOpts.AllScopes = AllScopesCompletion; // Initialize and run ClangdLSPServer. - // Change stdin to binary to not lose \r\n on windows. - llvm::sys::ChangeStdinToBinary(); - auto Transport = newJSONTransport( - stdin, llvm::outs(), - InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, PrettyPrint, - InputStyle); ClangdLSPServer LSPServer( - *Transport, CCOpts, CompileCommandsDirPath, + Out, CCOpts, CompileCommandsDirPath, /*ShouldUseInMemoryCDB=*/CompileArgsFrom == LSPCompileArgs, Opts); constexpr int NoShutdownRequestErrorCode = 1; llvm::set_thread_name("clangd.main"); - return LSPServer.run() ? 0 : NoShutdownRequestErrorCode; + // Change stdin to binary to not lose \r\n on windows. + llvm::sys::ChangeStdinToBinary(); + return LSPServer.run(stdin, InputStyle) ? 0 : NoShutdownRequestErrorCode; } diff --git a/test/clangd/compile-commands-path-in-initialize.test b/test/clangd/compile-commands-path-in-initialize.test index 87086e3c3..ef7cd4f48 100644 --- a/test/clangd/compile-commands-path-in-initialize.test +++ b/test/clangd/compile-commands-path-in-initialize.test @@ -24,5 +24,3 @@ # CHECK-NEXT: "message": "MACRO is one", --- {"jsonrpc":"2.0","id":10000,"method":"shutdown"} ---- -{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/completion-snippets.test b/test/clangd/completion-snippets.test index 22cd0821b..965ae587f 100644 --- a/test/clangd/completion-snippets.test +++ b/test/clangd/completion-snippets.test @@ -52,5 +52,3 @@ # CHECK-NEXT: } --- {"jsonrpc":"2.0","id":4,"method":"shutdown"} ---- -{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/completion.test b/test/clangd/completion.test index 0094d4740..2283059e4 100644 --- a/test/clangd/completion.test +++ b/test/clangd/completion.test @@ -68,5 +68,3 @@ # CHECK-NEXT: ] --- {"jsonrpc":"2.0","id":4,"method":"shutdown"} ---- -{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/crash-non-added-files.test b/test/clangd/crash-non-added-files.test index 023c4d68e..d86f7d26d 100644 --- a/test/clangd/crash-non-added-files.test +++ b/test/clangd/crash-non-added-files.test @@ -32,5 +32,3 @@ {"jsonrpc":"2.0","id":6,"method":"shutdown"} --- {"jsonrpc":"2.0","method":"exit"} ---- -{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/execute-command.test b/test/clangd/execute-command.test index 85d4b9b88..492006fdf 100644 --- a/test/clangd/execute-command.test +++ b/test/clangd/execute-command.test @@ -62,5 +62,3 @@ {"jsonrpc":"2.0","id":9,"method":"workspace/executeCommand","params":{"arguments":[{"custom":"foo", "changes":{"test:///foo.c":[{"range":{"start":{"line":0,"character":32},"end":{"line":0,"character":32}},"newText":"("},{"range":{"start":{"line":0,"character":37},"end":{"line":0,"character":37}},"newText":")"}]}}],"command":"clangd.applyFix"}} --- {"jsonrpc":"2.0","id":3,"method":"shutdown"} ---- -{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/input-mirror.test b/test/clangd/input-mirror.test index 52845621e..88409c4d7 100644 --- a/test/clangd/input-mirror.test +++ b/test/clangd/input-mirror.test @@ -12,6 +12,3 @@ Content-Length: 172 Content-Length: 44 {"jsonrpc":"2.0","id":3,"method":"shutdown"} -Content-Length: 33 - -{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/signature-help.test b/test/clangd/signature-help.test index 37b8b500b..f13f49c2a 100644 --- a/test/clangd/signature-help.test +++ b/test/clangd/signature-help.test @@ -23,5 +23,3 @@ # CHECK-NEXT: } --- {"jsonrpc":"2.0","id":100000,"method":"shutdown"} ---- -{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/textdocument-didchange-fail.test b/test/clangd/textdocument-didchange-fail.test index 33350c4a3..3bd01e96b 100644 --- a/test/clangd/textdocument-didchange-fail.test +++ b/test/clangd/textdocument-didchange-fail.test @@ -35,5 +35,3 @@ # CHECK-NEXT:} --- {"jsonrpc":"2.0","id":4,"method":"shutdown"} ---- -{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/trace.test b/test/clangd/trace.test index 83b040a51..9036128d4 100644 --- a/test/clangd/trace.test +++ b/test/clangd/trace.test @@ -21,5 +21,3 @@ # CHECK: }, --- {"jsonrpc":"2.0","id":5,"method":"shutdown"} ---- -{"jsonrpc":"2.0","method":"exit"} diff --git a/test/clangd/xrefs.test b/test/clangd/xrefs.test index 58ca44cb0..f2e17c4b8 100644 --- a/test/clangd/xrefs.test +++ b/test/clangd/xrefs.test @@ -55,5 +55,3 @@ # CHECK-NEXT: ] --- {"jsonrpc":"2.0","id":10000,"method":"shutdown"} ---- -{"jsonrpc":"2.0","method":"exit"} diff --git a/unittests/clangd/CMakeLists.txt b/unittests/clangd/CMakeLists.txt index 07341c80a..85eb8b290 100644 --- a/unittests/clangd/CMakeLists.txt +++ b/unittests/clangd/CMakeLists.txt @@ -27,7 +27,6 @@ add_extra_unittest(ClangdTests GlobalCompilationDatabaseTests.cpp HeadersTests.cpp IndexTests.cpp - JSONTransportTests.cpp QualityTests.cpp RIFFTests.cpp SerializationTests.cpp diff --git a/unittests/clangd/JSONTransportTests.cpp b/unittests/clangd/JSONTransportTests.cpp deleted file mode 100644 index c00dcce81..000000000 --- a/unittests/clangd/JSONTransportTests.cpp +++ /dev/null @@ -1,199 +0,0 @@ -//===-- JSONTransportTests.cpp -------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#include "Protocol.h" -#include "Transport.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include - -using namespace llvm; -namespace clang { -namespace clangd { -namespace { - -// No fmemopen on windows, so we can't easily run this test. -#ifndef WIN32 - -// Fixture takes care of managing the input/output buffers for the transport. -class JSONTransportTest : public ::testing::Test { - std::string InBuf, OutBuf, MirrorBuf; - llvm::raw_string_ostream Out, Mirror; - std::unique_ptr In; - -protected: - JSONTransportTest() : Out(OutBuf), Mirror(MirrorBuf), In(nullptr, nullptr) {} - - template - std::unique_ptr transport(std::string InData, bool Pretty, - JSONStreamStyle Style) { - InBuf = std::move(InData); - In = {fmemopen(&InBuf[0], InBuf.size(), "r"), &fclose}; - return newJSONTransport(In.get(), Out, &Mirror, Pretty, Style); - } - - std::string input() const { return InBuf; } - std::string output() { return Out.str(); } - std::string input_mirror() { return Mirror.str(); } -}; - -// Echo is a simple server running on a transport: -// - logs each message it gets. -// - when it gets a call, replies to it -// - when it gets a notification for method "call", makes a call on Target -// Hangs up when it gets an exit notification. -class Echo : public Transport::MessageHandler { - Transport &Target; - std::string LogBuf; - raw_string_ostream Log; - -public: - Echo(Transport &Target) : Target(Target), Log(LogBuf) {} - - std::string log() { return Log.str(); } - - bool onNotify(StringRef Method, json::Value Params) override { - Log << "Notification " << Method << ": " << Params << "\n"; - if (Method == "call") - Target.call("echo call", std::move(Params), 42); - return Method != "exit"; - } - - bool onCall(StringRef Method, json::Value Params, json::Value ID) override { - Log << "Call " << Method << "(" << ID << "): " << Params << "\n"; - if (Method == "err") - Target.reply(ID, make_error("trouble at mill", ErrorCode(88))); - else - Target.reply(ID, std::move(Params)); - return true; - } - - bool onReply(json::Value ID, Expected Params) override { - if (Params) - Log << "Reply(" << ID << "): " << *Params << "\n"; - else - Log << "Reply(" << ID - << "): error = " << llvm::toString(Params.takeError()) << "\n"; - return true; - } -}; - -std::string trim(StringRef S) { return S.trim().str(); } - -// Runs an Echo session using the standard JSON-RPC format we use in production. -TEST_F(JSONTransportTest, StandardDense) { - auto T = transport( - "Content-Length: 52\r\n\r\n" - R"({"jsonrpc": "2.0", "method": "call", "params": 1234})" - "Content-Length: 46\r\n\r\n" - R"({"jsonrpc": "2.0", "id": 1234, "result": 5678})" - "Content-Length: 67\r\n\r\n" - R"({"jsonrpc": "2.0", "method": "foo", "id": "abcd", "params": "efgh"})" - "Content-Length: 73\r\n\r\n" - R"({"jsonrpc": "2.0", "id": "xyz", "error": {"code": 99, "message": "bad!"}})" - "Content-Length: 68\r\n\r\n" - R"({"jsonrpc": "2.0", "method": "err", "id": "wxyz", "params": "boom!"})" - "Content-Length: 36\r\n\r\n" - R"({"jsonrpc": "2.0", "method": "exit"})", - /*Pretty=*/false, JSONStreamStyle::Standard); - Echo E(*T); - auto Err = T->loop(E); - EXPECT_FALSE(bool(Err)) << llvm::toString(std::move(Err)); - - EXPECT_EQ(trim(E.log()), trim(R"( -Notification call: 1234 -Reply(1234): 5678 -Call foo("abcd"): "efgh" -Reply("xyz"): error = 99: bad! -Call err("wxyz"): "boom!" -Notification exit: null - )")); - EXPECT_EQ( - trim(output()), - "Content-Length: 60\r\n\r\n" - R"({"id":42,"jsonrpc":"2.0","method":"echo call","params":1234})" - "Content-Length: 45\r\n\r\n" - R"({"id":"abcd","jsonrpc":"2.0","result":"efgh"})" - "Content-Length: 77\r\n\r\n" - R"({"error":{"code":88,"message":"trouble at mill"},"id":"wxyz","jsonrpc":"2.0"})"); - EXPECT_EQ(trim(input_mirror()), trim(input())); -} - -// Runs an Echo session using the "delimited" input and pretty-printed output -// that we use in lit tests. -TEST_F(JSONTransportTest, DelimitedPretty) { - auto T = transport(R"jsonrpc( -{"jsonrpc": "2.0", "method": "call", "params": 1234} ---- -{"jsonrpc": "2.0", "id": 1234, "result": 5678} ---- -{"jsonrpc": "2.0", "method": "foo", "id": "abcd", "params": "efgh"} ---- -{"jsonrpc": "2.0", "id": "xyz", "error": {"code": 99, "message": "bad!"}} ---- -{"jsonrpc": "2.0", "method": "err", "id": "wxyz", "params": "boom!"} ---- -{"jsonrpc": "2.0", "method": "exit"} - )jsonrpc", - /*Pretty=*/true, JSONStreamStyle::Delimited); - Echo E(*T); - auto Err = T->loop(E); - EXPECT_FALSE(bool(Err)) << llvm::toString(std::move(Err)); - - EXPECT_EQ(trim(E.log()), trim(R"( -Notification call: 1234 -Reply(1234): 5678 -Call foo("abcd"): "efgh" -Reply("xyz"): error = 99: bad! -Call err("wxyz"): "boom!" -Notification exit: null - )")); - EXPECT_EQ(trim(output()), "Content-Length: 77\r\n\r\n" - R"({ - "id": 42, - "jsonrpc": "2.0", - "method": "echo call", - "params": 1234 -})" - "Content-Length: 58\r\n\r\n" - R"({ - "id": "abcd", - "jsonrpc": "2.0", - "result": "efgh" -})" - "Content-Length: 105\r\n\r\n" - R"({ - "error": { - "code": 88, - "message": "trouble at mill" - }, - "id": "wxyz", - "jsonrpc": "2.0" -})"); - EXPECT_EQ(trim(input_mirror()), trim(input())); -} - -// IO errors such as EOF ane reported. -// The only successful return from loop() is if a handler returned false. -TEST_F(JSONTransportTest, EndOfFile) { - auto T = transport("Content-Length: 52\r\n\r\n" - R"({"jsonrpc": "2.0", "method": "call", "params": 1234})", - /*Pretty=*/false, JSONStreamStyle::Standard); - Echo E(*T); - auto Err = T->loop(E); - EXPECT_EQ(trim(E.log()), "Notification call: 1234"); - EXPECT_TRUE(bool(Err)); // Ran into EOF with no handler signalling done. - consumeError(std::move(Err)); - EXPECT_EQ(trim(input_mirror()), trim(input())); -} - -#endif - -} // namespace -} // namespace clangd -} // namespace clang From 30f224875dc67fd753245cfa6c8e900a8ac6c50b Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Tue, 16 Oct 2018 23:06:42 +0000 Subject: [PATCH 378/686] [clang-doc] Add unit tests for serialization Adds unit tests for the Serialize library. This is part of a move to convert clang-doc's tests to a more maintainable unit test framework, with a smaller number of integration tests to maintain and more granular failure feedback. Differential Revision: https://reviews.llvm.org/D53081 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344650 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/CMakeLists.txt | 1 + unittests/clang-doc/CMakeLists.txt | 29 +++ unittests/clang-doc/ClangDocTest.cpp | 182 ++++++++++++++ unittests/clang-doc/ClangDocTest.h | 51 ++++ unittests/clang-doc/SerializeTest.cpp | 346 ++++++++++++++++++++++++++ 5 files changed, 609 insertions(+) create mode 100644 unittests/clang-doc/CMakeLists.txt create mode 100644 unittests/clang-doc/ClangDocTest.cpp create mode 100644 unittests/clang-doc/ClangDocTest.h create mode 100644 unittests/clang-doc/SerializeTest.cpp diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index d123700ba..9e01473dd 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -16,6 +16,7 @@ endif() add_subdirectory(change-namespace) add_subdirectory(clang-apply-replacements) +add_subdirectory(clang-doc) add_subdirectory(clang-move) add_subdirectory(clang-query) add_subdirectory(clang-tidy) diff --git a/unittests/clang-doc/CMakeLists.txt b/unittests/clang-doc/CMakeLists.txt new file mode 100644 index 000000000..da2dcb7ef --- /dev/null +++ b/unittests/clang-doc/CMakeLists.txt @@ -0,0 +1,29 @@ +set(LLVM_LINK_COMPONENTS + support + BitReader + BitWriter + ) + +get_filename_component(CLANG_DOC_SOURCE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-doc REALPATH) +include_directories( + ${CLANG_DOC_SOURCE_DIR} + ) + +add_extra_unittest(ClangDocTests + ClangDocTest.cpp + SerializeTest.cpp + ) + +target_link_libraries(ClangDocTests + PRIVATE + clangAST + clangASTMatchers + clangBasic + clangDoc + clangFormat + clangFrontend + clangRewrite + clangTooling + clangToolingCore + ) diff --git a/unittests/clang-doc/ClangDocTest.cpp b/unittests/clang-doc/ClangDocTest.cpp new file mode 100644 index 000000000..e763d3547 --- /dev/null +++ b/unittests/clang-doc/ClangDocTest.cpp @@ -0,0 +1,182 @@ +//===-- clang-doc/ClangDocTest.cpp ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Representation.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "gtest/gtest.h" + +namespace clang { +namespace doc { + +NamespaceInfo *InfoAsNamespace(Info *I) { + assert(I->IT == InfoType::IT_namespace); + return static_cast(I); +} + +RecordInfo *InfoAsRecord(Info *I) { + assert(I->IT == InfoType::IT_record); + return static_cast(I); +} + +FunctionInfo *InfoAsFunction(Info *I) { + assert(I->IT == InfoType::IT_function); + return static_cast(I); +} + +EnumInfo *InfoAsEnum(Info *I) { + assert(I->IT == InfoType::IT_enum); + return static_cast(I); +} + +void CheckCommentInfo(CommentInfo &Expected, CommentInfo &Actual) { + EXPECT_EQ(Expected.Kind, Actual.Kind); + EXPECT_EQ(Expected.Text, Actual.Text); + EXPECT_EQ(Expected.Name, Actual.Name); + EXPECT_EQ(Expected.Direction, Actual.Direction); + EXPECT_EQ(Expected.ParamName, Actual.ParamName); + EXPECT_EQ(Expected.CloseName, Actual.CloseName); + EXPECT_EQ(Expected.SelfClosing, Actual.SelfClosing); + EXPECT_EQ(Expected.Explicit, Actual.Explicit); + + ASSERT_EQ(Expected.AttrKeys.size(), Actual.AttrKeys.size()); + for (size_t Idx = 0; Idx < Actual.AttrKeys.size(); ++Idx) + EXPECT_EQ(Expected.AttrKeys[Idx], Actual.AttrKeys[Idx]); + + ASSERT_EQ(Expected.AttrValues.size(), Actual.AttrValues.size()); + for (size_t Idx = 0; Idx < Actual.AttrValues.size(); ++Idx) + EXPECT_EQ(Expected.AttrValues[Idx], Actual.AttrValues[Idx]); + + ASSERT_EQ(Expected.Args.size(), Actual.Args.size()); + for (size_t Idx = 0; Idx < Actual.Args.size(); ++Idx) + EXPECT_EQ(Expected.Args[Idx], Actual.Args[Idx]); + + ASSERT_EQ(Expected.Children.size(), Actual.Children.size()); + for (size_t Idx = 0; Idx < Actual.Children.size(); ++Idx) + CheckCommentInfo(*Expected.Children[Idx], *Actual.Children[Idx]); +} + +void CheckReference(Reference &Expected, Reference &Actual) { + EXPECT_EQ(Expected.Name, Actual.Name); + EXPECT_EQ(Expected.RefType, Actual.RefType); +} + +void CheckTypeInfo(TypeInfo *Expected, TypeInfo *Actual) { + CheckReference(Expected->Type, Actual->Type); +} + +void CheckFieldTypeInfo(FieldTypeInfo *Expected, FieldTypeInfo *Actual) { + CheckTypeInfo(Expected, Actual); + EXPECT_EQ(Expected->Name, Actual->Name); +} + +void CheckMemberTypeInfo(MemberTypeInfo *Expected, MemberTypeInfo *Actual) { + CheckFieldTypeInfo(Expected, Actual); + EXPECT_EQ(Expected->Access, Actual->Access); +} + +void CheckBaseInfo(Info *Expected, Info *Actual) { + EXPECT_EQ(size_t(20), Actual->USR.size()); + EXPECT_EQ(Expected->Name, Actual->Name); + ASSERT_EQ(Expected->Namespace.size(), Actual->Namespace.size()); + for (size_t Idx = 0; Idx < Actual->Namespace.size(); ++Idx) + CheckReference(Expected->Namespace[Idx], Actual->Namespace[Idx]); + ASSERT_EQ(Expected->Description.size(), Actual->Description.size()); + for (size_t Idx = 0; Idx < Actual->Description.size(); ++Idx) + CheckCommentInfo(Expected->Description[Idx], Actual->Description[Idx]); +} + +void CheckSymbolInfo(SymbolInfo *Expected, SymbolInfo *Actual) { + CheckBaseInfo(Expected, Actual); + EXPECT_EQ(Expected->DefLoc.hasValue(), Actual->DefLoc.hasValue()); + if (Expected->DefLoc.hasValue() && Actual->DefLoc.hasValue()) { + EXPECT_EQ(Expected->DefLoc->LineNumber, Actual->DefLoc->LineNumber); + EXPECT_EQ(Expected->DefLoc->Filename, Actual->DefLoc->Filename); + } + ASSERT_EQ(Expected->Loc.size(), Actual->Loc.size()); + for (size_t Idx = 0; Idx < Actual->Loc.size(); ++Idx) + EXPECT_EQ(Expected->Loc[Idx], Actual->Loc[Idx]); +} + +void CheckFunctionInfo(FunctionInfo *Expected, FunctionInfo *Actual) { + CheckSymbolInfo(Expected, Actual); + + EXPECT_EQ(Expected->IsMethod, Actual->IsMethod); + CheckReference(Expected->Parent, Actual->Parent); + CheckTypeInfo(&Expected->ReturnType, &Actual->ReturnType); + + ASSERT_EQ(Expected->Params.size(), Actual->Params.size()); + for (size_t Idx = 0; Idx < Actual->Params.size(); ++Idx) + EXPECT_EQ(Expected->Params[Idx], Actual->Params[Idx]); + + EXPECT_EQ(Expected->Access, Actual->Access); +} + +void CheckEnumInfo(EnumInfo *Expected, EnumInfo *Actual) { + CheckSymbolInfo(Expected, Actual); + + EXPECT_EQ(Expected->Scoped, Actual->Scoped); + ASSERT_EQ(Expected->Members.size(), Actual->Members.size()); + for (size_t Idx = 0; Idx < Actual->Members.size(); ++Idx) + EXPECT_EQ(Expected->Members[Idx], Actual->Members[Idx]); +} + +void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *Actual) { + CheckBaseInfo(Expected, Actual); + + ASSERT_EQ(Expected->ChildNamespaces.size(), Actual->ChildNamespaces.size()); + for (size_t Idx = 0; Idx < Actual->ChildNamespaces.size(); ++Idx) + EXPECT_EQ(Expected->ChildNamespaces[Idx], Actual->ChildNamespaces[Idx]); + + ASSERT_EQ(Expected->ChildRecords.size(), Actual->ChildRecords.size()); + for (size_t Idx = 0; Idx < Actual->ChildRecords.size(); ++Idx) + EXPECT_EQ(Expected->ChildRecords[Idx], Actual->ChildRecords[Idx]); + + ASSERT_EQ(Expected->ChildFunctions.size(), Actual->ChildFunctions.size()); + for (size_t Idx = 0; Idx < Actual->ChildFunctions.size(); ++Idx) + CheckFunctionInfo(&Expected->ChildFunctions[Idx], + &Actual->ChildFunctions[Idx]); + + ASSERT_EQ(Expected->ChildEnums.size(), Actual->ChildEnums.size()); + for (size_t Idx = 0; Idx < Actual->ChildEnums.size(); ++Idx) + CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]); +} + +void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual) { + CheckSymbolInfo(Expected, Actual); + + EXPECT_EQ(Expected->TagType, Actual->TagType); + + ASSERT_EQ(Expected->Members.size(), Actual->Members.size()); + for (size_t Idx = 0; Idx < Actual->Members.size(); ++Idx) + EXPECT_EQ(Expected->Members[Idx], Actual->Members[Idx]); + + ASSERT_EQ(Expected->Parents.size(), Actual->Parents.size()); + for (size_t Idx = 0; Idx < Actual->Parents.size(); ++Idx) + CheckReference(Expected->Parents[Idx], Actual->Parents[Idx]); + + ASSERT_EQ(Expected->VirtualParents.size(), Actual->VirtualParents.size()); + for (size_t Idx = 0; Idx < Actual->VirtualParents.size(); ++Idx) + CheckReference(Expected->VirtualParents[Idx], Actual->VirtualParents[Idx]); + + ASSERT_EQ(Expected->ChildRecords.size(), Actual->ChildRecords.size()); + for (size_t Idx = 0; Idx < Actual->ChildRecords.size(); ++Idx) + EXPECT_EQ(Expected->ChildRecords[Idx], Actual->ChildRecords[Idx]); + + ASSERT_EQ(Expected->ChildFunctions.size(), Actual->ChildFunctions.size()); + for (size_t Idx = 0; Idx < Actual->ChildFunctions.size(); ++Idx) + CheckFunctionInfo(&Expected->ChildFunctions[Idx], + &Actual->ChildFunctions[Idx]); + + ASSERT_EQ(Expected->ChildEnums.size(), Actual->ChildEnums.size()); + for (size_t Idx = 0; Idx < Actual->ChildEnums.size(); ++Idx) + CheckEnumInfo(&Expected->ChildEnums[Idx], &Actual->ChildEnums[Idx]); +} + +} // namespace doc +} // namespace clang diff --git a/unittests/clang-doc/ClangDocTest.h b/unittests/clang-doc/ClangDocTest.h new file mode 100644 index 000000000..26c0b72ff --- /dev/null +++ b/unittests/clang-doc/ClangDocTest.h @@ -0,0 +1,51 @@ +//===-- clang-doc/ClangDocTest.h ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_DOC_CLANGDOCTEST_H +#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_DOC_CLANGDOCTEST_H + +#include "ClangDocTest.h" +#include "Representation.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "gtest/gtest.h" + +namespace clang { +namespace doc { + +using EmittedInfoList = std::vector>; + +static const SymbolID EmptySID = SymbolID(); +static const SymbolID NonEmptySID = + SymbolID{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + +NamespaceInfo *InfoAsNamespace(Info *I); +RecordInfo *InfoAsRecord(Info *I); +FunctionInfo *InfoAsFunction(Info *I); +EnumInfo *InfoAsEnum(Info *I); + +// Unlike the operator==, these functions explicitly does not check USRs, as +// that may change and it would be better to not rely on its implementation. +void CheckReference(Reference &Expected, Reference &Actual); +void CheckTypeInfo(TypeInfo *Expected, TypeInfo *Actual); +void CheckFieldTypeInfo(FieldTypeInfo *Expected, FieldTypeInfo *Actual); +void CheckMemberTypeInfo(MemberTypeInfo *Expected, MemberTypeInfo *Actual); + +// This function explicitly does not check USRs, as that may change and it would +// be better to not rely on its implementation. +void CheckBaseInfo(Info *Expected, Info *Actual); +void CheckSymbolInfo(SymbolInfo *Expected, SymbolInfo *Actual); +void CheckFunctionInfo(FunctionInfo *Expected, FunctionInfo *Actual); +void CheckEnumInfo(EnumInfo *Expected, EnumInfo *Actual); +void CheckNamespaceInfo(NamespaceInfo *Expected, NamespaceInfo *Actual); +void CheckRecordInfo(RecordInfo *Expected, RecordInfo *Actual); + +} // namespace doc +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_DOC_CLANGDOCTEST_H diff --git a/unittests/clang-doc/SerializeTest.cpp b/unittests/clang-doc/SerializeTest.cpp new file mode 100644 index 000000000..d5bf8f11f --- /dev/null +++ b/unittests/clang-doc/SerializeTest.cpp @@ -0,0 +1,346 @@ +//===-- clang-doc/SerializeTest.cpp ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Serialize.h" +#include "ClangDocTest.h" +#include "Representation.h" +#include "clang/AST/Comment.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "gtest/gtest.h" + +namespace clang { +namespace doc { + +class ClangDocSerializeTestVisitor + : public RecursiveASTVisitor { + + EmittedInfoList &EmittedInfos; + bool Public; + + comments::FullComment *getComment(const NamedDecl *D) const { + if (RawComment *Comment = + D->getASTContext().getRawCommentForDeclNoCache(D)) { + Comment->setAttached(); + return Comment->parse(D->getASTContext(), nullptr, D); + } + return nullptr; + } + +public: + ClangDocSerializeTestVisitor(EmittedInfoList &EmittedInfos, bool Public) + : EmittedInfos(EmittedInfos), Public(Public) {} + + bool VisitNamespaceDecl(const NamespaceDecl *D) { + auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, + /*File=*/"test.cpp", Public); + if (I) + EmittedInfos.emplace_back(std::move(I)); + return true; + } + + bool VisitFunctionDecl(const FunctionDecl *D) { + // Don't visit CXXMethodDecls twice + if (dyn_cast(D)) + return true; + auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, + /*File=*/"test.cpp", Public); + if (I) + EmittedInfos.emplace_back(std::move(I)); + return true; + } + + bool VisitCXXMethodDecl(const CXXMethodDecl *D) { + auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, + /*File=*/"test.cpp", Public); + if (I) + EmittedInfos.emplace_back(std::move(I)); + return true; + } + + bool VisitRecordDecl(const RecordDecl *D) { + auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, + /*File=*/"test.cpp", Public); + if (I) + EmittedInfos.emplace_back(std::move(I)); + return true; + } + + bool VisitEnumDecl(const EnumDecl *D) { + auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0, + /*File=*/"test.cpp", Public); + if (I) + EmittedInfos.emplace_back(std::move(I)); + return true; + } +}; + +void ExtractInfosFromCode(StringRef Code, size_t NumExpectedInfos, bool Public, + EmittedInfoList &EmittedInfos) { + auto ASTUnit = clang::tooling::buildASTFromCode(Code); + auto TU = ASTUnit->getASTContext().getTranslationUnitDecl(); + ClangDocSerializeTestVisitor Visitor(EmittedInfos, Public); + Visitor.TraverseTranslationUnitDecl(TU); + ASSERT_EQ(NumExpectedInfos, EmittedInfos.size()); +} + +void ExtractInfosFromCodeWithArgs(StringRef Code, size_t NumExpectedInfos, + bool Public, EmittedInfoList &EmittedInfos, + std::vector &Args) { + auto ASTUnit = clang::tooling::buildASTFromCodeWithArgs(Code, Args); + auto TU = ASTUnit->getASTContext().getTranslationUnitDecl(); + ClangDocSerializeTestVisitor Visitor(EmittedInfos, Public); + Visitor.TraverseTranslationUnitDecl(TU); + ASSERT_EQ(NumExpectedInfos, EmittedInfos.size()); +} + +// Test serialization of namespace declarations. +TEST(SerializeTest, emitNamespaceInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("namespace A { namespace B { void f() {} } }", 3, + /*Public=*/false, Infos); + + NamespaceInfo *A = InfoAsNamespace(Infos[0].get()); + NamespaceInfo ExpectedA(EmptySID, "A"); + CheckNamespaceInfo(&ExpectedA, A); + + NamespaceInfo *B = InfoAsNamespace(Infos[1].get()); + NamespaceInfo ExpectedB(EmptySID, "B"); + ExpectedB.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); + CheckNamespaceInfo(&ExpectedB, B); + + NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[2].get()); + NamespaceInfo ExpectedBWithFunction(EmptySID); + FunctionInfo F; + F.Name = "f"; + F.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); + F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + F.Namespace.emplace_back(EmptySID, "B", InfoType::IT_namespace); + F.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); + ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F)); + CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction); +} + +TEST(SerializeTest, emitAnonymousNamespaceInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("namespace { }", 1, /*Public=*/false, Infos); + + NamespaceInfo *A = InfoAsNamespace(Infos[0].get()); + NamespaceInfo ExpectedA(EmptySID); + CheckNamespaceInfo(&ExpectedA, A); +} + +// Test serialization of record declarations. +TEST(SerializeTest, emitRecordInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode(R"raw(class E { +public: + E() {} +protected: + void ProtectedMethod(); +};)raw", 3, /*Public=*/false, Infos); + + RecordInfo *E = InfoAsRecord(Infos[0].get()); + RecordInfo ExpectedE(EmptySID, "E"); + ExpectedE.TagType = TagTypeKind::TTK_Class; + ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + CheckRecordInfo(&ExpectedE, E); + + RecordInfo *RecordWithEConstructor = InfoAsRecord(Infos[1].get()); + RecordInfo ExpectedRecordWithEConstructor(EmptySID); + FunctionInfo EConstructor; + EConstructor.Name = "E"; + EConstructor.Parent = Reference(EmptySID, "E", InfoType::IT_record); + EConstructor.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); + EConstructor.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + EConstructor.Namespace.emplace_back(EmptySID, "E", InfoType::IT_record); + EConstructor.Access = AccessSpecifier::AS_public; + EConstructor.IsMethod = true; + ExpectedRecordWithEConstructor.ChildFunctions.emplace_back( + std::move(EConstructor)); + CheckRecordInfo(&ExpectedRecordWithEConstructor, RecordWithEConstructor); + + RecordInfo *RecordWithMethod = InfoAsRecord(Infos[2].get()); + RecordInfo ExpectedRecordWithMethod(EmptySID); + FunctionInfo Method; + Method.Name = "ProtectedMethod"; + Method.Parent = Reference(EmptySID, "E", InfoType::IT_record); + Method.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); + Method.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"}); + Method.Namespace.emplace_back(EmptySID, "E", InfoType::IT_record); + Method.Access = AccessSpecifier::AS_protected; + Method.IsMethod = true; + ExpectedRecordWithMethod.ChildFunctions.emplace_back(std::move(Method)); + CheckRecordInfo(&ExpectedRecordWithMethod, RecordWithMethod); +} + +// Test serialization of enum declarations. +TEST(SerializeTest, emitEnumInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("enum E { X, Y }; enum class G { A, B };", 2, + /*Public=*/false, Infos); + + NamespaceInfo *NamespaceWithEnum = InfoAsNamespace(Infos[0].get()); + NamespaceInfo ExpectedNamespaceWithEnum(EmptySID); + EnumInfo E; + E.Name = "E"; + E.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + E.Members.emplace_back("X"); + E.Members.emplace_back("Y"); + ExpectedNamespaceWithEnum.ChildEnums.emplace_back(std::move(E)); + CheckNamespaceInfo(&ExpectedNamespaceWithEnum, NamespaceWithEnum); + + NamespaceInfo *NamespaceWithScopedEnum = InfoAsNamespace(Infos[1].get()); + NamespaceInfo ExpectedNamespaceWithScopedEnum(EmptySID); + EnumInfo G; + G.Name = "G"; + G.Scoped = true; + G.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + G.Members.emplace_back("A"); + G.Members.emplace_back("B"); + ExpectedNamespaceWithScopedEnum.ChildEnums.emplace_back(std::move(G)); + CheckNamespaceInfo(&ExpectedNamespaceWithScopedEnum, NamespaceWithScopedEnum); +} + +TEST(SerializeTest, emitUndefinedRecordInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("class E;", 1, /*Public=*/false, Infos); + + RecordInfo *E = InfoAsRecord(Infos[0].get()); + RecordInfo ExpectedE(EmptySID, "E"); + ExpectedE.TagType = TagTypeKind::TTK_Class; + ExpectedE.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"}); + CheckRecordInfo(&ExpectedE, E); +} + +TEST(SerializeTest, emitRecordMemberInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("struct E { int I; };", 1, /*Public=*/false, Infos); + + RecordInfo *E = InfoAsRecord(Infos[0].get()); + RecordInfo ExpectedE(EmptySID, "E"); + ExpectedE.TagType = TagTypeKind::TTK_Struct; + ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + ExpectedE.Members.emplace_back("int", "I", AccessSpecifier::AS_public); + CheckRecordInfo(&ExpectedE, E); +} + +TEST(SerializeTest, emitInternalRecordInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("class E { class G {}; };", 2, /*Public=*/false, Infos); + + RecordInfo *E = InfoAsRecord(Infos[0].get()); + RecordInfo ExpectedE(EmptySID, "E"); + ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + ExpectedE.TagType = TagTypeKind::TTK_Class; + CheckRecordInfo(&ExpectedE, E); + + RecordInfo *G = InfoAsRecord(Infos[1].get()); + RecordInfo ExpectedG(EmptySID, "G"); + ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + ExpectedG.TagType = TagTypeKind::TTK_Class; + ExpectedG.Namespace.emplace_back(EmptySID, "E", InfoType::IT_record); + CheckRecordInfo(&ExpectedG, G); +} + +TEST(SerializeTest, emitPublicAnonymousNamespaceInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("namespace { class A; }", 0, /*Public=*/true, Infos); +} + +TEST(SerializeTest, emitPublicFunctionInternalInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("int F() { class G {}; return 0; };", 1, /*Public=*/true, + Infos); + + NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[0].get()); + NamespaceInfo ExpectedBWithFunction(EmptySID); + FunctionInfo F; + F.Name = "F"; + F.ReturnType = TypeInfo(EmptySID, "int", InfoType::IT_default); + F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F)); + CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction); +} + +TEST(SerializeTest, emitInlinedFunctionInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode("inline void F(int I) { };", 1, /*Public=*/true, Infos); + + NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[0].get()); + NamespaceInfo ExpectedBWithFunction(EmptySID); + FunctionInfo F; + F.Name = "F"; + F.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); + F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + F.Params.emplace_back("int", "I"); + ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F)); + CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction); +} + +TEST(SerializeTest, emitInheritedRecordInfo) { + EmittedInfoList Infos; + ExtractInfosFromCode( + "class F {}; class G{} ; class E : public F, virtual private G {};", 3, + /*Public=*/false, Infos); + + RecordInfo *F = InfoAsRecord(Infos[0].get()); + RecordInfo ExpectedF(EmptySID, "F"); + ExpectedF.TagType = TagTypeKind::TTK_Class; + ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + CheckRecordInfo(&ExpectedF, F); + + RecordInfo *G = InfoAsRecord(Infos[1].get()); + RecordInfo ExpectedG(EmptySID, "G"); + ExpectedG.TagType = TagTypeKind::TTK_Class; + ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + CheckRecordInfo(&ExpectedG, G); + + RecordInfo *E = InfoAsRecord(Infos[2].get()); + RecordInfo ExpectedE(EmptySID, "E"); + ExpectedE.Parents.emplace_back(EmptySID, "F", InfoType::IT_record); + ExpectedE.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); + ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"}); + ExpectedE.TagType = TagTypeKind::TTK_Class; + CheckRecordInfo(&ExpectedE, E); +} + +TEST(SerializeTest, emitModulePublicLFunctions) { + EmittedInfoList Infos; + std::vector Args; + Args.push_back("-fmodules-ts"); + ExtractInfosFromCodeWithArgs(R"raw(export module M; +int moduleFunction(int x); +static int staticModuleFunction(int x); +export double exportedModuleFunction(double y);)raw", + 2, /*Public=*/true, Infos, Args); + + NamespaceInfo *BWithFunction = InfoAsNamespace(Infos[0].get()); + NamespaceInfo ExpectedBWithFunction(EmptySID); + FunctionInfo F; + F.Name = "moduleFunction"; + F.ReturnType = TypeInfo(EmptySID, "int", InfoType::IT_default); + F.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"}); + F.Params.emplace_back("int", "x"); + ExpectedBWithFunction.ChildFunctions.emplace_back(std::move(F)); + CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction); + + NamespaceInfo *BWithExportedFunction = InfoAsNamespace(Infos[1].get()); + NamespaceInfo ExpectedBWithExportedFunction(EmptySID); + FunctionInfo ExportedF; + ExportedF.Name = "exportedModuleFunction"; + ExportedF.ReturnType = TypeInfo(EmptySID, "double", InfoType::IT_default); + ExportedF.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"}); + ExportedF.Params.emplace_back("double", "y"); + ExpectedBWithExportedFunction.ChildFunctions.emplace_back( + std::move(ExportedF)); + CheckNamespaceInfo(&ExpectedBWithExportedFunction, BWithExportedFunction); +} + +} // namespace doc +} // end namespace clang From e99be39e361dfbe42ba8864b944d7ee84bf247d6 Mon Sep 17 00:00:00 2001 From: Julie Hockett Date: Tue, 16 Oct 2018 23:06:53 +0000 Subject: [PATCH 379/686] [clang-doc] Add unit tests for bitcode Adds unit tests for the BitcodeWriter and BitcodeReader libraries. This is part of a move to convert clang-doc's tests to a more maintainable unit test framework, with a smaller number of integration tests to maintain and more granular failure feedback. Differential Revision: https://reviews.llvm.org/D53082 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@344651 91177308-0d34-0410-b5e6-96231b3b80d8 --- clang-doc/BitcodeWriter.cpp | 6 +- test/clang-doc/bc-comment.cpp | 100 ++++----- test/clang-doc/bc-linkage.cpp | 322 ++++++++++++++-------------- test/clang-doc/bc-module.cpp | 44 ++-- test/clang-doc/bc-namespace.cpp | 46 ++-- test/clang-doc/bc-record.cpp | 124 +++++------ test/clang-doc/mapper-comment.cpp | 26 +-- test/clang-doc/mapper-linkage.cpp | 96 ++++----- test/clang-doc/mapper-module.cpp | 20 +- test/clang-doc/mapper-namespace.cpp | 34 +-- test/clang-doc/mapper-record.cpp | 84 ++++---- test/clang-doc/md-comment.cpp | 2 +- test/clang-doc/md-linkage.cpp | 8 +- test/clang-doc/md-namespace.cpp | 6 +- test/clang-doc/md-record.cpp | 24 +-- test/clang-doc/public-comment.cpp | 4 +- test/clang-doc/public-linkage.cpp | 24 +-- test/clang-doc/public-module.cpp | 4 +- test/clang-doc/public-namespace.cpp | 8 +- test/clang-doc/public-record.cpp | 28 +-- test/clang-doc/yaml-comment.cpp | 4 +- test/clang-doc/yaml-linkage.cpp | 36 ++-- test/clang-doc/yaml-module.cpp | 6 +- test/clang-doc/yaml-namespace.cpp | 8 +- test/clang-doc/yaml-record.cpp | 30 +-- unittests/clang-doc/BitcodeTest.cpp | 260 ++++++++++++++++++++++ unittests/clang-doc/CMakeLists.txt | 1 + 27 files changed, 807 insertions(+), 548 deletions(-) create mode 100644 unittests/clang-doc/BitcodeTest.cpp diff --git a/clang-doc/BitcodeWriter.cpp b/clang-doc/BitcodeWriter.cpp index f73724e4f..bc990fa71 100644 --- a/clang-doc/BitcodeWriter.cpp +++ b/clang-doc/BitcodeWriter.cpp @@ -309,10 +309,8 @@ void ClangDocBitcodeWriter::emitRecord(const Location &Loc, RecordId ID) { // FIXME: Assert that the line number is of the appropriate size. Record.push_back(Loc.LineNumber); assert(Loc.Filename.size() < (1U << BitCodeConstants::StringLengthSize)); - // Record.push_back(Loc.Filename.size()); - // Stream.EmitRecordWithBlob(Abbrevs.get(ID), Record, Loc.Filename); - Record.push_back(4); - Stream.EmitRecordWithBlob(Abbrevs.get(ID), Record, "test"); + Record.push_back(Loc.Filename.size()); + Stream.EmitRecordWithBlob(Abbrevs.get(ID), Record, Loc.Filename); } void ClangDocBitcodeWriter::emitRecord(bool Val, RecordId ID) { diff --git a/test/clang-doc/bc-comment.cpp b/test/clang-doc/bc-comment.cpp index 3b006ab8a..8d0cefebd 100644 --- a/test/clang-doc/bc-comment.cpp +++ b/test/clang-doc/bc-comment.cpp @@ -32,169 +32,169 @@ void F(int I, int J) {} // RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'F' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'FullComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'BlockCommandComment' // CHECK-0-NEXT: blob data = 'brief' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: blob data = ' Brief description.' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: blob data = ' Extended description that' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: blob data = ' continues onto the next line.' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'HTMLStartTagComment' // CHECK-0-NEXT: blob data = 'ul' // CHECK-0-NEXT: blob data = 'class' -// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = 'test' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'HTMLStartTagComment' // CHECK-0-NEXT: blob data = 'li' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: blob data = ' Testing.' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'HTMLEndTagComment' // CHECK-0-NEXT: blob data = 'ul' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'VerbatimBlockComment' // CHECK-0-NEXT: blob data = 'verbatim' // CHECK-0-NEXT: blob data = 'endverbatim' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'VerbatimBlockLineComment' // CHECK-0-NEXT: blob data = ' The description continues.' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: blob data = ' --' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParamCommandComment' // CHECK-0-NEXT: blob data = '[out]' // CHECK-0-NEXT: blob data = 'I' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: blob data = ' is a parameter.' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParamCommandComment' // CHECK-0-NEXT: blob data = '[in]' // CHECK-0-NEXT: blob data = 'J' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: blob data = ' is a parameter.' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'BlockCommandComment' // CHECK-0-NEXT: blob data = 'return' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: blob data = ' void' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'FullComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: blob data = ' Bonus comment on definition' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'int' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'I' // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'int' // CHECK-0-NEXT: // CHECK-0-NEXT: diff --git a/test/clang-doc/bc-linkage.cpp b/test/clang-doc/bc-linkage.cpp index 9440798c7..7d3572f57 100644 --- a/test/clang-doc/bc-linkage.cpp +++ b/test/clang-doc/bc-linkage.cpp @@ -99,58 +99,58 @@ inline void anonInlineFunction(); // RUN: llvm-bcanalyzer --dump %t/docs/bc/8960B5C9247D6F5C532756E53A1AD1240FA2146F.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'named' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'namedFunction' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'named' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'namedStaticFunction' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'named' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'namedInlineFunction' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'named' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: @@ -160,105 +160,105 @@ inline void anonInlineFunction(); // RUN: llvm-bcanalyzer --dump %t/docs/bc/7CDD73DCD6CD72F7E5CE25502810A182C66C4B45.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'Class' -// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: blob data = '{{.*}}' // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'int' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'publicField' // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'int' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'protectedField' // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'int' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'privateField' // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'publicMethod' -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'Class' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'Class' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'void' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'protectedMethod' -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'Class' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'Class' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'void' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'privateMethod' -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'Class' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'Class' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'void' // CHECK-1-NEXT: // CHECK-1-NEXT: @@ -268,112 +268,112 @@ inline void anonInlineFunction(); // RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'function' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'void' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'x' // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'inlinedFunction' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'x' // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'functionWithInnerClass' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'x' // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'inlinedFunctionWithInnerClass' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'x' // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'staticFunction' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'void' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'x' // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: @@ -384,129 +384,129 @@ inline void anonInlineFunction(); // RUN: llvm-bcanalyzer --dump %t/docs/bc/4712C5FA37B298A25501D1033C619B65B0ECC449.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'NamedClass' -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'named' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: blob data = '{{.*}}' // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'int' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'namedPublicField' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'int' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'namedProtectedField' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'int' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'namedPrivateField' // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'namedPublicMethod' -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'named' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'void' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'namedProtectedMethod' -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'named' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'void' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'namedPrivateMethod' -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'named' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'void' // CHECK-3-NEXT: // CHECK-3-NEXT: @@ -516,125 +516,125 @@ inline void anonInlineFunction(); // RUN: llvm-bcanalyzer --dump %t/docs/bc/6E8FB72A89761E77020BFCEE9A9A6E64B15CC2A9.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'AnonClass' -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: blob data = '{{.*}}' // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'int' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'anonPublicField' // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'int' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'anonProtectedField' // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'int' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'anonPrivateField' // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'anonPublicMethod' -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'AnonClass' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'AnonClass' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'void' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'anonProtectedMethod' -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'AnonClass' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'AnonClass' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'void' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'anonPrivateMethod' -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'AnonClass' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'AnonClass' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'void' // CHECK-4-NEXT: // CHECK-4-NEXT: @@ -644,54 +644,54 @@ inline void anonInlineFunction(); // RUN: llvm-bcanalyzer --dump %t/docs/bc/83CC52D32583E0771710A7742DE81C839E953AC8.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'anonFunction' -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'void' // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'anonStaticFunction' -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'void' // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'anonInlineFunction' -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'void' // CHECK-5-NEXT: // CHECK-5-NEXT: diff --git a/test/clang-doc/bc-module.cpp b/test/clang-doc/bc-module.cpp index 101d8da85..16a7113cb 100644 --- a/test/clang-doc/bc-module.cpp +++ b/test/clang-doc/bc-module.cpp @@ -19,65 +19,65 @@ export double exportedModuleFunction(double y, int z); // ExternalLinkage // RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'moduleFunction' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'int' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'int' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'x' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'staticModuleFunction' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'int' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'int' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'x' // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'exportedModuleFunction' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'double' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'double' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'y' // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'int' // CHECK-0-NEXT: // CHECK-0-NEXT: diff --git a/test/clang-doc/bc-namespace.cpp b/test/clang-doc/bc-namespace.cpp index 79b35bd9a..790173a6f 100644 --- a/test/clang-doc/bc-namespace.cpp +++ b/test/clang-doc/bc-namespace.cpp @@ -30,25 +30,25 @@ E func(int i) { return X; } // RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'A' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'f' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'A' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: @@ -58,64 +58,64 @@ E func(int i) { return X; } // RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'B' -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'A' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'func' -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'B' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'A' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'enum A::B::E' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'int' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'i' // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'E' -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'B' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'A' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: blob data = '{{.*}}' // CHECK-1-NEXT: blob data = 'X' // CHECK-1-NEXT: // CHECK-1-NEXT: diff --git a/test/clang-doc/bc-record.cpp b/test/clang-doc/bc-record.cpp index 07f0da2eb..5dba09714 100644 --- a/test/clang-doc/bc-record.cpp +++ b/test/clang-doc/bc-record.cpp @@ -46,82 +46,82 @@ class G; // RUN: llvm-bcanalyzer --dump %t/docs/bc/289584A8E0FF4178A794622A547AA622503967A1.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = '{{.*}}' // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = '~E' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ProtectedMethod' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: @@ -131,27 +131,27 @@ class G; // RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'X' -// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: blob data = '{{.*}}' // CHECK-1-NEXT: // CHECK-1-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'C' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: @@ -162,62 +162,62 @@ class G; // RUN: llvm-bcanalyzer --dump %t/docs/bc/4202E8BF0ECB12AE354C8499C52725B0EE30AED5.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'G' -// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: blob data = '{{.*}}' // CHECK-3-NEXT: // CHECK-3-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'Y' -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'X' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: blob data = '{{.*}}' // CHECK-4-NEXT: // CHECK-4-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'H' -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'void' // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'B' -// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: blob data = '{{.*}}' // CHECK-5-NEXT: blob data = 'X' // CHECK-5-NEXT: blob data = 'Y' // CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'Bc' -// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: blob data = '{{.*}}' // CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'A' // CHECK-5-NEXT: blob data = 'B' @@ -226,33 +226,33 @@ class G; // RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-6 // CHECK-6: -// CHECK-6-NEXT: +// CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: +// CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: blob data = 'D' -// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: blob data = '{{.*}}' // CHECK-6-NEXT: // CHECK-6-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-7 // CHECK-7: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: blob data = 'F' -// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: blob data = '{{.*}}' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: blob data = 'E' // CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: blob data = 'D' // CHECK-7-NEXT: @@ -262,24 +262,24 @@ class G; // RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-8 // CHECK-8: -// CHECK-8-NEXT: +// CHECK-8-NEXT: // CHECK-8-NEXT: // CHECK-8-NEXT: -// CHECK-8-NEXT: +// CHECK-8-NEXT: // CHECK-8-NEXT: // CHECK-8-NEXT: blob data = 'A' -// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: blob data = '{{.*}}' // CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: // CHECK-8-NEXT: blob data = 'int' // CHECK-8-NEXT: // CHECK-8-NEXT: // CHECK-8-NEXT: blob data = 'X' // CHECK-8-NEXT: // CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: // CHECK-8-NEXT: blob data = 'int' // CHECK-8-NEXT: // CHECK-8-NEXT: diff --git a/test/clang-doc/mapper-comment.cpp b/test/clang-doc/mapper-comment.cpp index efd3dc54c..4ab32d687 100644 --- a/test/clang-doc/mapper-comment.cpp +++ b/test/clang-doc/mapper-comment.cpp @@ -32,39 +32,39 @@ void F(int I, int J) {} // RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'F' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'FullComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ParagraphComment' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'TextComment' // CHECK-0-NEXT: blob data = ' Bonus comment on definition' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'int' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'I' // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'int' // CHECK-0-NEXT: // CHECK-0-NEXT: diff --git a/test/clang-doc/mapper-linkage.cpp b/test/clang-doc/mapper-linkage.cpp index 8d6b238b0..cdd295edb 100644 --- a/test/clang-doc/mapper-linkage.cpp +++ b/test/clang-doc/mapper-linkage.cpp @@ -99,23 +99,23 @@ inline void anonInlineFunction(); // RUN: llvm-bcanalyzer --dump %t/docs/bc/8960B5C9247D6F5C532756E53A1AD1240FA2146F.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'namedInlineFunction' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'named' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: @@ -125,30 +125,30 @@ inline void anonInlineFunction(); // RUN: llvm-bcanalyzer --dump %t/docs/bc/7CDD73DCD6CD72F7E5CE25502810A182C66C4B45.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'privateMethod' -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'Class' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'Class' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'void' // CHECK-1-NEXT: // CHECK-1-NEXT: @@ -158,22 +158,22 @@ inline void anonInlineFunction(); // RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'staticFunctionWithInnerClass' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: @@ -184,36 +184,36 @@ inline void anonInlineFunction(); // RUN: llvm-bcanalyzer --dump %t/docs/bc/4712C5FA37B298A25501D1033C619B65B0ECC449.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'namedPrivateMethod' -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'named' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: blob data = '{{.*}}' -// CHECK-3-NEXT: +// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'NamedClass' // CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'void' // CHECK-3-NEXT: // CHECK-3-NEXT: @@ -223,35 +223,35 @@ inline void anonInlineFunction(); // RUN: llvm-bcanalyzer --dump %t/docs/bc/6E8FB72A89761E77020BFCEE9A9A6E64B15CC2A9.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'anonPrivateMethod' -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'AnonClass' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' -// CHECK-4-NEXT: +// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'AnonClass' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'void' // CHECK-4-NEXT: // CHECK-4-NEXT: @@ -261,22 +261,22 @@ inline void anonInlineFunction(); // RUN: llvm-bcanalyzer --dump %t/docs/bc/83CC52D32583E0771710A7742DE81C839E953AC8.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'anonInlineFunction' -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: blob data = '{{.*}}' -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'void' // CHECK-5-NEXT: // CHECK-5-NEXT: diff --git a/test/clang-doc/mapper-module.cpp b/test/clang-doc/mapper-module.cpp index 04a34c68d..fb22f98ae 100644 --- a/test/clang-doc/mapper-module.cpp +++ b/test/clang-doc/mapper-module.cpp @@ -19,29 +19,29 @@ export double exportedModuleFunction(double y, int z); // ExternalLinkage // RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'exportedModuleFunction' -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'double' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'double' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'y' // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'int' // CHECK-0-NEXT: // CHECK-0-NEXT: diff --git a/test/clang-doc/mapper-namespace.cpp b/test/clang-doc/mapper-namespace.cpp index d00082331..0cccd29e6 100644 --- a/test/clang-doc/mapper-namespace.cpp +++ b/test/clang-doc/mapper-namespace.cpp @@ -30,23 +30,23 @@ E func(int i) { return X; } // RUN: llvm-bcanalyzer --dump %t/docs/bc/8D042EFFC98B373450BC6B5B90A330C25A150E9C.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'f' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'A' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: @@ -56,35 +56,35 @@ E func(int i) { return X; } // RUN: llvm-bcanalyzer --dump %t/docs/bc/E21AF79E2A9D02554BA090D10DF39FE273F5CDB5.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'func' -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'B' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'A' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: blob data = '{{.*}}' -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'enum A::B::E' // CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'int' // CHECK-1-NEXT: // CHECK-1-NEXT: diff --git a/test/clang-doc/mapper-record.cpp b/test/clang-doc/mapper-record.cpp index 90f76b527..00b199826 100644 --- a/test/clang-doc/mapper-record.cpp +++ b/test/clang-doc/mapper-record.cpp @@ -46,30 +46,30 @@ class G; // RUN: llvm-bcanalyzer --dump %t/docs/bc/289584A8E0FF4178A794622A547AA622503967A1.bc | FileCheck %s --check-prefix CHECK-0 // CHECK-0: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'ProtectedMethod' -// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: blob data = '{{.*}}' -// CHECK-0-NEXT: +// CHECK-0-NEXT: blob data = '{{.*}}' +// CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'E' // CHECK-0-NEXT: // CHECK-0-NEXT: // CHECK-0-NEXT: -// CHECK-0-NEXT: -// CHECK-0-NEXT: +// CHECK-0-NEXT: +// CHECK-0-NEXT: // CHECK-0-NEXT: blob data = 'void' // CHECK-0-NEXT: // CHECK-0-NEXT: @@ -79,27 +79,27 @@ class G; // RUN: llvm-bcanalyzer --dump %t/docs/bc/CA7C7935730B5EACD25F080E9C83FA087CCDC75E.bc | FileCheck %s --check-prefix CHECK-1 // CHECK-1: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: -// CHECK-1-NEXT: +// CHECK-1-NEXT: // CHECK-1-NEXT: // CHECK-1-NEXT: blob data = 'X' -// CHECK-1-NEXT: blob data = '{{.*}}' +// CHECK-1-NEXT: blob data = '{{.*}}' // CHECK-1-NEXT: // CHECK-1-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/06B5F6A19BA9F6A832E127C9968282B94619B210.bc | FileCheck %s --check-prefix CHECK-2 // CHECK-2: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'C' -// CHECK-2-NEXT: blob data = '{{.*}}' -// CHECK-2-NEXT: -// CHECK-2-NEXT: +// CHECK-2-NEXT: blob data = '{{.*}}' +// CHECK-2-NEXT: +// CHECK-2-NEXT: // CHECK-2-NEXT: blob data = 'int' // CHECK-2-NEXT: // CHECK-2-NEXT: @@ -109,44 +109,44 @@ class G; // RUN: llvm-bcanalyzer --dump %t/docs/bc/4202E8BF0ECB12AE354C8499C52725B0EE30AED5.bc | FileCheck %s --check-prefix CHECK-3 // CHECK-3: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: -// CHECK-3-NEXT: +// CHECK-3-NEXT: // CHECK-3-NEXT: // CHECK-3-NEXT: blob data = 'G' -// CHECK-3-NEXT: blob data = '{{.*}}' +// CHECK-3-NEXT: blob data = '{{.*}}' // CHECK-3-NEXT: // CHECK-3-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/641AB4A3D36399954ACDE29C7A8833032BF40472.bc | FileCheck %s --check-prefix CHECK-4 // CHECK-4: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'Y' -// CHECK-4-NEXT: +// CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: blob data = 'X' // CHECK-4-NEXT: // CHECK-4-NEXT: // CHECK-4-NEXT: -// CHECK-4-NEXT: blob data = '{{.*}}' +// CHECK-4-NEXT: blob data = '{{.*}}' // CHECK-4-NEXT: // CHECK-4-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/0000000000000000000000000000000000000000.bc | FileCheck %s --check-prefix CHECK-5 // CHECK-5: -// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: -// CHECK-5-NEXT: -// CHECK-5-NEXT: +// CHECK-5-NEXT: +// CHECK-5-NEXT: // CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'Bc' -// CHECK-5-NEXT: blob data = '{{.*}}' +// CHECK-5-NEXT: blob data = '{{.*}}' // CHECK-5-NEXT: // CHECK-5-NEXT: blob data = 'A' // CHECK-5-NEXT: blob data = 'B' @@ -155,33 +155,33 @@ class G; // RUN: llvm-bcanalyzer --dump %t/docs/bc/0921737541208B8FA9BB42B60F78AC1D779AA054.bc | FileCheck %s --check-prefix CHECK-6 // CHECK-6: -// CHECK-6-NEXT: +// CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: -// CHECK-6-NEXT: +// CHECK-6-NEXT: // CHECK-6-NEXT: // CHECK-6-NEXT: blob data = 'D' -// CHECK-6-NEXT: blob data = '{{.*}}' +// CHECK-6-NEXT: blob data = '{{.*}}' // CHECK-6-NEXT: // CHECK-6-NEXT: // RUN: llvm-bcanalyzer --dump %t/docs/bc/E3B54702FABFF4037025BA194FC27C47006330B5.bc | FileCheck %s --check-prefix CHECK-7 // CHECK-7: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: blob data = 'F' -// CHECK-7-NEXT: blob data = '{{.*}}' +// CHECK-7-NEXT: blob data = '{{.*}}' // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: blob data = 'E' // CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: -// CHECK-7-NEXT: +// CHECK-7-NEXT: // CHECK-7-NEXT: // CHECK-7-NEXT: blob data = 'D' // CHECK-7-NEXT: @@ -191,23 +191,23 @@ class G; // RUN: llvm-bcanalyzer --dump %t/docs/bc/ACE81AFA6627B4CEF2B456FB6E1252925674AF7E.bc | FileCheck %s --check-prefix CHECK-8 // CHECK-8: -// CHECK-8-NEXT: +// CHECK-8-NEXT: // CHECK-8-NEXT: // CHECK-8-NEXT: -// CHECK-8-NEXT: +// CHECK-8-NEXT: // CHECK-8-NEXT: // CHECK-8-NEXT: blob data = 'A' -// CHECK-8-NEXT: blob data = '{{.*}}' +// CHECK-8-NEXT: blob data = '{{.*}}' // CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: // CHECK-8-NEXT: blob data = 'int' // CHECK-8-NEXT: // CHECK-8-NEXT: // CHECK-8-NEXT: blob data = 'X' // CHECK-8-NEXT: -// CHECK-8-NEXT: -// CHECK-8-NEXT: +// CHECK-8-NEXT: +// CHECK-8-NEXT: // CHECK-8-NEXT: blob data = 'int' // CHECK-8-NEXT: // CHECK-8-NEXT: diff --git a/test/clang-doc/md-comment.cpp b/test/clang-doc/md-comment.cpp index 8e8f97a97..cf40c3db7 100644 --- a/test/clang-doc/md-comment.cpp +++ b/test/clang-doc/md-comment.cpp @@ -35,7 +35,7 @@ void F(int I, int J) {} // CHECK-0: ## Functions // CHECK-0: ### F // CHECK-0: *void F(int I, int J)* -// CHECK-0: *Defined at line 28 of test* +// CHECK-0: *Defined at line 28 of {{.*}}* // CHECK-0: **brief** Brief description. // CHECK-0: Extended description that continues onto the next line. // CHECK-0: