Skip to content

Commit 64366d4

Browse files
committed
[clangd] Rollforward include-cleaner library usage in symbol collector.
Differential Revision: https://reviews.llvm.org/D156659
1 parent 2757085 commit 64366d4

File tree

5 files changed

+143
-24
lines changed

5 files changed

+143
-24
lines changed

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

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -821,25 +821,45 @@ void SymbolCollector::setIncludeLocation(const Symbol &S, SourceLocation DefLoc,
821821
// Use the expansion location to get the #include header since this is
822822
// where the symbol is exposed.
823823
IncludeFiles[S.ID] = SM.getDecomposedExpansionLoc(DefLoc).first;
824+
825+
// We update providers for a symbol with each occurence, as SymbolCollector
826+
// might run while parsing, rather than at the end of a translation unit.
827+
// Hence we see more and more redecls over time.
828+
auto [It, Inserted] = SymbolProviders.try_emplace(S.ID);
829+
auto Headers =
830+
include_cleaner::headersForSymbol(Sym, SM, Opts.PragmaIncludes);
831+
if (Headers.empty())
832+
return;
833+
834+
auto *HeadersIter = Headers.begin();
835+
include_cleaner::Header H = *HeadersIter;
836+
while (HeadersIter != Headers.end() &&
837+
H.kind() == include_cleaner::Header::Physical &&
838+
!tooling::isSelfContainedHeader(H.physical(), SM,
839+
PP->getHeaderSearchInfo())) {
840+
H = *HeadersIter;
841+
HeadersIter++;
842+
}
843+
It->second = H;
824844
}
825845

826846
llvm::StringRef getStdHeader(const Symbol *S, const LangOptions &LangOpts) {
827847
tooling::stdlib::Lang Lang = tooling::stdlib::Lang::CXX;
828-
if (LangOpts.C11)
829-
Lang = tooling::stdlib::Lang::C;
830-
else if(!LangOpts.CPlusPlus)
831-
return "";
832-
833-
if (S->Scope == "std::" && S->Name == "move") {
834-
if (!S->Signature.contains(','))
835-
return "<utility>";
836-
return "<algorithm>";
837-
}
838-
839-
if (auto StdSym = tooling::stdlib::Symbol::named(S->Scope, S->Name, Lang))
840-
if (auto Header = StdSym->header())
841-
return Header->name();
848+
if (LangOpts.C11)
849+
Lang = tooling::stdlib::Lang::C;
850+
else if(!LangOpts.CPlusPlus)
842851
return "";
852+
853+
if (S->Scope == "std::" && S->Name == "move") {
854+
if (!S->Signature.contains(','))
855+
return "<utility>";
856+
return "<algorithm>";
857+
}
858+
859+
if (auto StdSym = tooling::stdlib::Symbol::named(S->Scope, S->Name, Lang))
860+
if (auto Header = StdSym->header())
861+
return Header->name();
862+
return "";
843863
}
844864

