Skip to content

[clangd] Perform self-containedness check at EOF #75965

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 21 additions & 17 deletions clang-tools-extra/clangd/index/SymbolCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -826,22 +826,8 @@ void SymbolCollector::setIncludeLocation(const Symbol &S, SourceLocation DefLoc,
// We update providers for a symbol with each occurence, as SymbolCollector
// might run while parsing, rather than at the end of a translation unit.
// Hence we see more and more redecls over time.
auto [It, Inserted] = SymbolProviders.try_emplace(S.ID);
auto Headers =
SymbolProviders[S.ID] =
include_cleaner::headersForSymbol(Sym, SM, Opts.PragmaIncludes);
if (Headers.empty())
return;

auto *HeadersIter = Headers.begin();
include_cleaner::Header H = *HeadersIter;
while (HeadersIter != Headers.end() &&
H.kind() == include_cleaner::Header::Physical &&
!tooling::isSelfContainedHeader(H.physical(), SM,
PP->getHeaderSearchInfo())) {
H = *HeadersIter;
HeadersIter++;
}
It->second = H;
}

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

assert(Directives == Symbol::Include);
// For #include's, use the providers computed by the include-cleaner
// library.
assert(Directives == Symbol::Include);
// Ignore providers that are not self-contained, this is especially
// important for symbols defined in the main-file. We want to prefer the
// header, if possible.
// TODO: Limit this to specifically ignore main file, when we're indexing a
// non-header file?
auto SelfContainedProvider =
[this](llvm::ArrayRef<include_cleaner::Header> Providers)
-> std::optional<include_cleaner::Header> {
for (const auto &H : Providers) {
if (H.kind() != include_cleaner::Header::Physical)
return H;
if (tooling::isSelfContainedHeader(H.physical(), PP->getSourceManager(),
PP->getHeaderSearchInfo()))
return H;
}
return std::nullopt;
};
const auto OptionalProvider = SelfContainedProvider(Providers);
if (!OptionalProvider)
continue;
const auto &H = *OptionalProvider;
Expand Down
9 changes: 8 additions & 1 deletion clang-tools-extra/clangd/index/SymbolCollector.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,25 @@
#include "index/Relation.h"
#include "index/Symbol.h"
#include "index/SymbolID.h"
#include "index/SymbolLocation.h"
#include "index/SymbolOrigin.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Index/IndexSymbol.h"
#include "clang/Sema/CodeCompleteConsumer.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <utility>

namespace clang {
namespace clangd {
Expand Down Expand Up @@ -177,7 +184,7 @@ class SymbolCollector : public index::IndexDataConsumer {

// Providers for Symbol.IncludeHeaders.
// The final spelling is calculated in finish().
llvm::DenseMap<SymbolID, std::optional<include_cleaner::Header>>
llvm::DenseMap<SymbolID, llvm::SmallVector<include_cleaner::Header>>
SymbolProviders;
// Files which contain ObjC symbols.
// This is finalized and used in finish().
Expand Down
30 changes: 30 additions & 0 deletions clang-tools-extra/clangd/unittests/IndexActionTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,36 @@ TEST_F(IndexActionTest, SymbolFromCC) {
hasName("foo"),
includeHeader(URI::create(testPath("main.h")).toString()))));
}

TEST_F(IndexActionTest, IncludeHeaderForwardDecls) {
std::string MainFilePath = testPath("main.cpp");
addFile(MainFilePath, R"cpp(
#include "fwd.h"
#include "full.h"
)cpp");
addFile(testPath("fwd.h"), R"cpp(
#ifndef _FWD_H_
#define _FWD_H_
struct Foo;
#endif
)cpp");
addFile(testPath("full.h"), R"cpp(
#ifndef _FULL_H_
#define _FULL_H_
struct Foo {};

// This decl is important, as otherwise we detect control macro for the file,
// before handling definition of Foo.
void other();
#endif
)cpp");
IndexFileIn IndexFile = runIndexingAction(MainFilePath);
EXPECT_THAT(*IndexFile.Symbols,
testing::Contains(AllOf(
hasName("Foo"),
includeHeader(URI::create(testPath("full.h")).toString()))))
<< *IndexFile.Symbols->begin();
}
} // namespace
} // namespace clangd
} // namespace clang