[clang] [clang][Lexer] Save and restore MIOpt in `peekNextPPToken()` (PR #180700)

Thorsten Klein via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 10 00:05:35 PST 2026


https://github.com/thorsten-klein created https://github.com/llvm/llvm-project/pull/180700

Fixes issue #180155 which is caused by `peekNextPPToken()` not saving and restoring the MIOpt state, corrupting include guard optimization. 

A test is added which uses C++20 mode and include guards to verify headers are only processed once even when peeking ahead at tokens.

>From 42db0a6bba2c2cab31e01d231bb273e910d348e0 Mon Sep 17 00:00:00 2001
From: Thorsten Klein <thorsten.klein at bshg.com>
Date: Mon, 9 Feb 2026 14:35:47 +0100
Subject: [PATCH] [clang][Lexer] Save and restore MIOpt in peekNextPPToken()

Fixes issue #180155 which is caused by peekNextPPToken() not saving and
restoring the MIOpt state, corrupting include guard optimization.
A test is added which uses C++20 mode and include guards to verify
headers are only processed once even when peeking ahead at tokens.
---
 clang/lib/Lex/Lexer.cpp                       |  2 ++
 .../Preprocessor/peek-next-token-miopt.cpp    | 23 +++++++++++++++++++
 .../test/Preprocessor/peek-next-token-miopt.h | 15 ++++++++++++
 3 files changed, 40 insertions(+)
 create mode 100644 clang/test/Preprocessor/peek-next-token-miopt.cpp
 create mode 100644 clang/test/Preprocessor/peek-next-token-miopt.h

diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp
index 1498657047bd6..2215a2c623bab 100644
--- a/clang/lib/Lex/Lexer.cpp
+++ b/clang/lib/Lex/Lexer.cpp
@@ -3233,6 +3233,7 @@ std::optional<Token> Lexer::peekNextPPToken() {
   bool atStartOfLine = IsAtStartOfLine;
   bool atPhysicalStartOfLine = IsAtPhysicalStartOfLine;
   bool leadingSpace = HasLeadingSpace;
+  MultipleIncludeOpt SavedMIOpt = MIOpt;
 
   Token Tok;
   Lex(Tok);
@@ -3243,6 +3244,7 @@ std::optional<Token> Lexer::peekNextPPToken() {
   HasLeadingSpace = leadingSpace;
   IsAtStartOfLine = atStartOfLine;
   IsAtPhysicalStartOfLine = atPhysicalStartOfLine;
+  MIOpt = SavedMIOpt;
   // Restore the lexer back to non-skipping mode.
   LexingRawMode = false;
 
diff --git a/clang/test/Preprocessor/peek-next-token-miopt.cpp b/clang/test/Preprocessor/peek-next-token-miopt.cpp
new file mode 100644
index 0000000000000..1b6efb33f4038
--- /dev/null
+++ b/clang/test/Preprocessor/peek-next-token-miopt.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -std=c++20 -E %s -verify
+// expected-no-diagnostics
+
+// Test that peekNextPPToken() correctly saves and restores the MIOpt
+// (Multiple Include Optimization) state. This is important when processing
+// C++20 modules where peekNextPPToken() is called to look ahead.
+// Without saving/restoring MIOpt, the include guard detection could be
+// corrupted.
+
+#include "peek-next-token-miopt.h"
+#include "peek-next-token-miopt.h"
+
+// The header should only be processed once due to the include guard.
+// If MIOpt state is corrupted by peekNextPPToken(), this test would fail.
+
+#ifndef TEST_MACRO
+#error "TEST_MACRO should be defined from the header"
+#endif
+
+// Verify the value is 1 (defined once, not redefined)
+#if TEST_MACRO != 1
+#error "TEST_MACRO should be 1"
+#endif
diff --git a/clang/test/Preprocessor/peek-next-token-miopt.h b/clang/test/Preprocessor/peek-next-token-miopt.h
new file mode 100644
index 0000000000000..9460c24b999a2
--- /dev/null
+++ b/clang/test/Preprocessor/peek-next-token-miopt.h
@@ -0,0 +1,15 @@
+// Header file with include guard for peek-next-token-miopt.cpp test
+
+#ifndef PEEK_NEXT_TOKEN_MIOPT_H
+#define PEEK_NEXT_TOKEN_MIOPT_H
+
+// This should only be defined once
+#ifndef TEST_MACRO
+#define TEST_MACRO 1
+#else
+// If the header is included twice, redefine to show the bug
+#undef TEST_MACRO
+#define TEST_MACRO 2
+#endif
+
+#endif // PEEK_NEXT_TOKEN_MIOPT_H



More information about the cfe-commits mailing list