845865
void SymbolCollector::finish() {
@@ -865,13 +885,16 @@ void SymbolCollector::finish() {
865885
}
866886
}
867887
llvm::DenseMap<FileID, bool> FileToContainsImportsOrObjC;
888+
llvm::DenseMap<include_cleaner::Header, std::string> HeaderSpelling;
868889
// Fill in IncludeHeaders.
869890
// We delay this until end of TU so header guards are all resolved.
870-
for (const auto &[SID, FID] : IncludeFiles) {
891+
for (const auto &[SID, OptionalProvider] : SymbolProviders) {
871892
const Symbol *S = Symbols.find(SID);
872893
if (!S)
873894
continue;
895+
assert(IncludeFiles.find(SID) != IncludeFiles.end());
874896

897+
const auto FID = IncludeFiles.at(SID);
875898
// Determine if the FID is #include'd or #import'ed.
876899
Symbol::IncludeDirective Directives = Symbol::Invalid;
877900
auto CollectDirectives = shouldCollectIncludePath(S->SymInfo.Kind);
@@ -891,20 +914,61 @@ void SymbolCollector::finish() {
891914
if (Directives == Symbol::Invalid)
892915
continue;
893916

894-
// FIXME: Use the include-cleaner library instead.
895-
llvm::StringRef IncludeHeader = getStdHeader(S, ASTCtx->getLangOpts());
896-
if (IncludeHeader.empty())
897-
IncludeHeader = HeaderFileURIs->getIncludeHeader(FID);
917+
// Use the include location-based logic for Objective-C symbols.
918+
if (Directives & Symbol::Import) {
919+
llvm::StringRef IncludeHeader = getStdHeader(S, ASTCtx->getLangOpts());
920+
if (IncludeHeader.empty())
921+
IncludeHeader = HeaderFileURIs->getIncludeHeader(FID);
922+
923+
if (!IncludeHeader.empty()) {
924+
auto NewSym = *S;
925+
NewSym.IncludeHeaders.push_back({IncludeHeader, 1, Directives});
926+
Symbols.insert(NewSym);
927+
}
928+
// FIXME: use providers from include-cleaner library once it's polished
929+
// for Objective-C.
930+
continue;
931+
}
932+
933+
assert(Directives == Symbol::Include);
934+
// For #include's, use the providers computed by the include-cleaner
935+
// library.
936+
if (!OptionalProvider)
937+
continue;
938+
const auto &H = *OptionalProvider;
939+
const auto [SpellingIt, Inserted] = HeaderSpelling.try_emplace(H);
940+
if (Inserted) {
941+
auto &SM = ASTCtx->getSourceManager();
942+
if (H.kind() == include_cleaner::Header::Kind::Physical) {
943+
// FIXME: Get rid of this once include-cleaner has support for system
944+
// headers.
945+
if (auto Canonical =
946+
HeaderFileURIs->mapCanonical(H.physical()->getName());
947+
!Canonical.empty())
948+
SpellingIt->second = Canonical;
949+
// For physical files, prefer URIs as spellings might change
950+
// depending on the translation unit.
951+
else if (tooling::isSelfContainedHeader(H.physical(), SM,
952+
PP->getHeaderSearchInfo()))
953+
SpellingIt->second =
954+
HeaderFileURIs->toURI(H.physical()->getLastRef());
955+
} else {
956+
SpellingIt->second = include_cleaner::spellHeader(
957+
{H, PP->getHeaderSearchInfo(),
958+
SM.getFileEntryForID(SM.getMainFileID())});
959+
}
960+
}
898961

899-
if (!IncludeHeader.empty()) {
962+
if (!SpellingIt->second.empty()) {
900963
auto NewSym = *S;
901-
NewSym.IncludeHeaders.push_back({IncludeHeader, 1, Directives});
964+
NewSym.IncludeHeaders.push_back({SpellingIt->second, 1, Directives});
902965
Symbols.insert(NewSym);
903966
}
904967
}
905968

906969
ReferencedSymbols.clear();
907970
IncludeFiles.clear();
971+
SymbolProviders.clear();
908972
FilesWithObjCConstructs.clear();
909973
}
910974

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ class SymbolCollector : public index::IndexDataConsumer {
175175
void setIncludeLocation(const Symbol &S, SourceLocation,
176176
const include_cleaner::Symbol &Sym);
177177

178+
// Providers for Symbol.IncludeHeaders.
179+
// The final spelling is calculated in finish().
180+
llvm::DenseMap<SymbolID, std::optional<include_cleaner::Header>>
181+
SymbolProviders;
178182
// Files which contain ObjC symbols.
179183
// This is finalized and used in finish().
180184
llvm::DenseSet<FileID> FilesWithObjCConstructs;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ TEST(FileIndexTest, IncludeCollected) {
239239
"<the/good/header.h>");
240240
}
241241

242-
TEST(FileIndexTest, DISABLED_IWYUPragmaExport) {
242+
TEST(FileIndexTest, IWYUPragmaExport) {
243243
FileIndex M;
244244

245245
TestTU File;

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,15 @@
88

99
#include "Headers.h"
1010
#include "TestFS.h"
11+
#include "URI.h"
1112
#include "index/IndexAction.h"
1213
#include "index/Serialization.h"
14+
#include "clang/Basic/SourceLocation.h"
15+
#include "clang/Basic/SourceManager.h"
1316
#include "clang/Tooling/Tooling.h"
1417
#include "gmock/gmock.h"
1518
#include "gtest/gtest.h"
19+
#include <string>
1620

1721
namespace clang {
1822
namespace clangd {
@@ -40,6 +44,11 @@ MATCHER(hasSameURI, "") {
4044
return toUri(Path) == URI;
4145
}
4246

47+
MATCHER_P(includeHeader, P, "") {
48+
return (arg.IncludeHeaders.size() == 1) &&
49+
(arg.IncludeHeaders.begin()->IncludeHeader == P);
50+
}
51+
4352
::testing::Matcher<const IncludeGraphNode &>
4453
includesAre(const std::vector<std::string> &Includes) {
4554
return ::testing::Field(&IncludeGraphNode::DirectIncludes,
@@ -312,6 +321,26 @@ TEST_F(IndexActionTest, SkipNestedSymbols) {
312321
EXPECT_THAT(*IndexFile.Symbols, testing::Contains(hasName("Bar")));
313322
EXPECT_THAT(*IndexFile.Symbols, Not(testing::Contains(hasName("Baz"))));
314323
}
324+
325+
TEST_F(IndexActionTest, SymbolFromCC) {
326+
std::string MainFilePath = testPath("main.cpp");
327+
addFile(MainFilePath, R"cpp(
328+
#include "main.h"
329+
void foo() {}
330+
)cpp");
331+
addFile(testPath("main.h"), R"cpp(
332+
#pragma once
333+
void foo();
334+
)cpp");
335+
Opts.FileFilter = [](const SourceManager &SM, FileID F) {
336+
return !SM.getFileEntryRefForID(F)->getName().endswith("main.h");
337+
};
338+
IndexFileIn IndexFile = runIndexingAction(MainFilePath, {"-std=c++14"});
339+
EXPECT_THAT(*IndexFile.Symbols,
340+
UnorderedElementsAre(AllOf(
341+
hasName("foo"),
342+
includeHeader(URI::create(testPath("main.h")).toString()))));
343+
}
315344
} // namespace
316345
} // namespace clangd
317346
} // namespace clang

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

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "index/SymbolCollector.h"
1515
#include "clang/Basic/FileManager.h"
1616
#include "clang/Basic/FileSystemOptions.h"
17+
#include "clang/Basic/SourceLocation.h"
1718
#include "clang/Frontend/CompilerInstance.h"
1819
#include "clang/Index/IndexingAction.h"
1920
#include "clang/Index/IndexingOptions.h"
@@ -1554,6 +1555,8 @@ TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
15541555
// Move overloads have special handling.
15551556
template <typename _T> T&& move(_T&& __value);
15561557
template <typename _I, typename _O> _O move(_I, _I, _O);
1558+
template <typename _T, typename _O, typename _I> _O move(
1559+
_T&&, _O, _O, _I);
15571560
}
15581561
)cpp",
15591562
/*Main=*/"");
@@ -1565,7 +1568,8 @@ TEST_F(SymbolCollectorTest, CanonicalSTLHeader) {
15651568
includeHeader("<string>")),
15661569
// Parameter names are demangled.
15671570
AllOf(labeled("move(T &&value)"), includeHeader("<utility>")),
1568-
AllOf(labeled("move(I, I, O)"), includeHeader("<algorithm>"))));
1571+
AllOf(labeled("move(I, I, O)"), includeHeader("<algorithm>")),
1572+
AllOf(labeled("move(T &&, O, O, I)"), includeHeader("<algorithm>"))));
15691573
}
15701574

