Skip to content

Commit 80e15f7

Browse files
committed
[clangd] Collect comments from function definitions into the index
This is useful with projects that put their (doxygen) comments at the implementation site, rather than the header.
1 parent ca95bee commit 80e15f7

File tree

5 files changed

+69
-11
lines changed

5 files changed

+69
-11
lines changed

clang-tools-extra/clangd/index/Symbol.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,11 @@ struct Symbol {
145145
ImplementationDetail = 1 << 2,
146146
/// Symbol is visible to other files (not e.g. a static helper function).
147147
VisibleOutsideFile = 1 << 3,
148+
/// Symbol has an attached documentation comment.
149+
HasDocComment = 1 << 4
148150
};
149-
150151
SymbolFlag Flags = SymbolFlag::None;
152+
151153
/// FIXME: also add deprecation message and fixit?
152154
};
153155

clang-tools-extra/clangd/index/SymbolCollector.cpp

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -635,17 +635,20 @@ bool SymbolCollector::handleDeclOccurrence(
635635
return true;
636636

637637
const Symbol *BasicSymbol = Symbols.find(ID);
638-
if (isPreferredDeclaration(*OriginalDecl, Roles))
638+
bool SkipDocCheckInDef = false;
639+
if (isPreferredDeclaration(*OriginalDecl, Roles)) {
639640
// If OriginalDecl is preferred, replace/create the existing canonical
640641
// declaration (e.g. a class forward declaration). There should be at most
641642
// one duplicate as we expect to see only one preferred declaration per
642643
// TU, because in practice they are definitions.
643644
BasicSymbol = addDeclaration(*OriginalDecl, std::move(ID), IsMainFileOnly);
644-
else if (!BasicSymbol || DeclIsCanonical)
645+
SkipDocCheckInDef = true;
646+
} else if (!BasicSymbol || DeclIsCanonical) {
645647
BasicSymbol = addDeclaration(*ND, std::move(ID), IsMainFileOnly);
648+
}
646649

647650
if (Roles & static_cast<unsigned>(index::SymbolRole::Definition))
648-
addDefinition(*OriginalDecl, *BasicSymbol);
651+
addDefinition(*OriginalDecl, *BasicSymbol, SkipDocCheckInDef);
649652

650653
return true;
651654
}
@@ -1025,15 +1028,17 @@ const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, SymbolID ID,
10251028
*ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
10261029
*CompletionTUInfo,
10271030
/*IncludeBriefComments*/ false);
1028-
std::string Documentation =
1029-
formatDocumentation(*CCS, getDocComment(Ctx, SymbolCompletion,
1030-
/*CommentsFromHeaders=*/true));
1031+
std::string DocComment = getDocComment(Ctx, SymbolCompletion,
1032+
/*CommentsFromHeaders=*/true);
1033+
std::string Documentation = formatDocumentation(*CCS, DocComment);
10311034
if (!(S.Flags & Symbol::IndexedForCodeCompletion)) {
10321035
if (Opts.StoreAllDocumentation)
10331036
S.Documentation = Documentation;
10341037
Symbols.insert(S);
10351038
return Symbols.find(S.ID);
10361039
}
1040+
if (!DocComment.empty())
1041+
S.Flags |= Symbol::HasDocComment;
10371042
S.Documentation = Documentation;
10381043
std::string Signature;
10391044
std::string SnippetSuffix;
@@ -1058,8 +1063,8 @@ const Symbol *SymbolCollector::addDeclaration(const NamedDecl &ND, SymbolID ID,
10581063
return Symbols.find(S.ID);
10591064
}
10601065

