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

Reply via email to