15711575
TEST_F(SymbolCollectorTest, IWYUPragma) {
@@ -1592,7 +1596,7 @@ TEST_F(SymbolCollectorTest, IWYUPragmaWithDoubleQuotes) {
15921596
includeHeader("\"the/good/header.h\""))));
15931597
}
15941598

1595-
TEST_F(SymbolCollectorTest, DISABLED_IWYUPragmaExport) {
1599+
TEST_F(SymbolCollectorTest, IWYUPragmaExport) {
15961600
CollectorOpts.CollectIncludePath = true;
15971601
const std::string Header = R"cpp(#pragma once
15981602
#include "exporter.h"
@@ -1978,6 +1982,24 @@ TEST_F(SymbolCollectorTest, Concepts) {
19781982
qName("A"), hasKind(clang::index::SymbolKind::Concept))));
19791983
}
19801984

1985+
TEST_F(SymbolCollectorTest, IncludeHeaderForwardDecls) {
1986+
CollectorOpts.CollectIncludePath = true;
1987+
const std::string Header = R"cpp(#pragma once
1988+
struct Foo;
1989+
#include "full.h"
1990+
)cpp";
1991+
auto FullFile = testPath("full.h");
1992+
InMemoryFileSystem->addFile(FullFile, 0,
1993+
llvm::MemoryBuffer::getMemBuffer(R"cpp(
1994+
#pragma once
1995+
struct Foo {};)cpp"));
1996+
runSymbolCollector(Header, /*Main=*/"",
1997+
/*ExtraArgs=*/{"-I", testRoot()});
1998+
EXPECT_THAT(Symbols, UnorderedElementsAre(AllOf(
1999+
qName("Foo"),
2000+
includeHeader(URI::create(FullFile).toString()))))
2001+
<< *Symbols.begin();
2002+
}
19812003
} // namespace
19822004
} // namespace clangd
19832005
} // namespace clang

0 commit comments

Comments
 (0)