https://github.com/joaobfernandes0 created 
https://github.com/llvm/llvm-project/pull/214021

clang-format indents preprocessor directives only relative to each other: 
IndentPPDirectives moves them by the #if nesting depth and discards the 
indentation of the code block they sit in, so a #pragma nested in two levels of 
braces is flushed to the first column. ScopedMacroState resets Line.Level to 0 
when a directive starts parsing, which is where that indentation is lost.

D92753 (e9e6e3b34a8e) previously added an IndentPragmas option for this and was 
reverted in 9af03864df74 for breaking #pragma indentation inside #ifdef/#endif. 
It recovered the code level with

  Level - (PPBranchLevel + 1)

on an unsigned Level that already had PPBranchLevel added to it, which 
underflows for a directive at file scope inside a conditional. The underlying 
problem was that a single Level field had to carry both the brace nesting and 
the preprocessor nesting.

UnwrappedLine::PPLevel has since made the preprocessor nesting a separate axis, 
so track the enclosing brace level as a third one. PPScopeLevel is copied from 
the code level in readToken() before any preprocessor nesting is mixed into 
Level, which removes the subtraction and with it the underflow. 
PPScopeLevelStack keeps #else/#elif/#endif aligned with the #if that opened the 
conditional even when the braces in between are unbalanced and the two are in 
different scopes.

The new PPScopeIndent option selects whether that offset applies to no 
directive, to #pragma only, or to all of them. It is added on top of 
IndentPPDirectives and has no effect for AfterHash, which keeps the hash in the 
first column by design, or for Leave.

Fixes #57923
Fixes #174577

From 43beb9ebbc09292b1f0fc9c845b70553ceaf261c Mon Sep 17 00:00:00 2001
From: Joao Batista <[email protected]>
Date: Mon, 3 Aug 2026 20:49:43 -0300
Subject: [PATCH] [clang-format] Add PPScopeIndent to indent directives to
 their scope

clang-format indents preprocessor directives only relative to each other:
IndentPPDirectives moves them by the #if nesting depth and discards the
indentation of the code block they sit in, so a #pragma nested in two
levels of braces is flushed to the first column. ScopedMacroState resets
Line.Level to 0 when a directive starts parsing, which is where that
indentation is lost.

D92753 (e9e6e3b34a8e) previously added an IndentPragmas option for this
and was reverted in 9af03864df74 for breaking #pragma indentation inside
#ifdef/#endif. It recovered the code level with

  Level - (PPBranchLevel + 1)

on an unsigned Level that already had PPBranchLevel added to it, which
underflows for a directive at file scope inside a conditional. The
underlying problem was that a single Level field had to carry both the
brace nesting and the preprocessor nesting.

UnwrappedLine::PPLevel has since made the preprocessor nesting a separate
axis, so track the enclosing brace level as a third one. PPScopeLevel is
copied from the code level in readToken() before any preprocessor nesting
is mixed into Level, which removes the subtraction and with it the
underflow. PPScopeLevelStack keeps #else/#elif/#endif aligned with the #if
that opened the conditional even when the braces in between are
unbalanced and the two are in different scopes.

The new PPScopeIndent option selects whether that offset applies to no
directive, to #pragma only, or to all of them. It is added on top of
IndentPPDirectives and has no effect for AfterHash, which keeps the hash
in the first column by design, or for Leave.

Fixes #57923
Fixes #174577
---
 clang/docs/ClangFormatStyleOptions.rst      |  63 +++++++++
 clang/docs/ReleaseNotes.md                  |   3 +
 clang/include/clang/Format/Format.h         |  58 ++++++++
 clang/lib/Format/Format.cpp                 |  12 ++
 clang/lib/Format/TokenAnnotator.cpp         |   4 +-
 clang/lib/Format/TokenAnnotator.h           |   3 +-
 clang/lib/Format/UnwrappedLineFormatter.cpp |  33 ++++-
 clang/lib/Format/UnwrappedLineParser.cpp    |  19 +++
 clang/lib/Format/UnwrappedLineParser.h      |  16 +++
 clang/unittests/Format/ConfigParseTest.cpp  |   6 +
 clang/unittests/Format/FormatTest.cpp       | 146 ++++++++++++++++++++
 11 files changed, 359 insertions(+), 4 deletions(-)