1061-
void SymbolCollector::addDefinition(const NamedDecl &ND,
1062-
const Symbol &DeclSym) {
1066+
void SymbolCollector::addDefinition(const NamedDecl &ND, const Symbol &DeclSym,
1067+
bool SkipDocCheck) {
10631068
if (DeclSym.Definition)
10641069
return;
10651070
const auto &SM = ND.getASTContext().getSourceManager();
@@ -1074,6 +1079,27 @@ void SymbolCollector::addDefinition(const NamedDecl &ND,
10741079
Symbol S = DeclSym;
10751080
// FIXME: use the result to filter out symbols.
10761081
S.Definition = *DefLoc;
1082+
1083+
std::string DocComment;
1084+
std::string Documentation;
1085+
if (!SkipDocCheck && !(S.Flags & Symbol::HasDocComment) &&
1086+
(llvm::isa<FunctionDecl>(ND) || llvm::isa<CXXMethodDecl>(ND))) {
1087+
CodeCompletionResult SymbolCompletion(&getTemplateOrThis(ND), 0);
1088+
const auto *CCS = SymbolCompletion.CreateCodeCompletionString(
1089+
*ASTCtx, *PP, CodeCompletionContext::CCC_Symbol, *CompletionAllocator,
1090+
*CompletionTUInfo,
1091+
/*IncludeBriefComments*/ false);
1092+
DocComment = getDocComment(ND.getASTContext(), SymbolCompletion,
1093+
/*CommentsFromHeaders=*/true);
1094+
if (!S.Documentation.empty())
1095+
Documentation = S.Documentation.str() + '\n' + DocComment;
1096+
else
1097+
Documentation = formatDocumentation(*CCS, DocComment);
1098+
if (!DocComment.empty())
1099+
S.Flags |= Symbol::HasDocComment;
1100+
S.Documentation = Documentation;
1101+
}
1102+
10771103
Symbols.insert(S);
10781104
}
10791105

clang-tools-extra/clangd/index/SymbolCollector.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ class SymbolCollector : public index::IndexDataConsumer {
161161
private:
162162
const Symbol *addDeclaration(const NamedDecl &, SymbolID,
163163
bool IsMainFileSymbol);
164-
void addDefinition(const NamedDecl &, const Symbol &DeclSymbol);
164+
void addDefinition(const NamedDecl &, const Symbol &DeclSymbol,
165+
bool SkipDocCheck);
165166
void processRelations(const NamedDecl &ND, const SymbolID &ID,
166167
ArrayRef<index::SymbolRelation> Relations);
167168

clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,26 @@ TEST_F(SymbolCollectorTest, Documentation) {
14771477
forCodeCompletion(false))));
14781478
}
14791479

1480+
TEST_F(SymbolCollectorTest, DocumentationInMain) {
1481+
const std::string Header = R"(
1482+
// doc Foo
1483+
class Foo {
1484+
void f();
1485+
};
1486+
)";
1487+
const std::string Main = R"(
1488+
// doc f
1489+
void Foo::f() {}
1490+
)";
1491+
CollectorOpts.StoreAllDocumentation = true;
1492+
runSymbolCollector(Header, Main);
1493+
EXPECT_THAT(Symbols,
1494+
UnorderedElementsAre(
1495+
AllOf(qName("Foo"), doc("doc Foo"), forCodeCompletion(true)),
1496+
AllOf(qName("Foo::f"), doc("doc f"), returnType(""),
1497+
forCodeCompletion(false))));
1498+
}
1499+
14801500
TEST_F(SymbolCollectorTest, ClassMembers) {
14811501
const std::string Header = R"(
14821502
class Foo {

clang/lib/AST/ASTContext.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,8 +451,17 @@ const RawComment *ASTContext::getRawCommentForAnyRedecl(
451451
if (LastCheckedRedecl) {
452452
if (LastCheckedRedecl == Redecl) {
453453
LastCheckedRedecl = nullptr;
454+
continue;
455+
}
456+
if (auto F = llvm::dyn_cast<FunctionDecl>(Redecl)) {
457+
if (!F->isThisDeclarationADefinition())
458+
continue;
459+
} else if (auto M = llvm::dyn_cast<CXXMethodDecl>(Redecl)) {
460+
if (!M->isThisDeclarationADefinition())
461+
continue;
462+
} else {
463+
continue;
454464
}
455-
continue;
456465
}
457466
const RawComment *RedeclComment = getRawCommentForDeclNoCache(Redecl);
458467
if (RedeclComment) {

0 commit comments

Comments
 (0)