Skip to content

[clang] Reset FileID based diag state mappings #143695

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
Jun 12, 2025
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
13 changes: 10 additions & 3 deletions clang/include/clang/Basic/Diagnostic.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,10 +420,13 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
bool empty() const { return Files.empty(); }

/// Clear out this map.
void clear() {
void clear(bool Soft) {
// Just clear the cache when in soft mode.
Files.clear();
FirstDiagState = CurDiagState = nullptr;
CurDiagStateLoc = SourceLocation();
if (!Soft) {
FirstDiagState = CurDiagState = nullptr;
CurDiagStateLoc = SourceLocation();
}
}

/// Produce a debugging dump of the diagnostic state.
Expand Down Expand Up @@ -918,6 +921,10 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
/// Reset the state of the diagnostic object to its initial configuration.
/// \param[in] soft - if true, doesn't reset the diagnostic mappings and state
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.
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Basic/Diagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ bool DiagnosticsEngine::popMappings(SourceLocation Loc) {
return true;
}

void DiagnosticsEngine::ResetPragmas() { DiagStatesByLoc.clear(/*Soft=*/true); }

void DiagnosticsEngine::Reset(bool soft /*=false*/) {
ErrorOccurred = false;
UncompilableErrorOccurred = false;
Expand All @@ -135,7 +137,7 @@ void DiagnosticsEngine::Reset(bool soft /*=false*/) {
if (!soft) {
// Clear state related to #pragma diagnostic.
DiagStates.clear();
DiagStatesByLoc.clear();
DiagStatesByLoc.clear(false);
DiagStateOnPushStack.clear();

// Create a DiagState and DiagStatePoint representing diagnostic changes
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Basic/SourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ void SourceManager::clearIDTables() {
NextLocalOffset = 0;
CurrentLoadedOffset = MaxLoadedOffset;
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) {
Expand Down
51 changes: 51 additions & 0 deletions clang/unittests/Frontend/CompilerInstanceTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Basic/FileManager.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "gtest/gtest.h"
Expand Down Expand Up @@ -97,4 +100,52 @@ TEST(CompilerInstance, AllowDiagnosticLogWithUnownedDiagnosticConsumer) {
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
Loading