Reapply r359778: [clangd] Fix code completion of macros defined in the preamble region of the main file.

The bad assert has been removed, and updateOutOfDateIdentifier has been guarded.
This reverts commit r359796.

llvm-svn: 359799
This commit is contained in:
Sam McCall 2019-05-02 16:12:36 +00:00
parent 26e095e84f
commit 1b29dec05f
5 changed files with 97 additions and 13 deletions

View File

@ -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,35 @@ 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) {
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 +134,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 +153,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 +168,7 @@ private:
PreambleParsedCallback ParsedCallback;
IncludeStructure Includes;
CanonicalIncludes CanonIncludes;
std::vector<std::string> MainFileMacros;
std::unique_ptr<CommentHandler> IWYUHandler = nullptr;
SourceManager *SourceMgr = nullptr;
};
@ -459,11 +497,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 +582,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);

View File

@ -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;

View File

@ -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,24 @@ 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))
if (II->isOutOfDate())
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 +1078,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 +1098,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));

View File

@ -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"

View File

@ -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) {