[clang] Reset FileID based diag state mappings (#143695)
When sharing same compiler instance for multiple compilations, we reset source manager's file id tables in between runs. Diagnostics engine keeps a cache based on these file ids, that became dangling references across compilations. This patch makes sure we reset those whenever sourcemanager is trashing its FileIDs.
This commit is contained in:
@@ -424,10 +424,13 @@ private:
|
|||||||
bool empty() const { return Files.empty(); }
|
bool empty() const { return Files.empty(); }
|
||||||
|
|
||||||
/// Clear out this map.
|
/// Clear out this map.
|
||||||
void clear() {
|
void clear(bool Soft) {
|
||||||
|
// Just clear the cache when in soft mode.
|
||||||
Files.clear();
|
Files.clear();
|
||||||
FirstDiagState = CurDiagState = nullptr;
|
if (!Soft) {
|
||||||
CurDiagStateLoc = SourceLocation();
|
FirstDiagState = CurDiagState = nullptr;
|
||||||
|
CurDiagStateLoc = SourceLocation();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produce a debugging dump of the diagnostic state.
|
/// Produce a debugging dump of the diagnostic state.
|
||||||
@@ -920,6 +923,10 @@ public:
|
|||||||
/// Reset the state of the diagnostic object to its initial configuration.
|
/// Reset the state of the diagnostic object to its initial configuration.
|
||||||
/// \param[in] soft - if true, doesn't reset the diagnostic mappings and state
|
/// \param[in] soft - if true, doesn't reset the diagnostic mappings and state
|
||||||
void Reset(bool soft = false);
|
void Reset(bool soft = false);
|
||||||
|
/// We keep a cache of FileIDs for diagnostics mapped by pragmas. These might
|
||||||
|
/// get invalidated when diagnostics engine is shared across different
|
||||||
|
/// compilations. Provide users with a way to reset that.
|
||||||
|
void ResetPragmas();
|
||||||
|
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
// DiagnosticsEngine classification and reporting interfaces.
|
// DiagnosticsEngine classification and reporting interfaces.
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ bool DiagnosticsEngine::popMappings(SourceLocation Loc) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DiagnosticsEngine::ResetPragmas() { DiagStatesByLoc.clear(/*Soft=*/true); }
|
||||||
|
|
||||||
void DiagnosticsEngine::Reset(bool soft /*=false*/) {
|
void DiagnosticsEngine::Reset(bool soft /*=false*/) {
|
||||||
ErrorOccurred = false;
|
ErrorOccurred = false;
|
||||||
UncompilableErrorOccurred = false;
|
UncompilableErrorOccurred = false;
|
||||||
@@ -135,7 +137,7 @@ void DiagnosticsEngine::Reset(bool soft /*=false*/) {
|
|||||||
if (!soft) {
|
if (!soft) {
|
||||||
// Clear state related to #pragma diagnostic.
|
// Clear state related to #pragma diagnostic.
|
||||||
DiagStates.clear();
|
DiagStates.clear();
|
||||||
DiagStatesByLoc.clear();
|
DiagStatesByLoc.clear(false);
|
||||||
DiagStateOnPushStack.clear();
|
DiagStateOnPushStack.clear();
|
||||||
|
|
||||||
// Create a DiagState and DiagStatePoint representing diagnostic changes
|
// Create a DiagState and DiagStatePoint representing diagnostic changes
|
||||||
|
|||||||
@@ -344,6 +344,9 @@ void SourceManager::clearIDTables() {
|
|||||||
NextLocalOffset = 0;
|
NextLocalOffset = 0;
|
||||||
CurrentLoadedOffset = MaxLoadedOffset;
|
CurrentLoadedOffset = MaxLoadedOffset;
|
||||||
createExpansionLoc(SourceLocation(), SourceLocation(), SourceLocation(), 1);
|
createExpansionLoc(SourceLocation(), SourceLocation(), SourceLocation(), 1);
|
||||||
|
// Diagnostics engine keeps some references to fileids, mostly for dealing
|
||||||
|
// with diagnostic pragmas, make sure they're reset as well.
|
||||||
|
Diag.ResetPragmas();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SourceManager::isMainFile(const FileEntry &SourceFile) {
|
bool SourceManager::isMainFile(const FileEntry &SourceFile) {
|
||||||
|
|||||||
@@ -9,9 +9,12 @@
|
|||||||
#include "clang/Frontend/CompilerInstance.h"
|
#include "clang/Frontend/CompilerInstance.h"
|
||||||
#include "clang/Basic/FileManager.h"
|
#include "clang/Basic/FileManager.h"
|
||||||
#include "clang/Frontend/CompilerInvocation.h"
|
#include "clang/Frontend/CompilerInvocation.h"
|
||||||
|
#include "clang/Frontend/FrontendActions.h"
|
||||||
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
#include "clang/Frontend/TextDiagnosticPrinter.h"
|
||||||
|
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||||
#include "llvm/Support/FileSystem.h"
|
#include "llvm/Support/FileSystem.h"
|
||||||
#include "llvm/Support/Format.h"
|
#include "llvm/Support/Format.h"
|
||||||
|
#include "llvm/Support/MemoryBuffer.h"
|
||||||
#include "llvm/Support/ToolOutputFile.h"
|
#include "llvm/Support/ToolOutputFile.h"
|
||||||
#include "llvm/Support/VirtualFileSystem.h"
|
#include "llvm/Support/VirtualFileSystem.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
@@ -97,4 +100,52 @@ TEST(CompilerInstance, AllowDiagnosticLogWithUnownedDiagnosticConsumer) {
|
|||||||
ASSERT_EQ(DiagnosticOutput, "error: expected no crash\n");
|
ASSERT_EQ(DiagnosticOutput, "error: expected no crash\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(CompilerInstance, MultipleInputsCleansFileIDs) {
|
||||||
|
auto VFS = makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
|
||||||
|
VFS->addFile("a.cc", /*ModificationTime=*/{},
|
||||||
|
MemoryBuffer::getMemBuffer(R"cpp(
|
||||||
|
#include "a.h"
|
||||||
|
)cpp"));
|
||||||
|
// Paddings of `void foo();` in the sources below are "important". We're
|
||||||
|
// testing against source locations from previous compilations colliding.
|
||||||
|
// Hence the `unused` variable in `b.h` needs to be within `#pragma clang
|
||||||
|
// diagnostic` block from `a.h`.
|
||||||
|
VFS->addFile("a.h", /*ModificationTime=*/{}, MemoryBuffer::getMemBuffer(R"cpp(
|
||||||
|
#include "b.h"
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic warning "-Wunused"
|
||||||
|
void foo();
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
)cpp"));
|
||||||
|
VFS->addFile("b.h", /*ModificationTime=*/{}, MemoryBuffer::getMemBuffer(R"cpp(
|
||||||
|
void foo(); void foo(); void foo(); void foo();
|
||||||
|
inline void foo() { int unused = 2; }
|
||||||
|
)cpp"));
|
||||||
|
|
||||||
|
DiagnosticOptions DiagOpts;
|
||||||
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
|
||||||
|
CompilerInstance::createDiagnostics(*VFS, DiagOpts);
|
||||||
|
|
||||||
|
CreateInvocationOptions CIOpts;
|
||||||
|
CIOpts.Diags = Diags;
|
||||||
|
|
||||||
|
const char *Args[] = {"clang", "-xc++", "a.cc"};
|
||||||
|
std::shared_ptr<CompilerInvocation> CInvok =
|
||||||
|
createInvocation(Args, std::move(CIOpts));
|
||||||
|
ASSERT_TRUE(CInvok) << "could not create compiler invocation";
|
||||||
|
|
||||||
|
CompilerInstance Instance(std::move(CInvok));
|
||||||
|
Instance.setDiagnostics(Diags.get());
|
||||||
|
Instance.createFileManager(VFS);
|
||||||
|
|
||||||
|
// Run once for `a.cc` and then for `a.h`. This makes sure we get the same
|
||||||
|
// file ID for `b.h` in the second run as `a.h` from first run.
|
||||||
|
const auto &OrigInputKind = Instance.getFrontendOpts().Inputs[0].getKind();
|
||||||
|
Instance.getFrontendOpts().Inputs.emplace_back("a.h", OrigInputKind);
|
||||||
|
|
||||||
|
SyntaxOnlyAction Act;
|
||||||
|
EXPECT_TRUE(Instance.ExecuteAction(Act)) << "Failed to execute action";
|
||||||
|
EXPECT_FALSE(Diags->hasErrorOccurred());
|
||||||
|
EXPECT_EQ(Diags->getNumWarnings(), 0u);
|
||||||
|
}
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|||||||
Reference in New Issue
Block a user