aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam McCall <sam.mccall@gmail.com>2019-05-02 11:06:44 +0000
committerSam McCall <sam.mccall@gmail.com>2019-05-02 11:06:44 +0000
commit318bfc45f291c762f467ce9b9016f6f7ce5af3cc (patch)
treea49c066b8369ab51f5c2e13538ac30ae36555219
parent9a7e547cc1d4d11e6d9ce18fab6c25b3031edce2 (diff)
[clangd] Fix code completion of macros defined in the preamble region of the main file.
Summary: This is a tricky case (we baked the assumption that symbols come from the preamble xor mainfile pretty deeply) and the fix is a bit of a hack: We look at the code to guess the macro names, and deserialize them from the preamble "by hand". Reviewers: ilya-biryukov Subscribers: ioeric, MaskRay, jkorous, arphaman, kadircet, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D60937 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@359778 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--clangd/ClangdUnit.cpp50
-rw-r--r--clangd/ClangdUnit.h5
-rw-r--r--clangd/CodeComplete.cpp33
-rw-r--r--clangd/index/SymbolCollector.cpp1
-rw-r--r--clangd/unittests/CodeCompleteTests.cpp21
5 files changed, 97 insertions, 13 deletions
diff --git a/clangd/ClangdUnit.cpp b/clangd/ClangdUnit.cpp
index 9d0efeca..6de56718 100644
--- a/clangd/ClangdUnit.cpp
+++ b/clangd/ClangdUnit.cpp
@@ -20,6 +20,8 @@
#include "index/Index.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TokenKinds.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
@@ -94,6 +96,36 @@ private:
std::vector<Decl *> TopLevelDecls;
};
+class CollectMainFileMacros : public PPCallbacks {
+public:
+ explicit CollectMainFileMacros(const SourceManager &SM,
+ std::vector<std::string> *Out)
+ : SM(SM), Out(Out) {}
+
+ void FileChanged(SourceLocation Loc, FileChangeReason,
+ SrcMgr::CharacteristicKind, FileID Prev) {
+ InMainFile = SM.isWrittenInMainFile(Loc);
+ }
+
+ void MacroDefined(const Token &MacroName, const MacroDirective *MD) {
+ assert(MacroName.is(tok::identifier));
+ if (InMainFile)
+ MainFileMacros.insert(MacroName.getIdentifierInfo()->getName());
+ }
+
+ void EndOfMainFile() {
+ for (const auto& Entry : MainFileMacros)
+ Out->push_back(Entry.getKey());
+ llvm::sort(*Out);
+ }
+
+ private:
+ const SourceManager &SM;
+ bool InMainFile = true;
+ llvm::StringSet<> MainFileMacros;
+ std::vector<std::string> *Out;
+};
+
class CppFilePreambleCallbacks : public PreambleCallbacks {
public:
CppFilePreambleCallbacks(PathRef File, PreambleParsedCallback ParsedCallback)
@@ -103,6 +135,10 @@ public:
IncludeStructure takeIncludes() { return std::move(Includes); }
+ std::vector<std::string> takeMainFileMacros() {
+ return std::move(MainFileMacros);
+ }
+
CanonicalIncludes takeCanonicalIncludes() { return std::move(CanonIncludes); }
void AfterExecute(CompilerInstance &CI) override {
@@ -118,7 +154,9 @@ public:
std::unique_ptr<PPCallbacks> createPPCallbacks() override {
assert(SourceMgr && "SourceMgr must be set at this point");
- return collectIncludeStructureCallback(*SourceMgr, &Includes);
+ return llvm::make_unique<PPChainedCallbacks>(
+ collectIncludeStructureCallback(*SourceMgr, &Includes),
+ llvm::make_unique<CollectMainFileMacros>(*SourceMgr, &MainFileMacros));
}
CommentHandler *getCommentHandler() override {
@@ -131,6 +169,7 @@ private:
PreambleParsedCallback ParsedCallback;
IncludeStructure Includes;
CanonicalIncludes CanonIncludes;
+ std::vector<std::string> MainFileMacros;
std::unique_ptr<CommentHandler> IWYUHandler = nullptr;
SourceManager *SourceMgr = nullptr;
};
@@ -459,11 +498,13 @@ const CanonicalIncludes &ParsedAST::getCanonicalIncludes() const {
PreambleData::PreambleData(PrecompiledPreamble Preamble,
std::vector<Diag> Diags, IncludeStructure Includes,
+ std::vector<std::string> MainFileMacros,
std::unique_ptr<PreambleFileStatusCache> StatCache,
CanonicalIncludes CanonIncludes)
: Preamble(std::move(Preamble)), Diags(std::move(Diags)),
- Includes(std::move(Includes)), StatCache(std::move(StatCache)),
- CanonIncludes(std::move(CanonIncludes)) {}
+ Includes(std::move(Includes)), MainFileMacros(std::move(MainFileMacros)),
+ StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) {
+}
ParsedAST::ParsedAST(std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<CompilerInstance> Clang,
@@ -542,7 +583,8 @@ buildPreamble(PathRef FileName, CompilerInvocation &CI,
std::vector<Diag> Diags = PreambleDiagnostics.take();
return std::make_shared<PreambleData>(
std::move(*BuiltPreamble), std::move(Diags),
- SerializedDeclsCollector.takeIncludes(), std::move(StatCache),
+ SerializedDeclsCollector.takeIncludes(),
+ SerializedDeclsCollector.takeMainFileMacros(), std::move(StatCache),
SerializedDeclsCollector.takeCanonicalIncludes());
} else {
elog("Could not build a preamble for file {0}", FileName);
diff --git a/clangd/ClangdUnit.h b/clangd/ClangdUnit.h
index 1f4b6803..d5d29981 100644
--- a/clangd/ClangdUnit.h
+++ b/clangd/ClangdUnit.h
@@ -48,6 +48,7 @@ namespace clangd {
struct PreambleData {
PreambleData(PrecompiledPreamble Preamble, std::vector<Diag> Diags,
IncludeStructure Includes,
+ std::vector<std::string> MainFileMacros,
std::unique_ptr<PreambleFileStatusCache> StatCache,
CanonicalIncludes CanonIncludes);
@@ -57,6 +58,10 @@ struct PreambleData {
// Processes like code completions and go-to-definitions will need #include
// information, and their compile action skips preamble range.
IncludeStructure Includes;
+ // Macros defined in the preamble section of the main file.
+ // Users care about headers vs main-file, not preamble vs non-preamble.
+ // These should be treated as main-file entities e.g. for code completion.
+ std::vector<std::string> MainFileMacros;
// Cache of FS operations performed when building the preamble.
// When reusing a preamble, this cache can be consumed to save IO.
std::unique_ptr<PreambleFileStatusCache> StatCache;
diff --git a/clangd/CodeComplete.cpp b/clangd/CodeComplete.cpp
index f8a4a459..87809eb8 100644
--- a/clangd/CodeComplete.cpp
+++ b/clangd/CodeComplete.cpp
@@ -44,6 +44,8 @@
#include "clang/Format/Format.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/ExternalPreprocessorSource.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Sema/CodeCompleteConsumer.h"
#include "clang/Sema/DeclSpec.h"
@@ -1017,6 +1019,23 @@ struct SemaCompleteInput {
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS;
};
+void loadMainFilePreambleMacros(const Preprocessor &PP,
+ const PreambleData &Preamble) {
+ // The ExternalPreprocessorSource has our macros, if we know where to look.
+ // We can read all the macros using PreambleMacros->ReadDefinedMacros(),
+ // but this includes transitively included files, so may deserialize a lot.
+ ExternalPreprocessorSource *PreambleMacros = PP.getExternalSource();
+ // As we have the names of the macros, we can look up their IdentifierInfo
+ // and then use this to load just the macros we want.
+ IdentifierInfoLookup *PreambleIdentifiers =
+ PP.getIdentifierTable().getExternalIdentifierLookup();
+ if (!PreambleIdentifiers || !PreambleMacros)
+ return;
+ for (const auto& MacroName : Preamble.MainFileMacros)
+ if (auto *II = PreambleIdentifiers->get(MacroName))
+ PreambleMacros->updateOutOfDateIdentifier(*II);
+}
+
// Invokes Sema code completion on a file.
// If \p Includes is set, it will be updated based on the compiler invocation.
bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,
@@ -1058,9 +1077,9 @@ bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,
// However, if we're completing *inside* the preamble section of the draft,
// overriding the preamble will break sema completion. Fortunately we can just
// skip all includes in this case; these completions are really simple.
- bool CompletingInPreamble =
- ComputePreambleBounds(*CI->getLangOpts(), ContentsBuffer.get(), 0).Size >
- Input.Offset;
+ PreambleBounds PreambleRegion =
+ ComputePreambleBounds(*CI->getLangOpts(), ContentsBuffer.get(), 0);
+ bool CompletingInPreamble = PreambleRegion.Size > Input.Offset;
// NOTE: we must call BeginSourceFile after prepareCompilerInstance. Otherwise
// the remapped buffers do not get freed.
IgnoreDiagnostics DummyDiagsConsumer;
@@ -1078,6 +1097,14 @@ bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,
Input.FileName);
return false;
}
+ // Macros can be defined within the preamble region of the main file.
+ // They don't fall nicely into our index/Sema dichotomy:
+ // - they're not indexed for completion (they're not available across files)
+ // - but Sema code complete won't see them: as part of the preamble, they're
+ // deserialized only when mentioned.
+ // Force them to be deserialized so SemaCodeComplete sees them.
+ if (Input.Preamble)
+ loadMainFilePreambleMacros(Clang->getPreprocessor(), *Input.Preamble);
if (Includes)
Clang->getPreprocessor().addPPCallbacks(
collectIncludeStructureCallback(Clang->getSourceManager(), Includes));
diff --git a/clangd/index/SymbolCollector.cpp b/clangd/index/SymbolCollector.cpp
index 05e2c5cd..b9f32172 100644
--- a/clangd/index/SymbolCollector.cpp
+++ b/clangd/index/SymbolCollector.cpp
@@ -24,6 +24,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Index/IndexSymbol.h"
+#include "clang/Index/IndexingAction.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/Support/Casting.h"
diff --git a/clangd/unittests/CodeCompleteTests.cpp b/clangd/unittests/CodeCompleteTests.cpp
index 97f9124e..c0ec8dd1 100644
--- a/clangd/unittests/CodeCompleteTests.cpp
+++ b/clangd/unittests/CodeCompleteTests.cpp
@@ -2073,19 +2073,28 @@ TEST(CompletionTest, MergeMacrosFromIndexAndSema) {
UnorderedElementsAre(Named("Clangd_Macro_Test")));
}
-TEST(CompletionTest, NoMacroFromPreambleIfIndexIsSet) {
+TEST(CompletionTest, MacroFromPreamble) {
+ MockFSProvider FS;
+ MockCompilationDatabase CDB;
+ std::string FooHeader = testPath("foo.h");
+ FS.Files[FooHeader] = "#define CLANGD_PREAMBLE_HEADER x\n";
+ IgnoreDiagnostics DiagConsumer;
+ ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
auto Results = completions(
- R"cpp(#define CLANGD_PREAMBLE x
+ R"cpp(#include "foo.h"
+ #define CLANGD_PREAMBLE_MAIN x
int x = 0;
#define CLANGD_MAIN x
void f() { CLANGD_^ }
)cpp",
{func("CLANGD_INDEX")});
- // Index is overriden in code completion options, so the preamble symbol is
- // not seen.
- EXPECT_THAT(Results.Completions, UnorderedElementsAre(Named("CLANGD_MAIN"),
- Named("CLANGD_INDEX")));
+ // We should get results from the main file, including the preamble section.
+ // However no results from included files (the index should cover them).
+ EXPECT_THAT(Results.Completions,
+ UnorderedElementsAre(Named("CLANGD_PREAMBLE_MAIN"),
+ Named("CLANGD_MAIN"),
+ Named("CLANGD_INDEX")));
}
TEST(CompletionTest, DeprecatedResults) {