https://github.com/turgu1 created
https://github.com/llvm/llvm-project/pull/186686
This is a new indentation style for PP directives that align with the
surrounding C++ indentation level. Code inside a PP conditional block is
indented one further level relative to the directive that guards it.
Here is an example of the result of using this new PP indentation style:
``` C++
// ============================================================
// SECTION 1: Top-level code (C-depth 0)
// ============================================================
// --- 1.1 PP-depth 1 (single #if at top level) ---
#if LEVEL1
int a1;
#elif LEVEL1_ALT
int a2;
#else
int a3;
#endif
// --- 1.2 PP-depth 2 (nested #if at top level) ---
#if OUTER
#if INNER
int b1;
#elif INNER_ALT
int b2;
#else
int b3;
#endif
#else
#ifdef OUTER_ALT
int b4;
#endif
#endif
// --- 1.3 PP-depth 3 (triple nested at top level) ---
#ifndef GUARD1
#ifdef GUARD2
#if GUARD3
int c1;
#endif
#endif
#endif
// --- 1.4 #define and #include at top level ---
#define TOP_MACRO 42
#include <cstdint>
#if HAS_OPTIONAL
#include <optional>
#else
#include <cassert>
#endif
// ============================================================
// SECTION 2: Function body (C-depth 1)
// ============================================================
void section2() {
// --- 2.1 PP-depth 1 inside function ---
int x = 0;
#if COND_A
x = 1;
#elif COND_B
x = 2;
#else
x = 3;
#endif
// --- 2.2 PP-depth 2 inside function ---
#ifdef OUTER_FN
#if INNER_FN
x = 10;
#elif INNER_FN_ALT
x = 11;
#else
x = 12;
#endif
#endif
// --- 2.3 PP-depth 3 inside function ---
#if L1
#ifdef L2
#ifndef L3
x = 100;
#else
x = 101;
#endif
#endif
#endif
// --- 2.4 #define inside function ---
#define LOCAL_MACRO(x) ((x) * 2)
#if HAS_LOCAL
#define LOCAL_ALT(x) ((x) + 1)
#endif
}
// ============================================================
// SECTION 3: Nested block (C-depth 2)
// ============================================================
void section3() {
if (true) {
int y = 0;
// --- 3.1 PP-depth 1 inside if-body ---
#if COND_C
y = 1;
#else
y = 2;
#endif
// --- 3.2 PP-depth 2 inside if-body ---
#if OUTER_IF
#if INNER_IF
y = 10;
#endif
#endif
// --- 3.3 PP-depth 3 inside if-body ---
#ifdef L1_IF
#if L2_IF
#ifndef L3_IF
y = 100;
#elif L3_ALT
y = 101;
#else
y = 102;
#endif
#endif
#endif
// --- 3.4 #include inside conditional inside nested block ---
#if PLATFORM_A
#include "platform_a.h"
#elif PLATFORM_B
#include "platform_b.h"
#endif
}
}
// ============================================================
// SECTION 4: Doubly nested block (C-depth 3)
// ============================================================
void section4() {
for (int i = 0; i < 10; ++i) {
while (cond) {
int z = 0;
// --- 4.1 PP-depth 1 inside doubly nested block ---
#if COND_D
z = i;
#endif
// --- 4.2 PP-depth 2 inside doubly nested block ---
#ifndef OUTER_DEEP
#if INNER_DEEP
z = i * 2;
#endif
#endif
// --- 4.3 PP-depth 3 inside doubly nested block ---
#if D1
#ifdef D2
#if D3
z = i * 3;
#elif D3_ALT
z = i * 4;
#endif
#endif
#endif
}
}
}
// ============================================================
// SECTION 5: struct / class bodies (C-depth 1)
// ============================================================
// --- 5.1 struct with PP-depth 1 ---
struct Vec2 {
float x;
float y;
#if HAVE_OPERATORS
Vec2 operator+(const Vec2 &o) const;
#endif
};
// --- 5.2 struct with PP-depth 2 ---
struct Vec3 {
float x;
float y;
#if THREE_D
float z;
#ifdef HAVE_W
float w;
#endif
#endif
};
// --- 5.3 struct with PP-depth 3 ---
struct Config {
int mode;
#if PLATFORM_LINUX
#if ARCH_X86
#if BITS_64
long long addr;
#else
int addr;
#endif
#endif
#elif PLATFORM_WIN
void *handle;
#endif
};
// --- 5.4 class with access specifiers and PP-depth 1 ---
class MyDevice {
public:
#if HAS_VIRTUAL
virtual void init();
#endif
void reset();
protected:
#if HAS_IRQ
void handleIRQ();
#endif
private:
int state_;
#if HAS_DMA
uint8_t *dmaBuffer_;
#endif
};
// --- 5.5 class with PP-depth 2 ---
class Platform {
public:
#if PLATFORM_A
#if BOARD_V2
bool initV2();
#else
bool initV1();
#endif
#else
bool initGeneric();
#endif
};
// --- 5.6 class with PP-depth 3 ---
class Driver {
public:
#if BUS_I2C
#if SPEED_FAST
#if STRETCH
bool initFastStretch();
#else
bool initFast();
#endif
#else
bool initSlow();
#endif
#elif BUS_SPI
bool initSPI();
#endif
};
// ============================================================
// SECTION 6: enum class bodies (C-depth 1)
// ============================================================
// --- 6.1 enum with PP-depth 1 ---
enum class Color {
red, green, blue,
#if HAS_ALPHA
alpha,
#endif
count
};
// --- 6.2 enum with PP-depth 2 ---
enum class Feature {
none,
#if PLATFORM_FULL
wifi,
#ifdef HAS_BLE
ble,
#endif
#endif
basic
};
// --- 6.3 enum with PP-depth 3 ---
enum class Bus {
none,
#if HAS_I2C
#if HAS_I2C_FAST
#if HAS_I2C_STRETCH
i2c_fast_stretch,
#endif
i2c_fast,
#endif
i2c_slow,
#endif
#if HAS_SPI
spi,
#endif
unknown
};
// ============================================================
// SECTION 7: Mixed — nested C++ blocks with PP at every level
// ============================================================
namespace ns {
#if NS_ENABLED
class Outer {
public:
#if OUTER_ENABLED
class Inner {
public:
#if INNER_ENABLED
void method() {
#if COND_METHOD
doSomething();
#endif
}
#endif
};
#endif
};
#endif // NS_ENABLED
} // namespace ns
// ============================================================
// SECTION 8: Lambdas and closures (C-depth varies)
// ============================================================
void section8() {
auto fn = [&]() {
#if USE_FAST_PATH
fast();
#else
slow();
#endif
};
auto fn2 = [&]() {
if (flag) {
#ifdef FEATURE_X
#if FEATURE_X_V2
doV2();
#else
doV1();
#endif
#endif
}
};
}
// ============================================================
// SECTION 9: Switch statements (C-depth 2)
// ============================================================
void section9(int v) {
switch (v) {
case 0:
#if CASE0_SPECIAL
special0();
#else
default0();
#endif
break;
case 1:
#if CASE1_A
#if CASE1_B
doAB();
#else
doA();
#endif
#endif
break;
default:
break;
}
}
// ============================================================
// SECTION 10: Template / namespace combinations
// ============================================================
namespace outer {
namespace inner {
#if TMPL_ENABLED
template <typename T> class Tmpl {
public:
#if HAS_SPECIAL
void special() {
#if SPECIAL_CASE
T::doSpecial();
#endif
}
#endif
T value_;
};
#endif
} // namespace inner
} // namespace outer
```
>From c25fdac05571a44bb9c7e7ec297f7d50afe549c6 Mon Sep 17 00:00:00 2001
From: Guy Turcotte <[email protected]>
Date: Sat, 14 Mar 2026 21:07:36 -0400
Subject: [PATCH 1/2] A new IndentPPDirectives style: BeforeHashWithCode
---
clang/include/clang/Format/Format.h | 13 ++++
clang/lib/Format/Format.cpp | 1 +
clang/lib/Format/FormatTokenSource.h | 5 +-
clang/lib/Format/UnwrappedLineFormatter.cpp | 13 +++-
clang/lib/Format/UnwrappedLineFormatter.h | 3 +-
clang/lib/Format/UnwrappedLineParser.cpp | 68 +++++++++++++++++----
6 files changed, 86 insertions(+), 17 deletions(-)
diff --git a/clang/include/clang/Format/Format.h
b/clang/include/clang/Format/Format.h
index aea18a836328f..cb25b3a82cd5c 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3247,6 +3247,19 @@ struct FormatStyle {
/// #endif
/// \endcode
PPDIS_BeforeHash,
+ /// Indents directives before the hash with the current code indentation
+ /// level.
+ /// \code
+ /// if (foo)
+ /// {
+ /// #if FOO
+ /// #if BAR
+ /// #include <foo>
+ /// #endif
+ /// #endif
+ /// }
+ /// \endcode
+ PPDIS_BeforeHashWithCode,
/// Leaves indentation of directives as-is.
/// \note
/// Ignores ``PPIndentWidth``.
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index b98a9086811cd..563c9ba03adf2 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -622,6 +622,7 @@ struct
ScalarEnumerationTraits<FormatStyle::PPDirectiveIndentStyle> {
IO.enumCase(Value, "None", FormatStyle::PPDIS_None);
IO.enumCase(Value, "AfterHash", FormatStyle::PPDIS_AfterHash);
IO.enumCase(Value, "BeforeHash", FormatStyle::PPDIS_BeforeHash);
+ IO.enumCase(Value, "BeforeHashWithCode",
FormatStyle::PPDIS_BeforeHashWithCode);
IO.enumCase(Value, "Leave", FormatStyle::PPDIS_Leave);
}
};
diff --git a/clang/lib/Format/FormatTokenSource.h
b/clang/lib/Format/FormatTokenSource.h
index 8f00e5f4582c6..7ab49542ef099 100644
--- a/clang/lib/Format/FormatTokenSource.h
+++ b/clang/lib/Format/FormatTokenSource.h
@@ -191,14 +191,15 @@ class IndexedTokenSource : public FormatTokenSource {
class ScopedMacroState : public FormatTokenSource {
public:
ScopedMacroState(UnwrappedLine &Line, FormatTokenSource *&TokenSource,
- FormatToken *&ResetToken)
+ FormatToken *&ResetToken, bool PreserveLevel = false)
: Line(Line), TokenSource(TokenSource), ResetToken(ResetToken),
PreviousLineLevel(Line.Level), PreviousTokenSource(TokenSource),
Token(nullptr), PreviousToken(nullptr) {
FakeEOF.Tok.startToken();
FakeEOF.Tok.setKind(tok::eof);
TokenSource = this;
- Line.Level = 0;
+ if (!PreserveLevel)
+ Line.Level = 0;
Line.InPPDirective = true;
// InMacroBody gets set after the `#define x` part.
}
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp
b/clang/lib/Format/UnwrappedLineFormatter.cpp
index 74c0f4bf75721..f3b1510e3f687 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -68,6 +68,12 @@ class LevelIndentTracker {
? (Line.Level - Line.PPLevel) * Style.IndentWidth +
AdditionalIndent
: Line.First->OriginalColumn;
+ } else if (Style.IndentPPDirectives ==
FormatStyle::PPDIS_BeforeHashWithCode &&
+ Line.InPPDirective && !Line.InMacroBody) {
+ // For BeforeHashWithCode, PP directives are indented at the surrounding
+ // code level, using the same indentation as regular code (not
PPIndentWidth).
+ Indent = getIndent(Line.Level);
+ Indent += AdditionalIndent;
} else if (Style.IndentPPDirectives != FormatStyle::PPDIS_None &&
(Line.InPPDirective ||
(Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHash &&
@@ -1467,7 +1473,8 @@ unsigned UnwrappedLineFormatter::format(
if (!DryRun) {
bool LastLine = TheLine.First->is(tok::eof);
formatFirstToken(TheLine, PreviousLine, PrevPrevLine, Lines, Indent,
- LastLine ? LastStartColumn : NextStartColumn +
Indent);
+ LastLine ? LastStartColumn : NextStartColumn + Indent,
+ 0);
}
NextLine = Joiner.getNextMergedLine(DryRun, IndentTracker);
@@ -1516,7 +1523,7 @@ unsigned UnwrappedLineFormatter::format(
if (ReformatLeadingWhitespace) {
formatFirstToken(TheLine, PreviousLine, PrevPrevLine, Lines,
TheLine.First->OriginalColumn,
- TheLine.First->OriginalColumn);
+ TheLine.First->OriginalColumn, 0);
} else {
Whitespaces->addUntouchableToken(*TheLine.First,
TheLine.InPPDirective);
@@ -1649,7 +1656,7 @@ void UnwrappedLineFormatter::formatFirstToken(
const AnnotatedLine &Line, const AnnotatedLine *PreviousLine,
const AnnotatedLine *PrevPrevLine,
const SmallVectorImpl<AnnotatedLine *> &Lines, unsigned Indent,
- unsigned NewlineIndent) {
+ unsigned NewlineIndent, unsigned PPNestingLevel) {
FormatToken &RootToken = *Line.First;
if (RootToken.is(tok::eof)) {
unsigned Newlines = std::min(
diff --git a/clang/lib/Format/UnwrappedLineFormatter.h
b/clang/lib/Format/UnwrappedLineFormatter.h
index 9b8acf427a2a0..17889d03644b6 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.h
+++ b/clang/lib/Format/UnwrappedLineFormatter.h
@@ -47,7 +47,8 @@ class UnwrappedLineFormatter {
const AnnotatedLine *PreviousLine,
const AnnotatedLine *PrevPrevLine,
const SmallVectorImpl<AnnotatedLine *> &Lines,
- unsigned Indent, unsigned NewlineIndent);
+ unsigned Indent, unsigned NewlineIndent,
+ unsigned PPNestingLevel = 0);
/// Returns the column limit for a line, taking into account whether we
/// need an escaped newline due to a continued preprocessor directive.
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp
b/clang/lib/Format/UnwrappedLineParser.cpp
index ddf584c6ed818..c3ad21cf5291e 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -998,7 +998,12 @@ void UnwrappedLineParser::parseChildBlock() {
void UnwrappedLineParser::parsePPDirective() {
assert(FormatTok->is(tok::hash) && "'#' expected");
- ScopedMacroState MacroState(*Line, Tokens, FormatTok);
+ // For BeforeHashWithCode, PP directives are indented at the surrounding code
+ // level. Preserving Line->Level allows ScopedMacroState to keep the code
+ // context level instead of resetting it to 0.
+ const bool PreserveLevel =
+ Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHashWithCode;
+ ScopedMacroState MacroState(*Line, Tokens, FormatTok, PreserveLevel);
nextToken();
@@ -4639,7 +4644,27 @@ void UnwrappedLineParser::addUnwrappedLine(LineLevel
AdjustLevel) {
// At the top level we only get here when no unexpansion is going on, or
// when conditional formatting led to unfinished macro reconstructions.
assert(!Reconstruct || (CurrentLines != &Lines) || !PPStack.empty());
+ // For BeforeHashWithCode, code lines inside PP conditional blocks must be
+ // indented by the PP nesting depth so that code appears more indented than
+ // its enclosing PP directive (mirroring how C++ block content is indented
+ // relative to the opening brace). PP directive lines already have
+ // PPBranchLevel+1 added in parsePPUnknown; code lines need the same
+ // treatment. We temporarily adjust Level here and restore it afterwards so
+ // that the next line still starts from the correct C++ brace level.
+ // For BeforeHashWithCode, code lines inside PP blocks need their level
+ // raised to match the enclosing PP directive's nesting depth. Using
+ // Line->PPLevel (set at first-token push time) instead of the current
+ // PPBranchLevel, which may have been altered by later PP directives that
+ // readToken processed after the code tokens but before addUnwrappedLine.
+ const bool BWHCCodeLine =
+ Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHashWithCode &&
+ !Line->InPPDirective && Line->PPLevel > 0;
+ const unsigned PPAdj = BWHCCodeLine ? Line->PPLevel : 0;
+ if (BWHCCodeLine)
+ Line->Level += PPAdj;
CurrentLines->push_back(std::move(*Line));
+ if (BWHCCodeLine)
+ Line->Level -= PPAdj;
}
Line->Tokens.clear();
Line->MatchingOpeningBlockLineIndex = UnwrappedLine::kInvalidIndex;
@@ -4926,16 +4951,28 @@ void UnwrappedLineParser::readToken(int
LevelDifference) {
// directives only after that unwrapped line was finished later.
bool SwitchToPreprocessorLines = !Line->Tokens.empty();
ScopedLineState BlockState(*this, SwitchToPreprocessorLines);
- assert((LevelDifference >= 0 ||
- static_cast<unsigned>(-LevelDifference) <= Line->Level) &&
- "LevelDifference makes Line->Level negative");
- Line->Level += LevelDifference;
- // Comments stored before the preprocessor directive need to be output
- // before the preprocessor directive, at the same level as the
- // preprocessor directive, as we consider them to apply to the directive.
- if (Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHash &&
- PPBranchLevel > 0) {
- Line->Level += PPBranchLevel;
+ // For BeforeHashWithCode, the PP directive is indented at the
surrounding
+ // code level. Apply LevelDifference to get the correct code context
level
+ // (e.g. leaving a block), but do NOT apply PPBranchLevel since PP
+ // directives should align with the code rather than nesting PP levels.
+ if (Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHashWithCode) {
+ assert((LevelDifference >= 0 ||
+ static_cast<unsigned>(-LevelDifference) <= Line->Level) &&
+ "LevelDifference makes Line->Level negative");
+ Line->Level += LevelDifference;
+ } else {
+ assert((LevelDifference >= 0 ||
+ static_cast<unsigned>(-LevelDifference) <= Line->Level) &&
+ "LevelDifference makes Line->Level negative");
+ Line->Level += LevelDifference;
+ // Comments stored before the preprocessor directive need to be output
+ // before the preprocessor directive, at the same level as the
+ // preprocessor directive, as we consider them to apply to the
+ // directive.
+ if (Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHash &&
+ PPBranchLevel > 0) {
+ Line->Level += PPBranchLevel;
+ }
}
assert(Line->Level >= Line->UnbracedBodyLevel);
Line->Level -= Line->UnbracedBodyLevel;
@@ -5111,6 +5148,15 @@ UnwrappedLineParser::parseMacroCall() {
}
void UnwrappedLineParser::pushToken(FormatToken *Tok) {
+ // For BeforeHashWithCode style, record the actual PP nesting depth when the
+ // first token of a code (non-PP) line is pushed. This captures the true PP
+ // context at line-start time, before readToken may subsequently process
+ // more PP directives and change PPBranchLevel before addUnwrappedLine.
+ if (Line->Tokens.empty() && !Line->InPPDirective &&
+ Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHashWithCode) {
+ Line->PPLevel =
+ PPBranchLevel >= 0 ? static_cast<unsigned>(PPBranchLevel + 1) : 0;
+ }
Line->Tokens.push_back(UnwrappedLineNode(Tok));
if (AtEndOfPPLine) {
auto &Tok = *Line->Tokens.back().Tok;
>From 43a5fe4b85a2c30096722ea92d683fa75df54b2f Mon Sep 17 00:00:00 2001
From: Guy Turcotte <[email protected]>
Date: Sun, 15 Mar 2026 13:23:36 -0400
Subject: [PATCH 2/2] Some correction
---
clang/lib/Format/UnwrappedLineFormatter.cpp | 6 +-
clang/lib/Format/UnwrappedLineParser.cpp | 67 ++++++++++++++++++++-
2 files changed, 68 insertions(+), 5 deletions(-)
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp
b/clang/lib/Format/UnwrappedLineFormatter.cpp
index f3b1510e3f687..ad55a9ee678ea 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -68,10 +68,12 @@ class LevelIndentTracker {
? (Line.Level - Line.PPLevel) * Style.IndentWidth +
AdditionalIndent
: Line.First->OriginalColumn;
- } else if (Style.IndentPPDirectives ==
FormatStyle::PPDIS_BeforeHashWithCode &&
+ } else if (Style.IndentPPDirectives ==
+ FormatStyle::PPDIS_BeforeHashWithCode &&
Line.InPPDirective && !Line.InMacroBody) {
// For BeforeHashWithCode, PP directives are indented at the surrounding
- // code level, using the same indentation as regular code (not
PPIndentWidth).
+ // code level, using the same indentation as regular code (not
+ // PPIndentWidth).
Indent = getIndent(Line.Level);
Indent += AdditionalIndent;
} else if (Style.IndentPPDirectives != FormatStyle::PPDIS_None &&
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp
b/clang/lib/Format/UnwrappedLineParser.cpp
index c3ad21cf5291e..34aa92b31f79b 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -1237,6 +1237,18 @@ void UnwrappedLineParser::parsePPUnknown() {
nextToken();
if (Style.IndentPPDirectives != FormatStyle::PPDIS_None)
Line->Level += PPBranchLevel + 1;
+ // For BeforeHashWithCode, PP directives inside unreachable branches must
+ // not be emitted: in multi-pass formatting the surrounding C++ braces may
+ // have been skipped (PP_Unreachable code is not parsed), leaving
+ // Line->Level too low. The resulting incorrect replacement would conflict
+ // with the correct one produced by the reachable pass, causing an
+ // "overlapping replacement" error and an empty output. Simply discard the
+ // accumulated tokens so the reachable pass wins.
+ if (Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHashWithCode &&
+ !PPStack.empty() && PPStack.back().Kind == PP_Unreachable) {
+ Line->Tokens.clear();
+ return;
+ }
addUnwrappedLine();
}
@@ -2524,11 +2536,30 @@ bool UnwrappedLineParser::parseBracedList(bool
IsAngleBracket, bool IsEnum) {
parseChildBlock();
}
}
+ // For BeforeHashWithCode enum bodies: whenever readToken just processed a
+ // PP directive and returned the first post-PP token (AtEndOfPPLine=true),
+ // flush the accumulated pre-PP body tokens as their own UnwrappedLine.
+ // This gives each PP-separated segment its own line so the BWHCCodeLine
+ // level-boost in addUnwrappedLine can apply the correct indentation.
+ if (IsEnum && !IsAngleBracket &&
+ Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHashWithCode &&
+ Style.AllowShortEnumsOnASingleLine && AtEndOfPPLine &&
+ !Line->Tokens.empty()) {
+ addUnwrappedLine();
+ }
if (FormatTok->is(IsAngleBracket ? tok::greater : tok::r_brace)) {
if (IsEnum) {
FormatTok->setBlockKind(BK_Block);
- if (!Style.AllowShortEnumsOnASingleLine)
+ if (!Style.AllowShortEnumsOnASingleLine) {
+ addUnwrappedLine();
+ } else if (Style.IndentPPDirectives ==
+ FormatStyle::PPDIS_BeforeHashWithCode &&
+ !Line->Tokens.empty()) {
+ // For BeforeHashWithCode, flush any remaining enum body tokens
+ // before the closing brace so they get their own UnwrappedLine
+ // with the correct indentation level.
addUnwrappedLine();
+ }
}
nextToken();
return !HasError;
@@ -3885,10 +3916,20 @@ bool UnwrappedLineParser::parseEnum() {
if (!Style.AllowShortEnumsOnASingleLine) {
addUnwrappedLine();
Line->Level += 1;
+ } else if (Style.IndentPPDirectives ==
+ FormatStyle::PPDIS_BeforeHashWithCode) {
+ // For BeforeHashWithCode, flush the enum declaration as its own
+ // UnwrappedLine (like AllowShortEnumsOnASingleLine=false does) so that
+ // body tokens start in a fresh line. Each PP-separated segment of the
+ // body can then be emitted at its correct indentation via BWHCCodeLine.
+ addUnwrappedLine();
+ ++Line->Level;
}
bool HasError = !parseBracedList(/*IsAngleBracket=*/false, /*IsEnum=*/true);
if (!Style.AllowShortEnumsOnASingleLine)
Line->Level -= 1;
+ else if (Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHashWithCode)
+ --Line->Level;
if (HasError) {
if (FormatTok->is(tok::semi))
nextToken();
@@ -4950,6 +4991,9 @@ void UnwrappedLineParser::readToken(int LevelDifference) {
// If there is an unfinished unwrapped line, we flush the preprocessor
// directives only after that unwrapped line was finished later.
bool SwitchToPreprocessorLines = !Line->Tokens.empty();
+ // Save CurrentLines before ScopedLineState may switch it to
+ // PreprocessorDirectives, so we can detect child-block contexts later.
+ const auto *OrigCurrentLines = CurrentLines;
ScopedLineState BlockState(*this, SwitchToPreprocessorLines);
// For BeforeHashWithCode, the PP directive is indented at the
surrounding
// code level. Apply LevelDifference to get the correct code context
level
@@ -4960,16 +5004,33 @@ void UnwrappedLineParser::readToken(int
LevelDifference) {
static_cast<unsigned>(-LevelDifference) <= Line->Level) &&
"LevelDifference makes Line->Level negative");
Line->Level += LevelDifference;
+ // When this PP directive is being deferred to PreprocessorDirectives
+ // (SwitchToPreprocessorLines=true), it may be encountered at a deeper
+ // C++ brace nesting than the opening PP directive of the same
+ // conditional block. This happens inside child-block contexts (e.g.
+ // lambda bodies): the opening #if is processed by readToken() before
+ // parseChildBlock()'s ScopedLineState adds +1 to Line->Level, while
+ // the closing #endif is processed after, giving it a Level one higher
+ // than the #if. Detect child-block context by checking
OrigCurrentLines
+ // (saved before ScopedLineState may switch CurrentLines): inside a
+ // child block it points to the parent's token children list rather
than
+ // the top-level Lines vector. Use the level recorded for the first
+ // deferred directive (the opening #if) so that all directives of the
+ // same conditional block share the same C++ level.
+ if (SwitchToPreprocessorLines && !PreprocessorDirectives.empty() &&
+ OrigCurrentLines != &Lines) {
+ Line->Level = PreprocessorDirectives.front().Level;
+ }
} else {
assert((LevelDifference >= 0 ||
static_cast<unsigned>(-LevelDifference) <= Line->Level) &&
- "LevelDifference makes Line->Level negative");
+ "LevelDifference makes Line->Level negative");
Line->Level += LevelDifference;
// Comments stored before the preprocessor directive need to be output
// before the preprocessor directive, at the same level as the
// preprocessor directive, as we consider them to apply to the
// directive.
- if (Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHash &&
+ if (Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHash &&
PPBranchLevel > 0) {
Line->Level += PPBranchLevel;
}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits