diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -731,7 +731,7 @@ llvm::raw_string_ostream OS(HI.Definition); A->printPretty(OS, AST.getASTContext().getPrintingPolicy()); } - // FIXME: attributes have documentation, can we get at that? + HI.Documentation = Attr::getDocumentation(A->getKind()).str(); return HI; } diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp --- a/clang-tools-extra/clangd/unittests/HoverTests.cpp +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -12,6 +12,7 @@ #include "TestIndex.h" #include "TestTU.h" #include "index/MemIndex.h" +#include "clang/AST/Attr.h" #include "clang/Basic/Specifiers.h" #include "clang/Index/IndexSymbol.h" #include "llvm/ADT/None.h" @@ -2384,7 +2385,7 @@ HI.Name = "nonnull"; HI.Kind = index::SymbolKind::Unknown; // FIXME: no suitable value HI.Definition = "__attribute__((nonnull))"; - HI.Documentation = ""; // FIXME + HI.Documentation = Attr::getDocumentation(attr::NonNull).str(); }}, }; diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -109,6 +109,8 @@ // Pretty print this attribute. void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const; + + static StringRef getDocumentation(attr::Kind); }; class TypeAttr : public Attr { diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -13,6 +13,11 @@ SOURCE Interp/Opcodes.td TARGET Opcodes) +clang_tablegen(AttrDocTable.cpp -gen-clang-attr-doc-table + -I ${CMAKE_CURRENT_SOURCE_DIR}/../../include/ + SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/../../include/clang/Basic/Attr.td + TARGET ClangAttrDocTable) + add_clang_library(clangAST APValue.cpp ASTConcept.cpp @@ -24,6 +29,7 @@ ASTImporterLookupTable.cpp ASTStructuralEquivalence.cpp ASTTypeTraits.cpp + AttrDocTable.cpp AttrImpl.cpp Comment.cpp CommentBriefParser.cpp diff --git a/clang/unittests/AST/AttrTest.cpp b/clang/unittests/AST/AttrTest.cpp new file mode 100644 --- /dev/null +++ b/clang/unittests/AST/AttrTest.cpp @@ -0,0 +1,24 @@ +//===- unittests/AST/AttrTests.cpp --- Attribute tests --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Attr.h" +#include "clang/Basic/AttrKinds.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace clang; + +namespace { + +TEST(Attr, Doc) { + EXPECT_THAT(Attr::getDocumentation(attr::Used).str(), + testing::HasSubstr("The compiler must emit the definition even " + "if it appears to be unused")); +} + +} // namespace diff --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt --- a/clang/unittests/AST/CMakeLists.txt +++ b/clang/unittests/AST/CMakeLists.txt @@ -15,6 +15,7 @@ ASTTraverserTest.cpp ASTTypeTraitsTest.cpp ASTVectorTest.cpp + AttrTest.cpp CommentLexer.cpp CommentParser.cpp CommentTextTest.cpp diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -4210,6 +4210,42 @@ getPragmaAttributeSupport(Records).generateParsingHelpers(OS); } +void EmitClangAttrDocTable(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("Clang attribute documentation", OS); + + OS << R"cpp( + #include "clang/AST/Attr.h" + #include "llvm/ADT/StringRef.h" + )cpp"; + std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); + for (const auto *A : Attrs) { + if (!A->getValueAsBit("ASTNode")) + continue; + std::vector Docs = A->getValueAsListOfDefs("Documentation"); + for (const auto *D : Docs) { + OS << "\nstatic const char AttrDoc_" << A->getName() << "[] = " + << "R\"reST(" + << D->getValueAsOptionalString("Content").getValueOr("").trim() + << ")reST\";\n"; + // Only look at the first documentation if there are several. + // (Currently there's only one such attr, revisit if this becomes common). + break; + } + } + OS << R"cpp( + static const llvm::StringRef AttrDoc[] = { + #define ATTR(NAME) AttrDoc_##NAME, + #include "clang/Basic/AttrList.inc" + }; + + llvm::StringRef clang::Attr::getDocumentation(clang::attr::Kind K) { + if(K < llvm::array_lengthof(AttrDoc)) + return AttrDoc[K]; + return ""; + } + )cpp"; +} + enum class SpellingKind { GNU, CXX11, diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp --- a/clang/utils/TableGen/TableGen.cpp +++ b/clang/utils/TableGen/TableGen.cpp @@ -30,6 +30,7 @@ GenClangAttrSubjectMatchRulesParserStringSwitches, GenClangAttrImpl, GenClangAttrList, + GenClangAttrDocTable, GenClangAttrSubjectMatchRuleList, GenClangAttrPCHRead, GenClangAttrPCHWrite, @@ -115,6 +116,8 @@ "Generate clang attribute implementations"), clEnumValN(GenClangAttrList, "gen-clang-attr-list", "Generate a clang attribute list"), + clEnumValN(GenClangAttrDocTable, "gen-clang-attr-doc-table", + "Generate a table of attribute documentation"), clEnumValN(GenClangAttrSubjectMatchRuleList, "gen-clang-attr-subject-match-rule-list", "Generate a clang attribute subject match rule list"), @@ -280,6 +283,9 @@ case GenClangAttrList: EmitClangAttrList(Records, OS); break; + case GenClangAttrDocTable: + EmitClangAttrDocTable(Records, OS); + break; case GenClangAttrSubjectMatchRuleList: EmitClangAttrSubjectMatchRuleList(Records, OS); break; diff --git a/clang/utils/TableGen/TableGenBackends.h b/clang/utils/TableGen/TableGenBackends.h --- a/clang/utils/TableGen/TableGenBackends.h +++ b/clang/utils/TableGen/TableGenBackends.h @@ -61,6 +61,7 @@ llvm::raw_ostream &OS); void EmitClangAttrNodeTraverse(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitClangAttrDocTable(llvm::RecordKeeper &Records, llvm::raw_ostream &OS); void EmitClangDiagsDefs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS, const std::string &Component);