[AST][clangd] Expose documentation of Attrs on hover.

This adds a method to Attr to get at the documentation programmatically.

Differential Revision: https://reviews.llvm.org/D107703
This commit is contained in:
Sam McCall 2021-08-08 00:41:40 +02:00
parent 2f1ee56f3c
commit 18f9e25ce1
9 changed files with 79 additions and 2 deletions

View File

@ -731,7 +731,7 @@ llvm::Optional<HoverInfo> getHoverContents(const Attr *A, ParsedAST &AST) {
llvm::raw_string_ostream OS(HI.Definition); llvm::raw_string_ostream OS(HI.Definition);
A->printPretty(OS, AST.getASTContext().getPrintingPolicy()); A->printPretty(OS, AST.getASTContext().getPrintingPolicy());
} }
// FIXME: attributes have documentation, can we get at that? HI.Documentation = Attr::getDocumentation(A->getKind()).str();
return HI; return HI;
} }

View File

@ -12,6 +12,7 @@
#include "TestIndex.h" #include "TestIndex.h"
#include "TestTU.h" #include "TestTU.h"
#include "index/MemIndex.h" #include "index/MemIndex.h"
#include "clang/AST/Attr.h"
#include "clang/Basic/Specifiers.h" #include "clang/Basic/Specifiers.h"
#include "clang/Index/IndexSymbol.h" #include "clang/Index/IndexSymbol.h"
#include "llvm/ADT/None.h" #include "llvm/ADT/None.h"
@ -2384,7 +2385,7 @@ TEST(Hover, All) {
HI.Name = "nonnull"; HI.Name = "nonnull";
HI.Kind = index::SymbolKind::Unknown; // FIXME: no suitable value HI.Kind = index::SymbolKind::Unknown; // FIXME: no suitable value
HI.Definition = "__attribute__((nonnull))"; HI.Definition = "__attribute__((nonnull))";
HI.Documentation = ""; // FIXME HI.Documentation = Attr::getDocumentation(attr::NonNull).str();
}}, }},
}; };

View File

@ -109,6 +109,8 @@ public:
// Pretty print this attribute. // Pretty print this attribute.
void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const; void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const;
static StringRef getDocumentation(attr::Kind);
}; };
class TypeAttr : public Attr { class TypeAttr : public Attr {

View File

@ -13,6 +13,11 @@ clang_tablegen(Opcodes.inc
SOURCE Interp/Opcodes.td SOURCE Interp/Opcodes.td
TARGET Opcodes) 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 add_clang_library(clangAST
APValue.cpp APValue.cpp
ASTConcept.cpp ASTConcept.cpp
@ -24,6 +29,7 @@ add_clang_library(clangAST
ASTImporterLookupTable.cpp ASTImporterLookupTable.cpp
ASTStructuralEquivalence.cpp ASTStructuralEquivalence.cpp
ASTTypeTraits.cpp ASTTypeTraits.cpp
AttrDocTable.cpp
AttrImpl.cpp AttrImpl.cpp
Comment.cpp Comment.cpp
CommentBriefParser.cpp CommentBriefParser.cpp

View File

@ -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

View File

@ -15,6 +15,7 @@ add_clang_unittest(ASTTests
ASTTraverserTest.cpp ASTTraverserTest.cpp
ASTTypeTraitsTest.cpp ASTTypeTraitsTest.cpp
ASTVectorTest.cpp ASTVectorTest.cpp
AttrTest.cpp
CommentLexer.cpp CommentLexer.cpp
CommentParser.cpp CommentParser.cpp
CommentTextTest.cpp CommentTextTest.cpp

View File

@ -4210,6 +4210,42 @@ void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records,
getPragmaAttributeSupport(Records).generateParsingHelpers(OS); 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<Record *> Attrs = Records.getAllDerivedDefinitions("Attr");
for (const auto *A : Attrs) {
if (!A->getValueAsBit("ASTNode"))
continue;
std::vector<Record *> 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 { enum class SpellingKind {
GNU, GNU,
CXX11, CXX11,

View File

@ -30,6 +30,7 @@ enum ActionType {
GenClangAttrSubjectMatchRulesParserStringSwitches, GenClangAttrSubjectMatchRulesParserStringSwitches,
GenClangAttrImpl, GenClangAttrImpl,
GenClangAttrList, GenClangAttrList,
GenClangAttrDocTable,
GenClangAttrSubjectMatchRuleList, GenClangAttrSubjectMatchRuleList,
GenClangAttrPCHRead, GenClangAttrPCHRead,
GenClangAttrPCHWrite, GenClangAttrPCHWrite,
@ -115,6 +116,8 @@ cl::opt<ActionType> Action(
"Generate clang attribute implementations"), "Generate clang attribute implementations"),
clEnumValN(GenClangAttrList, "gen-clang-attr-list", clEnumValN(GenClangAttrList, "gen-clang-attr-list",
"Generate a clang attribute list"), "Generate a clang attribute list"),
clEnumValN(GenClangAttrDocTable, "gen-clang-attr-doc-table",
"Generate a table of attribute documentation"),
clEnumValN(GenClangAttrSubjectMatchRuleList, clEnumValN(GenClangAttrSubjectMatchRuleList,
"gen-clang-attr-subject-match-rule-list", "gen-clang-attr-subject-match-rule-list",
"Generate a clang attribute subject match rule list"), "Generate a clang attribute subject match rule list"),
@ -280,6 +283,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenClangAttrList: case GenClangAttrList:
EmitClangAttrList(Records, OS); EmitClangAttrList(Records, OS);
break; break;
case GenClangAttrDocTable:
EmitClangAttrDocTable(Records, OS);
break;
case GenClangAttrSubjectMatchRuleList: case GenClangAttrSubjectMatchRuleList:
EmitClangAttrSubjectMatchRuleList(Records, OS); EmitClangAttrSubjectMatchRuleList(Records, OS);
break; break;

View File

@ -61,6 +61,7 @@ void EmitClangAttrTextNodeDump(llvm::RecordKeeper &Records,
llvm::raw_ostream &OS); llvm::raw_ostream &OS);
void EmitClangAttrNodeTraverse(llvm::RecordKeeper &Records, void EmitClangAttrNodeTraverse(llvm::RecordKeeper &Records,
llvm::raw_ostream &OS); llvm::raw_ostream &OS);
void EmitClangAttrDocTable(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitClangDiagsDefs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS, void EmitClangDiagsDefs(llvm::RecordKeeper &Records, llvm::raw_ostream &OS,
const std::string &Component); const std::string &Component);