diff --git a/clang-tools-extra/clangd/Headers.h b/clang-tools-extra/clangd/Headers.h --- a/clang-tools-extra/clangd/Headers.h +++ b/clang-tools-extra/clangd/Headers.h @@ -22,6 +22,7 @@ #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/Inclusions/HeaderIncludes.h" +#include "clang/Tooling/Inclusions/StandardLibrary.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" @@ -32,79 +33,8 @@ #include namespace clang { -class Decl; -class NamespaceDecl; namespace clangd { -// clangd has a built-in database of standard library symbols. -namespace stdlib { -class Symbol; - -// A standard library header, such as -// Lightweight class, in fact just an index into a table. -class Header { -public: - static llvm::Optional
named(llvm::StringRef Name); - - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Header &H) { - return OS << H.name(); - } - llvm::StringRef name() const; - -private: - Header(unsigned ID) : ID(ID) {} - unsigned ID; - friend Symbol; - friend llvm::DenseMapInfo
; - friend bool operator==(const Header &L, const Header &R) { - return L.ID == R.ID; - } -}; - -// A top-level standard library symbol, such as std::vector -// Lightweight class, in fact just an index into a table. -class Symbol { -public: - static llvm::Optional named(llvm::StringRef Scope, - llvm::StringRef Name); - - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Symbol &S) { - return OS << S.scope() << S.name(); - } - llvm::StringRef scope() const; - llvm::StringRef name() const; - // The preferred header for this symbol (e.g. the suggested insertion). - Header header() const; - // Some symbols may be provided my multiple headers. - llvm::SmallVector
headers() const; - -private: - Symbol(unsigned ID) : ID(ID) {} - unsigned ID; - friend class Recognizer; - friend llvm::DenseMapInfo; - friend bool operator==(const Symbol &L, const Symbol &R) { - return L.ID == R.ID; - } -}; - -// A functor to find the stdlib::Symbol associated with a decl. -// -// For non-top-level decls (std::vector::iterator), returns the top-level -// symbol (std::vector). -class Recognizer { -public: - Recognizer(); - llvm::Optional operator()(const Decl *D); - -private: - using NSSymbolMap = llvm::DenseMap; - NSSymbolMap *namespaceSymbols(const NamespaceDecl *D); - llvm::DenseMap NamespaceCache; -}; - -} // namespace stdlib - /// Returns true if \p Include is literal include like "path" or . bool isLiteralInclude(llvm::StringRef Include); @@ -341,38 +271,6 @@ } }; -template <> struct DenseMapInfo { - static inline clang::clangd::stdlib::Header getEmptyKey() { - return clang::clangd::stdlib::Header(-1); - } - static inline clang::clangd::stdlib::Header getTombstoneKey() { - return clang::clangd::stdlib::Header(-2); - } - static unsigned getHashValue(const clang::clangd::stdlib::Header &H) { - return hash_value(H.ID); - } - static bool isEqual(const clang::clangd::stdlib::Header &LHS, - const clang::clangd::stdlib::Header &RHS) { - return LHS == RHS; - } -}; - -template <> struct DenseMapInfo { - static inline clang::clangd::stdlib::Symbol getEmptyKey() { - return clang::clangd::stdlib::Symbol(-1); - } - static inline clang::clangd::stdlib::Symbol getTombstoneKey() { - return clang::clangd::stdlib::Symbol(-2); - } - static unsigned getHashValue(const clang::clangd::stdlib::Symbol &S) { - return hash_value(S.ID); - } - static bool isEqual(const clang::clangd::stdlib::Symbol &LHS, - const clang::clangd::stdlib::Symbol &RHS) { - return LHS == RHS; - } -}; - } // namespace llvm #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEADERS_H diff --git a/clang-tools-extra/clangd/Headers.cpp b/clang-tools-extra/clangd/Headers.cpp --- a/clang-tools-extra/clangd/Headers.cpp +++ b/clang-tools-extra/clangd/Headers.cpp @@ -350,154 +350,5 @@ RHS.Resolved, RHS.Written); } -namespace stdlib { -static llvm::StringRef *HeaderNames; -static std::pair *SymbolNames; -static unsigned *SymbolHeaderIDs; -static llvm::DenseMap *HeaderIDs; -// Maps symbol name -> Symbol::ID, within a namespace. -using NSSymbolMap = llvm::DenseMap; -static llvm::DenseMap *NamespaceSymbols; - -static int initialize() { - unsigned SymCount = 0; -#define SYMBOL(Name, NS, Header) ++SymCount; -#include "CSymbolMap.inc" -#include "StdSymbolMap.inc" -#undef SYMBOL - SymbolNames = new std::remove_reference_t[SymCount]; - SymbolHeaderIDs = - new std::remove_reference_t[SymCount]; - NamespaceSymbols = new std::remove_reference_t; - HeaderIDs = new std::remove_reference_t; - - auto AddNS = [&](llvm::StringRef NS) -> NSSymbolMap & { - auto R = NamespaceSymbols->try_emplace(NS, nullptr); - if (R.second) - R.first->second = new NSSymbolMap(); - return *R.first->second; - }; - - auto AddHeader = [&](llvm::StringRef Header) -> unsigned { - return HeaderIDs->try_emplace(Header, HeaderIDs->size()).first->second; - }; - - auto Add = [&, SymIndex(0)](llvm::StringRef Name, llvm::StringRef NS, - llvm::StringRef HeaderName) mutable { - if (NS == "None") - NS = ""; - - SymbolNames[SymIndex] = {NS, Name}; - SymbolHeaderIDs[SymIndex] = AddHeader(HeaderName); - - NSSymbolMap &NSSymbols = AddNS(NS); - NSSymbols.try_emplace(Name, SymIndex); - - ++SymIndex; - }; -#define SYMBOL(Name, NS, Header) Add(#Name, #NS, #Header); -#include "CSymbolMap.inc" -#include "StdSymbolMap.inc" -#undef SYMBOL - - HeaderNames = new llvm::StringRef[HeaderIDs->size()]; - for (const auto &E : *HeaderIDs) - HeaderNames[E.second] = E.first; - - return 0; -} - -static void ensureInitialized() { - static int Dummy = initialize(); - (void)Dummy; -} - -llvm::Optional
Header::named(llvm::StringRef Name) { - ensureInitialized(); - auto It = HeaderIDs->find(Name); - if (It == HeaderIDs->end()) - return llvm::None; - return Header(It->second); -} -llvm::StringRef Header::name() const { return HeaderNames[ID]; } -llvm::StringRef Symbol::scope() const { return SymbolNames[ID].first; } -llvm::StringRef Symbol::name() const { return SymbolNames[ID].second; } -llvm::Optional Symbol::named(llvm::StringRef Scope, - llvm::StringRef Name) { - ensureInitialized(); - if (NSSymbolMap *NSSymbols = NamespaceSymbols->lookup(Scope)) { - auto It = NSSymbols->find(Name); - if (It != NSSymbols->end()) - return Symbol(It->second); - } - return llvm::None; -} -Header Symbol::header() const { return Header(SymbolHeaderIDs[ID]); } -llvm::SmallVector
Symbol::headers() const { - return {header()}; // FIXME: multiple in case of ambiguity -} - -Recognizer::Recognizer() { ensureInitialized(); } - -NSSymbolMap *Recognizer::namespaceSymbols(const NamespaceDecl *D) { - auto It = NamespaceCache.find(D); - if (It != NamespaceCache.end()) - return It->second; - - NSSymbolMap *Result = [&]() -> NSSymbolMap * { - if (!D) // Nullptr means the global namespace - return NamespaceSymbols->lookup(""); - if (D->isAnonymousNamespace()) - return nullptr; - if (D->isInlineNamespace()) { - if (auto *Parent = llvm::dyn_cast_or_null(D->getParent())) - return namespaceSymbols(Parent); - return nullptr; - } - return NamespaceSymbols->lookup(printNamespaceScope(*D)); - }(); - NamespaceCache.try_emplace(D, Result); - return Result; -} - -llvm::Optional Recognizer::operator()(const Decl *D) { - // If D is std::vector::iterator, `vector` is the outer symbol to look up. - // We keep all the candidate DCs as some may turn out to be anon enums. - // Do this resolution lazily as we may turn out not to have a std namespace. - llvm::SmallVector IntermediateDecl; - const DeclContext *DC = D->getDeclContext(); - while (DC && !DC->isNamespace()) { - if (NamedDecl::classofKind(DC->getDeclKind())) - IntermediateDecl.push_back(DC); - DC = DC->getParent(); - } - NSSymbolMap *Symbols = namespaceSymbols(cast_or_null(DC)); - if (!Symbols) - return llvm::None; - - llvm::StringRef Name = [&]() -> llvm::StringRef { - for (const auto *SymDC : llvm::reverse(IntermediateDecl)) { - DeclarationName N = cast(SymDC)->getDeclName(); - if (const auto *II = N.getAsIdentifierInfo()) - return II->getName(); - if (!N.isEmpty()) - return ""; // e.g. operator<: give up - } - if (const auto *ND = llvm::dyn_cast(D)) - if (const auto *II = ND->getIdentifier()) - return II->getName(); - return ""; - }(); - if (Name.empty()) - return llvm::None; - - auto It = Symbols->find(Name); - if (It == Symbols->end()) - return llvm::None; - return Symbol(It->second); -} - -} // namespace stdlib - } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/index/CanonicalIncludes.cpp b/clang-tools-extra/clangd/index/CanonicalIncludes.cpp --- a/clang-tools-extra/clangd/index/CanonicalIncludes.cpp +++ b/clang-tools-extra/clangd/index/CanonicalIncludes.cpp @@ -85,7 +85,7 @@ if (Language.CPlusPlus) { static const auto *Symbols = new llvm::StringMap({ #define SYMBOL(Name, NameSpace, Header) {#NameSpace #Name, #Header}, -#include "StdSymbolMap.inc" +#include "clang/Tooling/Inclusions/StdSymbolMap.inc" // There are two std::move()s, this is by far the most common. SYMBOL(move, std::, ) // There are multiple headers for size_t, pick one. @@ -96,7 +96,7 @@ } else if (Language.C11) { static const auto *CSymbols = new llvm::StringMap({ #define SYMBOL(Name, NameSpace, Header) {#Name, #Header}, -#include "CSymbolMap.inc" +#include "clang/Tooling/Inclusions/CSymbolMap.inc" // There are multiple headers for size_t, pick one. SYMBOL(size_t, None, ) #undef SYMBOL diff --git a/clang-tools-extra/clangd/unittests/HeadersTests.cpp b/clang-tools-extra/clangd/unittests/HeadersTests.cpp --- a/clang-tools-extra/clangd/unittests/HeadersTests.cpp +++ b/clang-tools-extra/clangd/unittests/HeadersTests.cpp @@ -409,22 +409,6 @@ EXPECT_FALSE(Includes.isSelfContained(getID("pp_depend.h", Includes))); } -TEST(StdlibTest, All) { - auto VectorH = stdlib::Header::named(""); - EXPECT_TRUE(VectorH); - EXPECT_EQ(llvm::to_string(*VectorH), ""); - EXPECT_FALSE(stdlib::Header::named("HeadersTests.cpp")); - - auto Vector = stdlib::Symbol::named("std::", "vector"); - EXPECT_TRUE(Vector); - EXPECT_EQ(llvm::to_string(*Vector), "std::vector"); - EXPECT_FALSE(stdlib::Symbol::named("std::", "dongle")); - EXPECT_FALSE(stdlib::Symbol::named("clang::", "ASTContext")); - - EXPECT_EQ(Vector->header(), *VectorH); - EXPECT_THAT(Vector->headers(), ElementsAre(*VectorH)); -} - TEST(StdlibTest, Recognizer) { auto TU = TestTU::withCode(R"cpp( namespace std { diff --git a/clang-tools-extra/clangd/CSymbolMap.inc b/clang/include/clang/Tooling/Inclusions/CSymbolMap.inc rename from clang-tools-extra/clangd/CSymbolMap.inc rename to clang/include/clang/Tooling/Inclusions/CSymbolMap.inc diff --git a/clang/include/clang/Tooling/Inclusions/StandardLibrary.h b/clang/include/clang/Tooling/Inclusions/StandardLibrary.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Tooling/Inclusions/StandardLibrary.h @@ -0,0 +1,110 @@ +#include "clang/AST/Decl.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace stdlib { + +class Symbol; + +// A standard library header, such as +// Lightweight class, in fact just an index into a table. +class Header { +public: + static llvm::Optional
named(llvm::StringRef Name); + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Header &H) { + return OS << H.name(); + } + llvm::StringRef name() const; + +private: + Header(unsigned ID) : ID(ID) {} + unsigned ID; + friend Symbol; + friend llvm::DenseMapInfo
; + friend bool operator==(const Header &L, const Header &R) { + return L.ID == R.ID; + } +}; + +// A top-level standard library symbol, such as std::vector +// Lightweight class, in fact just an index into a table. +class Symbol { +public: + static llvm::Optional named(llvm::StringRef Scope, + llvm::StringRef Name); + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Symbol &S) { + return OS << S.scope() << S.name(); + } + llvm::StringRef scope() const; + llvm::StringRef name() const; + // The preferred header for this symbol (e.g. the suggested insertion). + Header header() const; + // Some symbols may be provided my multiple headers. + llvm::SmallVector
headers() const; + +private: + Symbol(unsigned ID) : ID(ID) {} + unsigned ID; + friend class Recognizer; + friend llvm::DenseMapInfo; + friend bool operator==(const Symbol &L, const Symbol &R) { + return L.ID == R.ID; + } +}; + +// A functor to find the stdlib::Symbol associated with a decl. +// +// For non-top-level decls (std::vector::iterator), returns the top-level +// symbol (std::vector). +class Recognizer { +public: + Recognizer(); + llvm::Optional operator()(const Decl *D); + +private: + using NSSymbolMap = llvm::DenseMap; + NSSymbolMap *namespaceSymbols(const NamespaceDecl *D); + llvm::DenseMap NamespaceCache; +}; + +} // namespace stdlib +} // namespace clang + +namespace llvm { + +template <> struct DenseMapInfo { + static inline clang::stdlib::Header getEmptyKey() { + return clang::stdlib::Header(-1); + } + static inline clang::stdlib::Header getTombstoneKey() { + return clang::stdlib::Header(-2); + } + static unsigned getHashValue(const clang::stdlib::Header &H) { + return hash_value(H.ID); + } + static bool isEqual(const clang::stdlib::Header &LHS, + const clang::stdlib::Header &RHS) { + return LHS == RHS; + } +}; + +template <> struct DenseMapInfo { + static inline clang::stdlib::Symbol getEmptyKey() { + return clang::stdlib::Symbol(-1); + } + static inline clang::stdlib::Symbol getTombstoneKey() { + return clang::stdlib::Symbol(-2); + } + static unsigned getHashValue(const clang::stdlib::Symbol &S) { + return hash_value(S.ID); + } + static bool isEqual(const clang::stdlib::Symbol &LHS, + const clang::stdlib::Symbol &RHS) { + return LHS == RHS; + } +}; +} // namespace llvm diff --git a/clang-tools-extra/clangd/StdSymbolMap.inc b/clang/include/clang/Tooling/Inclusions/StdSymbolMap.inc rename from clang-tools-extra/clangd/StdSymbolMap.inc rename to clang/include/clang/Tooling/Inclusions/StdSymbolMap.inc diff --git a/clang/lib/Tooling/Inclusions/CMakeLists.txt b/clang/lib/Tooling/Inclusions/CMakeLists.txt --- a/clang/lib/Tooling/Inclusions/CMakeLists.txt +++ b/clang/lib/Tooling/Inclusions/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_library(clangToolingInclusions HeaderIncludes.cpp IncludeStyle.cpp + StandardLibrary.cpp LINK_LIBS clangBasic diff --git a/clang/lib/Tooling/Inclusions/StandardLibrary.cpp b/clang/lib/Tooling/Inclusions/StandardLibrary.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Tooling/Inclusions/StandardLibrary.cpp @@ -0,0 +1,154 @@ +#include "clang/Tooling/Inclusions/StandardLibrary.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace stdlib { + +static llvm::StringRef *HeaderNames; +static std::pair *SymbolNames; +static unsigned *SymbolHeaderIDs; +static llvm::DenseMap *HeaderIDs; +// Maps symbol name -> Symbol::ID, within a namespace. +using NSSymbolMap = llvm::DenseMap; +static llvm::DenseMap *NamespaceSymbols; + +static int initialize() { + unsigned SymCount = 0; +#define SYMBOL(Name, NS, Header) ++SymCount; +#include "clang/Tooling/Inclusions/CSymbolMap.inc" +#include "clang/Tooling/Inclusions/StdSymbolMap.inc" +#undef SYMBOL + SymbolNames = new std::remove_reference_t[SymCount]; + SymbolHeaderIDs = + new std::remove_reference_t[SymCount]; + NamespaceSymbols = new std::remove_reference_t; + HeaderIDs = new std::remove_reference_t; + + auto AddNS = [&](llvm::StringRef NS) -> NSSymbolMap & { + auto R = NamespaceSymbols->try_emplace(NS, nullptr); + if (R.second) + R.first->second = new NSSymbolMap(); + return *R.first->second; + }; + + auto AddHeader = [&](llvm::StringRef Header) -> unsigned { + return HeaderIDs->try_emplace(Header, HeaderIDs->size()).first->second; + }; + + auto Add = [&, SymIndex(0)](llvm::StringRef Name, llvm::StringRef NS, + llvm::StringRef HeaderName) mutable { + if (NS == "None") + NS = ""; + + SymbolNames[SymIndex] = {NS, Name}; + SymbolHeaderIDs[SymIndex] = AddHeader(HeaderName); + + NSSymbolMap &NSSymbols = AddNS(NS); + NSSymbols.try_emplace(Name, SymIndex); + + ++SymIndex; + }; +#define SYMBOL(Name, NS, Header) Add(#Name, #NS, #Header); +#include "clang/Tooling/Inclusions/CSymbolMap.inc" +#include "clang/Tooling/Inclusions/StdSymbolMap.inc" +#undef SYMBOL + + HeaderNames = new llvm::StringRef[HeaderIDs->size()]; + for (const auto &E : *HeaderIDs) + HeaderNames[E.second] = E.first; + + return 0; +} + +static void ensureInitialized() { + static int Dummy = initialize(); + (void)Dummy; +} + +llvm::Optional
Header::named(llvm::StringRef Name) { + ensureInitialized(); + auto It = HeaderIDs->find(Name); + if (It == HeaderIDs->end()) + return llvm::None; + return Header(It->second); +} +llvm::StringRef Header::name() const { return HeaderNames[ID]; } +llvm::StringRef Symbol::scope() const { return SymbolNames[ID].first; } +llvm::StringRef Symbol::name() const { return SymbolNames[ID].second; } +llvm::Optional Symbol::named(llvm::StringRef Scope, + llvm::StringRef Name) { + ensureInitialized(); + if (NSSymbolMap *NSSymbols = NamespaceSymbols->lookup(Scope)) { + auto It = NSSymbols->find(Name); + if (It != NSSymbols->end()) + return Symbol(It->second); + } + return llvm::None; +} +Header Symbol::header() const { return Header(SymbolHeaderIDs[ID]); } +llvm::SmallVector
Symbol::headers() const { + return {header()}; // FIXME: multiple in case of ambiguity +} + +Recognizer::Recognizer() { ensureInitialized(); } + +NSSymbolMap *Recognizer::namespaceSymbols(const NamespaceDecl *D) { + auto It = NamespaceCache.find(D); + if (It != NamespaceCache.end()) + return It->second; + + NSSymbolMap *Result = [&]() -> NSSymbolMap * { + if (!D) // Nullptr means the global namespace + return NamespaceSymbols->lookup(""); + if (D->isAnonymousNamespace()) + return nullptr; + // Get namespace symbols for the first non-inline parent. + if (D->isInlineNamespace()) + if (auto *Parent = llvm::dyn_cast_or_null(D->getParent())) + return namespaceSymbols(Parent); + return NamespaceSymbols->lookup((D->getName() + "::").str()); + }(); + NamespaceCache.try_emplace(D, Result); + return Result; +} + +llvm::Optional Recognizer::operator()(const Decl *D) { + // If D is std::vector::iterator, `vector` is the outer symbol to look up. + // We keep all the candidate DCs as some may turn out to be anon enums. + // Do this resolution lazily as we may turn out not to have a std namespace. + llvm::SmallVector IntermediateDecl; + const DeclContext *DC = D->getDeclContext(); + while (DC && !DC->isNamespace()) { + if (NamedDecl::classofKind(DC->getDeclKind())) + IntermediateDecl.push_back(DC); + DC = DC->getParent(); + } + NSSymbolMap *Symbols = namespaceSymbols(cast_or_null(DC)); + if (!Symbols) + return llvm::None; + + llvm::StringRef Name = [&]() -> llvm::StringRef { + for (const auto *SymDC : llvm::reverse(IntermediateDecl)) { + DeclarationName N = cast(SymDC)->getDeclName(); + if (const auto *II = N.getAsIdentifierInfo()) + return II->getName(); + if (!N.isEmpty()) + return ""; // e.g. operator<: give up + } + if (const auto *ND = llvm::dyn_cast(D)) + if (const auto *II = ND->getIdentifier()) + return II->getName(); + return ""; + }(); + if (Name.empty()) + return llvm::None; + + auto It = Symbols->find(Name); + if (It == Symbols->end()) + return llvm::None; + return Symbol(It->second); +} + +} // namespace stdlib +} // namespace clang diff --git a/clang/unittests/Tooling/CMakeLists.txt b/clang/unittests/Tooling/CMakeLists.txt --- a/clang/unittests/Tooling/CMakeLists.txt +++ b/clang/unittests/Tooling/CMakeLists.txt @@ -17,6 +17,7 @@ ExecutionTest.cpp FixItTest.cpp HeaderIncludesTest.cpp + StandardLibraryTest.cpp LexicallyOrderedRecursiveASTVisitorTest.cpp LookupTest.cpp QualTypeNamesTest.cpp diff --git a/clang/unittests/Tooling/StandardLibraryTest.cpp b/clang/unittests/Tooling/StandardLibraryTest.cpp new file mode 100644 --- /dev/null +++ b/clang/unittests/Tooling/StandardLibraryTest.cpp @@ -0,0 +1,39 @@ +//===- unittest/Tooling/StandardLibrary.cpp -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Inclusions/StandardLibrary.h" +#include "llvm/Support/ScopedPrinter.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using ::testing::ElementsAre; + +namespace clang { +namespace tooling { +namespace { + +TEST(StdlibTest, All) { + auto VectorH = stdlib::Header::named(""); + EXPECT_TRUE(VectorH); + EXPECT_EQ(llvm::to_string(*VectorH), ""); + EXPECT_FALSE(stdlib::Header::named("HeadersTests.cpp")); + + auto Vector = stdlib::Symbol::named("std::", "vector"); + EXPECT_TRUE(Vector); + EXPECT_EQ(llvm::to_string(*Vector), "std::vector"); + EXPECT_FALSE(stdlib::Symbol::named("std::", "dongle")); + EXPECT_FALSE(stdlib::Symbol::named("clang::", "ASTContext")); + + EXPECT_EQ(Vector->header(), *VectorH); + EXPECT_THAT(Vector->headers(), ElementsAre(*VectorH)); +} + +} // namespace +} // namespace tooling +} // namespace clang