diff --git a/clang/docs/ClangFormatStyleOptions.rst 
b/clang/docs/ClangFormatStyleOptions.rst
index e8cf2409e6c70..65e21b5e493c2 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -5902,6 +5902,69 @@ the configuration (without a prefix: ``Auto``).
      # define BAR
      #endif
 
+.. _PPScopeIndent:
+
+**PPScopeIndent** (``PPDirectiveScopeIndentStyle``) 
:versionbadge:`clang-format 24` :ref:`¶ <PPScopeIndent>`
+  Whether preprocessor directives should follow the indentation of the scope
+  they appear in.
+
+  This is orthogonal to ``IndentPPDirectives``, which only indents
+  directives relative to each other; the offset selected here is added on
+  top of it, using ``IndentWidth`` rather than ``PPIndentWidth``. It has no
+  effect when ``IndentPPDirectives`` is ``AfterHash``, which keeps the hash
+  in the first column by design, or ``Leave``.
+
+  Only the directives themselves are moved: code guarded by a conditional is
+  not indented by that conditional. The closing ``#endif`` of a conditional
+  is always aligned with the ``#if`` that opened it, even when the braces in
+  between are unbalanced and the two are in different scopes.
+
+  Possible values:
+
+  * ``PPSIS_None`` (in configuration: ``None``)
+    Do not indent preprocessor directives to the scope of the surrounding
+    code.
+
+    .. code-block:: c++
+
+      void f() {
+        if (a) {
+      #pragma omp simd
+          for (int i = 0; i < 4; ++i) {
+          }
+        }
+      }
+
+  * ``PPSIS_Pragmas`` (in configuration: ``Pragmas``)
+    Indent only ``#pragma`` directives to the scope of the surrounding code.
+
+    .. code-block:: c++
+
+      void f() {
+        if (a) {
+          #pragma omp simd
+          for (int i = 0; i < 4; ++i) {
+          }
+        }
+      }
+
+  * ``PPSIS_All`` (in configuration: ``All``)
+    Indent all preprocessor directives to the scope of the surrounding code.
+
+    .. code-block:: c++
+
+      void f() {
+        if (a) {
+          #ifdef SIMD
+          #pragma omp simd
+          #endif
+          for (int i = 0; i < 4; ++i) {
+          }
+        }
+      }
+
+
+
 .. _PackArguments:
 
 **PackArguments** (``PackArgumentsStyle``) :versionbadge:`clang-format 23` 
:ref:`¶ <PackArguments>`
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 321bd7cd24ac0..2f2f22ab24a8c 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -505,6 +505,9 @@ features cannot lower the translation-unit ABI level;
 
 - Add `SpacesInBlockComments` option to control spacing after `/*` and
   before `*/` in ordinary block comments.
+- Add `PPScopeIndent` option to indent preprocessor directives to the scope of
+  the surrounding code, either for `#pragma` only or for all directives, in
+  addition to the indentation given by `IndentPPDirectives`.
 
 ### libclang
 
