Skip to content

Commit cf048e1

Browse files
authored
[clangd] Perform self-containedness check at EOF (llvm#75965)
Header gurads are not detected until we hit EOF. Make sure we postpone any such detection until then.
1 parent 01c4ecb commit cf048e1

File tree

3 files changed

+59
-18
lines changed

3 files changed

+59
-18
lines changed

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

+21-17
Original file line numberDiff line numberDiff line change
@@ -826,22 +826,8 @@ void SymbolCollector::setIncludeLocation(const Symbol &S, SourceLocation DefLoc,
826826
// We update providers for a symbol with each occurence, as SymbolCollector
827827
// might run while parsing, rather than at the end of a translation unit.
828828
// Hence we see more and more redecls over time.
829-
auto [It, Inserted] = SymbolProviders.try_emplace(S.ID);
830-
auto Headers =
829+
SymbolProviders[S.ID] =
831830
include_cleaner::headersForSymbol(Sym, SM, Opts.PragmaIncludes);
832-
if (Headers.empty())
833-
return;
834-
835-
auto *HeadersIter = Headers.begin();
836-
include_cleaner::Header H = *HeadersIter;
837-
while (HeadersIter != Headers.end() &&
838-
H.kind() == include_cleaner::Header::Physical &&
839-
!tooling::isSelfContainedHeader(H.physical(), SM,
840-
PP->getHeaderSearchInfo())) {
841-
H = *HeadersIter;
842-
HeadersIter++;
843-
}
844-
It->second = H;
845831
}
846832

847833
llvm::StringRef getStdHeader(const Symbol *S, const LangOptions &LangOpts) {
@@ -889,7 +875,7 @@ void SymbolCollector::finish() {
889875
llvm::DenseMap<include_cleaner::Header, std::string> HeaderSpelling;
890876
// Fill in IncludeHeaders.
891877
// We delay this until end of TU so header guards are all resolved.
892-
for (const auto &[SID, OptionalProvider] : SymbolProviders) {
878+
for (const auto &[SID, Providers] : SymbolProviders) {
893879
const Symbol *S = Symbols.find(SID);
894880
if (!S)
895881
continue;
@@ -931,9 +917,27 @@ void SymbolCollector::finish() {
931917
continue;
932918
}
933919

934-
assert(Directives == Symbol::Include);
935920
// For #include's, use the providers computed by the include-cleaner
936921
// library.
922+
assert(Directives == Symbol::Include);
923+
// Ignore providers that are not self-contained, this is especially
924+
// important for symbols defined in the main-file. We want to prefer the
925+
// header, if possible.
926+
// TODO: Limit this to specifically ignore main file, when we're indexing a
927+
// non-header file?
928+
auto SelfContainedProvider =
929+
[this](llvm::ArrayRef<include_cleaner::Header> Providers)
930+
-> std::optional<include_cleaner::Header> {
931+
for (const auto &H : Providers) {
932+
if (H.kind() != include_cleaner::Header::Physical)
933+
return H;
934+
if (tooling::isSelfContainedHeader(H.physical(), PP->getSourceManager(),
935+
PP->getHeaderSearchInfo()))
936+
return H;
937+
}
938+
return std::nullopt;
939+
};
940+
const auto OptionalProvider = SelfContainedProvider(Providers);
937941
if (!OptionalProvider)
938942
continue;
939943
const auto &H = *OptionalProvider;

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

+8-1
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,25 @@
1515
#include "index/Relation.h"
1616
#include "index/Symbol.h"
1717
#include "index/SymbolID.h"
18+
#include "index/SymbolLocation.h"
1819
#include "index/SymbolOrigin.h"
1920
#include "clang/AST/ASTContext.h"
2021
#include "clang/AST/Decl.h"
22+
#include "clang/Basic/LLVM.h"
2123
#include "clang/Basic/SourceLocation.h"
2224
#include "clang/Basic/SourceManager.h"
2325
#include "clang/Index/IndexDataConsumer.h"
2426
#include "clang/Index/IndexSymbol.h"
2527
#include "clang/Sema/CodeCompleteConsumer.h"
2628
#include "llvm/ADT/DenseMap.h"
29+
#include "llvm/ADT/DenseSet.h"
30+
#include "llvm/ADT/SmallVector.h"
31+
#include "llvm/ADT/StringRef.h"
2732
#include <functional>
2833
#include <memory>
2934
#include <optional>
35+
#include <string>
36+
#include <utility>
3037

3138
namespace clang {
3239
namespace clangd {
@@ -177,7 +184,7 @@ class SymbolCollector : public index::IndexDataConsumer {
177184

178185
// Providers for Symbol.IncludeHeaders.
179186
// The final spelling is calculated in finish().
180-
llvm::DenseMap<SymbolID, std::optional<include_cleaner::Header>>
187+
llvm::DenseMap<SymbolID, llvm::SmallVector<include_cleaner::Header>>
181188
SymbolProviders;
182189
// Files which contain ObjC symbols.
183190
// This is finalized and used in finish().

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

+30
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,36 @@ TEST_F(IndexActionTest, SymbolFromCC) {
341341
hasName("foo"),
342342
includeHeader(URI::create(testPath("main.h")).toString()))));
343343
}
344+
345+
TEST_F(IndexActionTest, IncludeHeaderForwardDecls) {
346+
std::string MainFilePath = testPath("main.cpp");
347+
addFile(MainFilePath, R"cpp(
348+
#include "fwd.h"
349+
#include "full.h"
350+
)cpp");
351+
addFile(testPath("fwd.h"), R"cpp(
352+
#ifndef _FWD_H_
353+
#define _FWD_H_
354+
struct Foo;
355+
#endif
356+
)cpp");
357+
addFile(testPath("full.h"), R"cpp(
358+
#ifndef _FULL_H_
359+
#define _FULL_H_
360+
struct Foo {};
361+
362+
// This decl is important, as otherwise we detect control macro for the file,
363+
// before handling definition of Foo.
364+
void other();
365+
#endif
366+
)cpp");
367+
IndexFileIn IndexFile = runIndexingAction(MainFilePath);
368+
EXPECT_THAT(*IndexFile.Symbols,
369+
testing::Contains(AllOf(
370+
hasName("Foo"),
371+
includeHeader(URI::create(testPath("full.h")).toString()))))
372+
<< *IndexFile.Symbols->begin();
373+
}
344374
} // namespace
345375
} // namespace clangd
346376
} // namespace clang

0 commit comments

Comments
 (0)