diff --git a/clang/include/clang/Format/Format.h 
b/clang/include/clang/Format/Format.h
index 540a50047696a..19801e64d0826 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -4509,6 +4509,63 @@ struct FormatStyle {
   /// \version 13
   int PPIndentWidth;
 
+  /// Different ways to indent preprocessor directives relative to the scope of
+  /// the surrounding code.
+  enum PPDirectiveScopeIndentStyle : int8_t {
+    /// Do not indent preprocessor directives to the scope of the surrounding
+    /// code.
+    /// \code
+    ///   void f() {
+    ///     if (a) {
+    ///   #pragma omp simd
+    ///       for (int i = 0; i < 4; ++i) {
+    ///       }
+    ///     }
+    ///   }
+    /// \endcode
+    PPSIS_None,
+    /// Indent only ``#pragma`` directives to the scope of the surrounding 
code.
+    /// \code
+    ///   void f() {
+    ///     if (a) {
+    ///       #pragma omp simd
+    ///       for (int i = 0; i < 4; ++i) {
+    ///       }
+    ///     }
+    ///   }
+    /// \endcode
+    PPSIS_Pragmas,
+    /// Indent all preprocessor directives to the scope of the surrounding 
code.
+    /// \code
+    ///   void f() {
+    ///     if (a) {
+    ///       #ifdef SIMD
+    ///       #pragma omp simd
+    ///       #endif
+    ///       for (int i = 0; i < 4; ++i) {
+    ///       }
+    ///     }
+    ///   }
+    /// \endcode
+    PPSIS_All
+  };
+
+  /// Whether preprocessor directives should follow the indentation of the 
scope
+  /// they appear in.
+  ///
+  /// This is orthogonal to ``IndentPPDirectives``, which only indents
+  /// directives relative to each other; the offset selected here is added on
+  /// top of it, using ``IndentWidth`` rather than ``PPIndentWidth``. It has no
+  /// effect when ``IndentPPDirectives`` is ``AfterHash``, which keeps the hash
+  /// in the first column by design, or ``Leave``.
+  ///
+  /// Only the directives themselves are moved: code guarded by a conditional 
is
+  /// not indented by that conditional. The closing ``#endif`` of a conditional
+  /// is always aligned with the ``#if`` that opened it, even when the braces 
in
+  /// between are unbalanced and the two are in different scopes.
+  /// \version 24
+  PPDirectiveScopeIndentStyle PPScopeIndent;
+
   /// Different specifiers and qualifiers alignment styles.
   enum QualifierAlignmentStyle : int8_t {
     /// Don't change specifiers/qualifiers to either Left or Right alignment
@@ -6224,6 +6281,7 @@ struct FormatStyle {
            PackArguments == R.PackArguments &&
            PackConstructorInitializers == R.PackConstructorInitializers &&
            PackParameters == R.PackParameters &&
+           PPScopeIndent == R.PPScopeIndent &&
            PenaltyBreakAssignment == R.PenaltyBreakAssignment &&
            PenaltyBreakBeforeFirstCallParameter ==
                R.PenaltyBreakBeforeFirstCallParameter &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 2b6e65efbf026..7b6bf07bb4a5c 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -664,6 +664,16 @@ struct 
ScalarEnumerationTraits<FormatStyle::PPDirectiveIndentStyle> {
   }
 };
 
+template <>
+struct ScalarEnumerationTraits<FormatStyle::PPDirectiveScopeIndentStyle> {
+  static void enumeration(IO &IO,
+                          FormatStyle::PPDirectiveScopeIndentStyle &Value) {
+    IO.enumCase(Value, "None", FormatStyle::PPSIS_None);
+    IO.enumCase(Value, "Pragmas", FormatStyle::PPSIS_Pragmas);
+    IO.enumCase(Value, "All", FormatStyle::PPSIS_All);
+  }
+};
+
 template <>
 struct ScalarEnumerationTraits<FormatStyle::QualifierAlignmentStyle> {
   static void enumeration(IO &IO, FormatStyle::QualifierAlignmentStyle &Value) 
{
@@ -1460,6 +1470,7 @@ template <> struct MappingTraits<FormatStyle> {
                    Style.PenaltyReturnTypeOnItsOwnLine);
     IO.mapOptional("PointerAlignment", Style.PointerAlignment);
     IO.mapOptional("PPIndentWidth", Style.PPIndentWidth);
+    IO.mapOptional("PPScopeIndent", Style.PPScopeIndent);
     IO.mapOptional("QualifierAlignment", Style.QualifierAlignment);
     // Default Order for Left/Right based Qualifier alignment.
     if (Style.QualifierAlignment == FormatStyle::QAS_Right)
@@ -2002,6 +2013,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind 
Language) {
                               /*BreakAfter=*/0};
   LLVMStyle.PointerAlignment = FormatStyle::PAS_Right;
   LLVMStyle.PPIndentWidth = -1;
+  LLVMStyle.PPScopeIndent = FormatStyle::PPSIS_None;
   LLVMStyle.QualifierAlignment = FormatStyle::QAS_Leave;
   LLVMStyle.ReferenceAlignment = FormatStyle::RAS_Pointer;
   LLVMStyle.ReflowComments = FormatStyle::RCS_Always;
diff --git a/clang/lib/Format/TokenAnnotator.cpp 
b/clang/lib/Format/TokenAnnotator.cpp
index 32ae8990f52c5..dc30681150f7a 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -6756,8 +6756,8 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine 
&Line,
 
 void TokenAnnotator::printDebugInfo(const AnnotatedLine &Line) const {
   llvm::errs() << "AnnotatedTokens(L=" << Line.Level << ", P=" << Line.PPLevel
-               << ", T=" << Line.Type << ", C=" << Line.IsContinuation
-               << "):\n";
+               << ", PS=" << Line.PPScopeLevel << ", T=" << Line.Type
+               << ", C=" << Line.IsContinuation << "):\n";
   const FormatToken *Tok = Line.First;
   while (Tok) {
     llvm::errs() << " I=" << Tok->IndentLevel << " M=" << Tok->MustBreakBefore
diff --git a/clang/lib/Format/TokenAnnotator.h 
b/clang/lib/Format/TokenAnnotator.h
index 264f39b7b1d60..e5ef48dc05e88 100644
--- a/clang/lib/Format/TokenAnnotator.h
+++ b/clang/lib/Format/TokenAnnotator.h
@@ -52,7 +52,7 @@ class AnnotatedLine {
 public:
   AnnotatedLine(const UnwrappedLine &Line)
       : First(Line.Tokens.front().Tok), Type(LT_Other), Level(Line.Level),
-        PPLevel(Line.PPLevel),
+        PPLevel(Line.PPLevel), PPScopeLevel(Line.PPScopeLevel),
         MatchingOpeningBlockLineIndex(Line.MatchingOpeningBlockLineIndex),
         MatchingClosingBlockLineIndex(Line.MatchingClosingBlockLineIndex),
         InPPDirective(Line.InPPDirective),
@@ -180,6 +180,7 @@ class AnnotatedLine {
   LineType Type;
   unsigned Level;
   unsigned PPLevel;
+  unsigned PPScopeLevel;
   size_t MatchingOpeningBlockLineIndex;
   size_t MatchingClosingBlockLineIndex;
   bool InPPDirective;
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp 
b/clang/lib/Format/UnwrappedLineFormatter.cpp
index 7ea424349d923..85325f88f6434 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -32,6 +32,30 @@ bool isRecordLBrace(const FormatToken &Tok) {
                      TT_StructLBrace, TT_UnionLBrace);
 }
 
+/// Returns the number of columns \p Line must be shifted by to follow the
+/// indentation of the code block that encloses it, or 0 if it must not be.
+///
+/// This is an offset added on top of the indentation that
+/// \c FormatStyle::IndentPPDirectives gives a directive relative to the other
+/// directives, and is independent of it: \c AnnotatedLine::Level counts
+/// preprocessor nesting, \c AnnotatedLine::PPScopeLevel counts the enclosing
+/// braces.
+unsigned getPPScopeIndent(const FormatStyle &Style, const AnnotatedLine &Line) 
{
+  if (Style.PPScopeIndent == FormatStyle::PPSIS_None || !Line.InPPDirective)
+    return 0;
+  // AfterHash keeps the hash in the first column by design, and Leave 
preserves
+  // the original indentation; neither combines with a scope indent.
+  if (Style.IndentPPDirectives == FormatStyle::PPDIS_AfterHash ||
+      Style.IndentPPDirectives == FormatStyle::PPDIS_Leave) {
+    return 0;
+  }
+  if (Style.PPScopeIndent == FormatStyle::PPSIS_Pragmas &&
+      !Line.InPragmaDirective) {
+    return 0;
+  }
+  return Line.PPScopeLevel * Style.IndentWidth;
+}
+
 /// Tracks the indent level of \c AnnotatedLines across levels.
 ///
 /// \c nextLine must be called for each \c AnnotatedLine, after which \c
@@ -58,6 +82,7 @@ class LevelIndentTracker {
   /// next.
   void nextLine(const AnnotatedLine &Line) {
     Offset = getIndentOffset(Line);
+    const unsigned PPScopeIndent = getPPScopeIndent(Style, Line);
     // Update the indent level cache size so that we can rely on it
     // having the right size in adjustToUnmodifiedline.
     if (Line.Level >= IndentForLevel.size())
@@ -89,6 +114,11 @@ class LevelIndentTracker {
       }
       Indent = getIndent(Line.Level);
     }
+    // Shift the directive by the indentation of the code block it appears in,
+    // on top of the indentation it got relative to the other directives. This
+    // applies to the whole directive, so a macro body stays attached to its
+    // #define.
+    Indent += PPScopeIndent;
     if (static_cast<int>(Indent) + Offset >= 0)
       Indent += Offset;
     if (Line.IsContinuation)
@@ -1766,7 +1796,8 @@ void UnwrappedLineFormatter::formatFirstToken(
   if (!Style.isJavaScript() &&
       Style.IndentPPDirectives < FormatStyle::PPDIS_BeforeHash &&
       (Line.Type == LT_PreprocessorDirective ||
-       Line.Type == LT_ImportStatement)) {
+       Line.Type == LT_ImportStatement) &&
+      getPPScopeIndent(Style, Line) == 0) {
     Indent = 0;
   }
 
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp 
b/clang/lib/Format/UnwrappedLineParser.cpp
index 8e6f7e2f2ce07..d5fa416f1037d 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -106,6 +106,7 @@ class ScopedLineState {
     Parser.Line = std::make_unique<UnwrappedLine>();
     Parser.Line->Level = PreBlockLine->Level;
     Parser.Line->PPLevel = PreBlockLine->PPLevel;
+    Parser.Line->PPScopeLevel = PreBlockLine->PPScopeLevel;
     Parser.Line->InPPDirective = PreBlockLine->InPPDirective;
     Parser.Line->InMacroBody = PreBlockLine->InMacroBody;
     Parser.Line->UnbracedBodyLevel = PreBlockLine->UnbracedBodyLevel;
@@ -181,6 +182,7 @@ void UnwrappedLineParser::reset() {
   NestedTooDeep.clear();
   NestedLambdas.clear();
   PPStack.clear();
+  PPScopeLevelStack.clear();
   Line->FirstStartColumn = FirstStartColumn;
 
   if (!Unexpanded.empty())
@@ -1064,6 +1066,9 @@ void 
UnwrappedLineParser::conditionalCompilationCondition(bool Unreachable) {
 }
 
 void UnwrappedLineParser::conditionalCompilationStart(bool Unreachable) {
+  // Remember where the #if sits so that the rest of the conditional can be
+  // aligned with it even if the braces in between are unbalanced.
+  PPScopeLevelStack.push_back(Line->PPScopeLevel);
   ++PPBranchLevel;
   assert(PPBranchLevel >= 0 && PPBranchLevel <= 
(int)PPLevelBranchIndex.size());
   if (PPBranchLevel == (int)PPLevelBranchIndex.size()) {
@@ -1076,6 +1081,8 @@ void 
UnwrappedLineParser::conditionalCompilationStart(bool Unreachable) {
 }
 
 void UnwrappedLineParser::conditionalCompilationAlternative() {
+  if (!PPScopeLevelStack.empty())
+    Line->PPScopeLevel = PPScopeLevelStack.back();
   if (!PPStack.empty())
     PPStack.pop_back();
   assert(PPBranchLevel < (int)PPLevelBranchIndex.size());
@@ -1087,6 +1094,10 @@ void 
UnwrappedLineParser::conditionalCompilationAlternative() {
 }
 
 void UnwrappedLineParser::conditionalCompilationEnd() {
+  if (!PPScopeLevelStack.empty()) {
+    Line->PPScopeLevel = PPScopeLevelStack.back();
+    PPScopeLevelStack.pop_back();
+  }
   assert(PPBranchLevel < (int)PPLevelBranchIndex.size());
   if (PPBranchLevel >= 0 && !PPChainBranchIndex.empty()) {
     if (PPChainBranchIndex.top() + 1 > PPLevelBranchCount[PPBranchLevel])
@@ -5032,6 +5043,11 @@ void UnwrappedLineParser::readToken(int LevelDifference) 
{
               static_cast<unsigned>(-LevelDifference) <= Line->Level) &&
              "LevelDifference makes Line->Level negative");
       Line->Level += LevelDifference;
+      // Remember the indent level of the enclosing code block before any
+      // preprocessor nesting is mixed into Level, so that PPScopeIndent can 
add
+      // it back as an independent offset. Recovering it from Level afterwards
+      // is what made the reverted D92753 underflow inside #if/#endif.
+      const unsigned CodeLevel = Line->Level;
       // 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.
@@ -5041,6 +5057,9 @@ void UnwrappedLineParser::readToken(int LevelDifference) {
       }
       assert(Line->Level >= Line->UnbracedBodyLevel);
       Line->Level -= Line->UnbracedBodyLevel;
+      Line->PPScopeLevel = CodeLevel >= Line->UnbracedBodyLevel
+                               ? CodeLevel - Line->UnbracedBodyLevel
+                               : 0;
       flushComments(isOnNewLine(*FormatTok));
       const bool IsEndIf = Tokens->peekNextToken()->is(tok::pp_endif);
       parsePPDirective();
diff --git a/clang/lib/Format/UnwrappedLineParser.h 
b/clang/lib/Format/UnwrappedLineParser.h
index 8fa4e9f7540d5..bb68fff07686e 100644
--- a/clang/lib/Format/UnwrappedLineParser.h
+++ b/clang/lib/Format/UnwrappedLineParser.h
@@ -42,6 +42,16 @@ struct UnwrappedLine {
   /// \c InMacroBody line, and 0 otherwise.
   unsigned PPLevel = 0;
 
+  /// The indent level of the code block enclosing this preprocessor directive,
+  /// and 0 if this line is not part of one.
+  ///
+  /// For a directive, \c Level counts preprocessor nesting only, because the
+  /// enclosing code indentation is discarded when the directive is parsed (see
+  /// \c ScopedMacroState). This keeps that indentation available as a separate
+  /// axis, so that \c FormatStyle::PPScopeIndent can add it back without 
having
+  /// to recover it from \c Level.
+  unsigned PPScopeLevel = 0;
+
   /// Whether this \c UnwrappedLine is part of a preprocessor directive.
   bool InPPDirective = false;
   /// Whether this \c UnwrappedLine is part of a pramga directive.
@@ -392,6 +402,12 @@ class UnwrappedLineParser {
   // sequence.
   std::stack<int> PPChainBranchIndex;
 
+  // Contains the enclosing code indent level of the #if that opened each
+  // conditional currently being parsed. Used to align #else/#elif/#endif with
+  // their #if when FormatStyle::PPScopeIndent is enabled, so that a 
conditional
+  // spanning an unbalanced brace does not indent its parts inconsistently.
+  SmallVector<unsigned, 8> PPScopeLevelStack;
+
   // Include guard search state. Used to fixup preprocessor indent levels
   // so that include guards do not participate in indentation.
   enum IncludeGuardState {
diff --git a/clang/unittests/Format/ConfigParseTest.cpp 
b/clang/unittests/Format/ConfigParseTest.cpp
index 9350ba7eb3de4..ddb291e229c2f 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -1052,6 +1052,12 @@ TEST(ConfigParseTest, ParsesConfiguration) {
   CHECK_PARSE("IndentGotoLabels: true", IndentGotoLabels,
               FormatStyle::IGLS_OuterIndent);
 
+  Style.PPScopeIndent = FormatStyle::PPSIS_All;
+  CHECK_PARSE("PPScopeIndent: None", PPScopeIndent, FormatStyle::PPSIS_None);
+  CHECK_PARSE("PPScopeIndent: Pragmas", PPScopeIndent,
+              FormatStyle::PPSIS_Pragmas);
+  CHECK_PARSE("PPScopeIndent: All", PPScopeIndent, FormatStyle::PPSIS_All);
+
   Style.BitFieldColonSpacing = FormatStyle::BFCS_None;
   CHECK_PARSE("BitFieldColonSpacing: Both", BitFieldColonSpacing,
               FormatStyle::BFCS_Both);
diff --git a/clang/unittests/Format/FormatTest.cpp 
b/clang/unittests/Format/FormatTest.cpp
index b72a683ac1fff..914df971854a8 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -5904,6 +5904,152 @@ TEST_F(FormatTest, IndentsPPDirectiveWithPPIndentWidth) 
{
                style);
 }
 
+TEST_F(FormatTest, IndentsPPDirectivesToScope) {
+  auto Style = getLLVMStyle();
+  Style.PPScopeIndent = FormatStyle::PPSIS_Pragmas;
+
+  // Only pragmas follow the enclosing scope; other directives are untouched.
+  verifyFormat("#include <omp.h>\n"
+               "void f() {\n"
+               "  #pragma omp parallel\n"
+               "  {\n"
+               "    g();\n"
+               "\n"
+               "    #pragma omp critical\n"
+               "    {\n"
+               "      h();\n"
+               "    }\n"
+               "  }\n"
+               "}",
+               "#include <omp.h>\n"
+               "void f() {\n"
+               "#pragma omp parallel\n"
+               "  {\n"
+               "    g();\n"
+               "\n"
+               "#pragma omp critical\n"
+               "    {\n"
+               "      h();\n"
+               "    }\n"
+               "  }\n"
+               "}",
+               Style);
+
+  // Conditionals keep their own indentation while the pragma follows the 
scope.
+  verifyFormat("void f() {\n"
+               "#ifdef SIMD\n"
+               "  #pragma omp simd\n"
+               "#endif\n"
+               "  for (int i = 0; i < 4; ++i) {\n"
+               "  }\n"
+               "}",
+               Style);
+
+  // All directives follow the enclosing scope.
+  Style.PPScopeIndent = FormatStyle::PPSIS_All;
+  verifyFormat("void f() {\n"
+               "  if (a) {\n"
+               "    #ifdef SIMD\n"
+               "    #pragma omp simd\n"
+               "    #endif\n"
+               "    for (int i = 0; i < 4; ++i) {\n"
+               "    }\n"
+               "  }\n"
+               "}",
+               Style);
+
+  // A directive at file scope inside a conditional must stay in the first
+  // column. Deriving the scope level from Line->Level instead of tracking it
+  // separately is what made the reverted D92753 underflow here.
+  verifyFormat("#ifdef A\n"
+               "#pragma pack()\n"
+               "#endif",
+               Style);
+
+  // #endif is aligned with the #if that opened the conditional, even when the
+  // braces in between are unbalanced and the two are in different scopes.
+  // Without that, the #endif below would be indented by one level.
+  verifyNoChange("#ifdef A\n"
+                 "void f() {\n"
+                 "#endif\n"
+                 "  g();\n"
+                 "}",
+                 Style);
+
+  // A macro body stays attached to its #define.
+  Style.IndentPPDirectives = FormatStyle::PPDIS_BeforeHash;
+  Style.AlignEscapedNewlines = FormatStyle::ENAS_Left;
+  verifyFormat("void f() {\n"
+               "  #ifdef A\n"
+               "    #define B \\\n"
+               "      c;      \\\n"
+               "      d;\n"
+               "  #endif\n"
+               "}",
+               Style);
+  Style.AlignEscapedNewlines = FormatStyle::ENAS_Right;
+
+  // BeforeHash indents the directives relative to each other and the scope
+  // indent is added on top of that, using IndentWidth.
+  verifyFormat("void f() {\n"
+               "  if (a) {\n"
+               "    #ifdef SIMD\n"
+               "      #pragma omp simd\n"
+               "    #endif\n"
+               "    for (int i = 0; i < 4; ++i) {\n"
+               "    }\n"
+               "  }\n"
+               "}",
+               "void f() {\n"
+               "  if (a) {\n"
+               "#ifdef SIMD\n"
+               "  #pragma omp simd\n"
+               "#endif\n"
+               "    for (int i = 0; i < 4; ++i) {\n"
+               "    }\n"
+               "  }\n"
+               "}",
+               Style);
+
+  // AfterHash keeps the hash in the first column by design, so the scope 
indent
+  // does not apply.
+  Style.IndentPPDirectives = FormatStyle::PPDIS_AfterHash;
+  verifyFormat("void f() {\n"
+               "  if (a) {\n"
+               "#ifdef SIMD\n"
+               "#  pragma omp simd\n"
+               "#endif\n"
+               "    for (int i = 0; i < 4; ++i) {\n"
+               "    }\n"
+               "  }\n"
+               "}",
+               Style);
+
+  // Leave preserves the original indentation, so the scope indent does not
+  // apply either.
+  Style.IndentPPDirectives = FormatStyle::PPDIS_Leave;
+  verifyNoChange("void f() {\n"
+                 "  if (a) {\n"
+                 "#pragma omp simd\n"
+                 "    for (int i = 0; i < 4; ++i) {\n"
+                 "    }\n"
+                 "  }\n"
+                 "}",
+                 Style);
+
+  // Without the option nothing changes.
+  Style.PPScopeIndent = FormatStyle::PPSIS_None;
+  Style.IndentPPDirectives = FormatStyle::PPDIS_None;
+  verifyFormat("void f() {\n"
+               "  if (a) {\n"
+               "#pragma omp simd\n"
+               "    for (int i = 0; i < 4; ++i) {\n"
+               "    }\n"
+               "  }\n"
+               "}",
+               Style);
+}
+
 TEST_F(FormatTest, IndentsPPDirectiveInReducedSpace) {
   verifyFormat("#define A(BB)", getLLVMStyleWithColumns(13));
   verifyFormat("#define A( \\\n    BB)", getLLVMStyleWithColumns(12));

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to