[clang] [clang-format] Allow one indent level before explicit access labels (PR #227704)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 2 15:03:43 PDT 2026
https://github.com/noambouillet updated https://github.com/llvm/llvm-project/pull/227704
>From f55190042b1ba98c2946eb94e468cc4899718a38 Mon Sep 17 00:00:00 2001
From: Noam Bouillet <noam.bouillet at epitech.eu>
Date: Wed, 30 Sep 2026 14:07:44 +0200
Subject: [PATCH 1/5] feat(format): make implicit access indent optional
Preserve existing output by default while allowing records without an explicit access label to use one indentation level.
Refs #61631
---
clang/docs/ClangFormatStyleOptions.md | 17 +++++--
clang/docs/ReleaseNotes.md | 4 ++
clang/include/clang/Format/Format.h | 16 +++++--
clang/lib/Format/Format.cpp | 3 ++
clang/lib/Format/UnwrappedLineParser.cpp | 47 ++++++++++++++-----
clang/lib/Format/UnwrappedLineParser.h | 6 ++-
clang/unittests/Format/ConfigParseTest.cpp | 1 +
clang/unittests/Format/FormatTest.cpp | 53 ++++++++++++++++++++++
clang/unittests/Format/FormatTestJava.cpp | 12 +++++
9 files changed, 140 insertions(+), 19 deletions(-)
diff --git a/clang/docs/ClangFormatStyleOptions.md b/clang/docs/ClangFormatStyleOptions.md
index 81984ff185e53..d831bcac07c9c 100644
--- a/clang/docs/ClangFormatStyleOptions.md
+++ b/clang/docs/ClangFormatStyleOptions.md
@@ -4752,9 +4752,10 @@ the configuration (without a prefix: `Auto`).
the record members, respecting the `AccessModifierOffset`. Record
members are indented one level below the record.
When `true`, access modifiers get their own indentation level. As a
- consequence, record members are always indented 2 levels below the record,
- regardless of the access modifier presence. Value of the
- `AccessModifierOffset` is ignored.
+ consequence, record members are by default indented 2 levels below the
+ record, regardless of the access modifier presence. Value of the
+ `AccessModifierOffset` is ignored. `IndentImplicitAccessModifiers` can
+ change the indentation before the first explicit access modifier.
```c++
false: true:
@@ -4952,6 +4953,16 @@ the configuration (without a prefix: `Auto`).
+(indentimplicitaccessmodifiers)=
+
+**IndentImplicitAccessModifiers** (`Boolean`) {versionbadge}`clang-format 24` {ref}`¶ <IndentImplicitAccessModifiers>`
+
+: When `IndentAccessModifiers` is `true`, indent members before the first
+ explicit access modifier by two levels. Set this option to `false` to
+ indent those members by one level. Members after an explicit access
+ modifier still use two levels. This option has no effect if
+ `IndentAccessModifiers` is false.
+
(indentppdirectives)=
**IndentPPDirectives** (`PPDirectiveIndentStyle`) {versionbadge}`clang-format 6` {ref}`¶ <IndentPPDirectives>`
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index c778703e8cc6f..a2090bb98df30 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -944,6 +944,10 @@ features cannot lower the translation-unit ABI level;
### clang-format
+- Add `IndentImplicitAccessModifiers` to allow members before the first
+ explicit access modifier to use one indentation level when
+ `IndentAccessModifiers` is enabled. The default preserves existing formatting.
+
- Add `SpacesInBlockComments` option to control spacing after `/*` and
before `*/` in ordinary block comments.
- Add `AfterRequiresExpression` sub-option of `BraceWrapping` to wrap the
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 6d4fa6e8ee2a7..40ae52a0148e8 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3187,9 +3187,10 @@ struct FormatStyle {
/// the record members, respecting the `AccessModifierOffset`. Record
/// members are indented one level below the record.
/// When `true`, access modifiers get their own indentation level. As a
- /// consequence, record members are always indented 2 levels below the record,
- /// regardless of the access modifier presence. Value of the
- /// `AccessModifierOffset` is ignored.
+ /// consequence, record members are by default indented 2 levels below the
+ /// record, regardless of the access modifier presence. Value of the
+ /// `AccessModifierOffset` is ignored. `IndentImplicitAccessModifiers` can
+ /// change the indentation before the first explicit access modifier.
/// \code
/// false: true:
/// class C { vs. class C {
@@ -3208,6 +3209,14 @@ struct FormatStyle {
/// \version 13
bool IndentAccessModifiers;
+ /// When `IndentAccessModifiers` is `true`, indent members before the first
+ /// explicit access modifier by two levels. Set this option to `false` to
+ /// indent those members by one level. Members after an explicit access
+ /// modifier still use two levels. This option has no effect if
+ /// `IndentAccessModifiers` is false.
+ /// \version 24
+ bool IndentImplicitAccessModifiers;
+
/// Indent case label blocks one level from the case label.
///
/// When `false`, the block following the case label uses the same
@@ -6242,6 +6251,7 @@ struct FormatStyle {
R.IncludeStyle.IncludeIsMainSourceRegex &&
IncludeStyle.MainIncludeChar == R.IncludeStyle.MainIncludeChar &&
IndentAccessModifiers == R.IndentAccessModifiers &&
+ IndentImplicitAccessModifiers == R.IndentImplicitAccessModifiers &&
IndentCaseBlocks == R.IndentCaseBlocks &&
IndentCaseLabels == R.IndentCaseLabels &&
IndentExportBlock == R.IndentExportBlock &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 4c78c1dbe9f80..2f6d70d2bf4e2 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1399,6 +1399,8 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("IncludeIsMainSourceRegex",
Style.IncludeStyle.IncludeIsMainSourceRegex);
IO.mapOptional("IndentAccessModifiers", Style.IndentAccessModifiers);
+ IO.mapOptional("IndentImplicitAccessModifiers",
+ Style.IndentImplicitAccessModifiers);
IO.mapOptional("IndentCaseBlocks", Style.IndentCaseBlocks);
IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels);
IO.mapOptional("IndentExportBlock", Style.IndentExportBlock);
@@ -1977,6 +1979,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.IncludeStyle.IncludeIsMainRegex = "(Test)?$";
LLVMStyle.IncludeStyle.MainIncludeChar = tooling::IncludeStyle::MICD_Quote;
LLVMStyle.IndentAccessModifiers = false;
+ LLVMStyle.IndentImplicitAccessModifiers = true;
LLVMStyle.IndentCaseBlocks = false;
LLVMStyle.IndentCaseLabels = false;
LLVMStyle.IndentExportBlock = true;
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 4825e825af1fa..0e0825112b513 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -352,7 +352,8 @@ bool UnwrappedLineParser::precededByCommentOrPPDirective() const {
/// (A simple block has a single statement.)
bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace,
IfStmtKind *IfKind,
- FormatToken **IfLeftBrace) {
+ FormatToken **IfLeftBrace,
+ bool *SeenExplicitAccessModifier) {
const bool InRequiresExpression =
OpeningBrace && OpeningBrace->is(TT_RequiresExpressionLBrace);
const bool IsPrecededByCommentOrPPDirective =
@@ -377,7 +378,18 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace,
Kind = tok::r_brace;
auto ParseDefault = [this, OpeningBrace, IfKind, &IfLBrace, &HasDoWhile,
- &HasLabel, &StatementCount] {
+ &HasLabel, &StatementCount,
+ SeenExplicitAccessModifier] {
+ const bool IsQtAccessLabel =
+ SeenExplicitAccessModifier && !*SeenExplicitAccessModifier &&
+ FormatTok->isOneOf(Keywords.kw_signals, Keywords.kw_qsignals,
+ Keywords.kw_slots, Keywords.kw_qslots) &&
+ Tokens->peekNextToken(/*SkipComment=*/true)->is(tok::colon);
+ if (SeenExplicitAccessModifier && !*SeenExplicitAccessModifier &&
+ (FormatTok->isAccessSpecifierKeyword() || IsQtAccessLabel)) {
+ ++Line->Level;
+ *SeenExplicitAccessModifier = true;
+ }
parseStructuralElement(OpeningBrace, IfKind, &IfLBrace,
HasDoWhile ? nullptr : &HasDoWhile,
HasLabel ? nullptr : &HasLabel);
@@ -744,11 +756,10 @@ bool UnwrappedLineParser::mightFitOnOneLine(
return Line.Level * Style.IndentWidth + Length <= ColumnLimit;
}
-FormatToken *UnwrappedLineParser::parseBlock(bool MustBeDeclaration,
- unsigned AddLevels, bool MunchSemi,
- bool KeepBraces,
- IfStmtKind *IfKind,
- bool UnindentWhitesmithsBraces) {
+FormatToken *UnwrappedLineParser::parseBlock(
+ bool MustBeDeclaration, unsigned AddLevels, bool MunchSemi, bool KeepBraces,
+ IfStmtKind *IfKind, bool UnindentWhitesmithsBraces,
+ bool IndentAfterExplicitAccessModifier) {
auto HandleVerilogBlockLabel = [this]() {
// ":" name
if (Style.isVerilog() && FormatTok->is(tok::colon)) {
@@ -820,7 +831,11 @@ FormatToken *UnwrappedLineParser::parseBlock(bool MustBeDeclaration,
Line->Level += AddLevels - (IsWhitesmiths ? 1 : 0);
FormatToken *IfLBrace = nullptr;
- const bool SimpleBlock = parseLevel(Tok, IfKind, &IfLBrace);
+ bool SeenExplicitAccessModifier = false;
+ const bool SimpleBlock =
+ parseLevel(Tok, IfKind, &IfLBrace,
+ IndentAfterExplicitAccessModifier ? &SeenExplicitAccessModifier
+ : nullptr);
if (eof())
return IfLBrace;
@@ -879,7 +894,8 @@ FormatToken *UnwrappedLineParser::parseBlock(bool MustBeDeclaration,
size_t PPEndHash = computePPHash();
// Munch the closing brace.
- nextToken(/*LevelDifference=*/-AddLevels);
+ nextToken(/*LevelDifference=*/
+ -static_cast<int>(AddLevels + SeenExplicitAccessModifier));
// When this is a function block and there is an unnecessary semicolon
// afterwards then mark it as optional (so the RemoveSemi pass can get rid of
@@ -4298,8 +4314,17 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) {
addUnwrappedLine();
}
- unsigned AddLevels = Style.IndentAccessModifiers ? 2u : 1u;
- parseBlock(/*MustBeDeclaration=*/true, AddLevels, /*MunchSemi=*/false);
+ const bool IndentAfterExplicitAccessModifier =
+ Style.isCpp() && Style.IndentAccessModifiers &&
+ !Style.IndentImplicitAccessModifiers;
+ unsigned AddLevels =
+ Style.IndentAccessModifiers && !IndentAfterExplicitAccessModifier
+ ? 2u
+ : 1u;
+ parseBlock(/*MustBeDeclaration=*/true, AddLevels, /*MunchSemi=*/false,
+ /*KeepBraces=*/true, /*IfKind=*/nullptr,
+ /*UnindentWhitesmithsBraces=*/false,
+ IndentAfterExplicitAccessModifier);
}
setPreviousRBraceType(ClosingBraceType);
}
diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h
index 5b93c8f346d75..2e1755ad3b2c6 100644
--- a/clang/lib/Format/UnwrappedLineParser.h
+++ b/clang/lib/Format/UnwrappedLineParser.h
@@ -126,13 +126,15 @@ class UnwrappedLineParser {
bool precededByCommentOrPPDirective() const;
bool parseLevel(const FormatToken *OpeningBrace = nullptr,
IfStmtKind *IfKind = nullptr,
- FormatToken **IfLeftBrace = nullptr);
+ FormatToken **IfLeftBrace = nullptr,
+ bool *SeenExplicitAccessModifier = nullptr);
bool mightFitOnOneLine(UnwrappedLine &Line,
const FormatToken *OpeningBrace = nullptr) const;
FormatToken *parseBlock(bool MustBeDeclaration = false,
unsigned AddLevels = 1u, bool MunchSemi = true,
bool KeepBraces = true, IfStmtKind *IfKind = nullptr,
- bool UnindentWhitesmithsBraces = false);
+ bool UnindentWhitesmithsBraces = false,
+ bool IndentAfterExplicitAccessModifier = false);
void parseChildBlock();
void parsePPDirective();
void parsePPDefine();
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index 86511edb9d40d..ccbc346901460 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -190,6 +190,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding");
CHECK_PARSE_BOOL(DisableFormat);
CHECK_PARSE_BOOL(IndentAccessModifiers);
+ CHECK_PARSE_BOOL(IndentImplicitAccessModifiers);
CHECK_PARSE_BOOL(IndentCaseBlocks);
CHECK_PARSE_BOOL(IndentCaseLabels);
CHECK_PARSE_BOOL(IndentExportBlock);
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index bb630da34d7d9..656a352ecb005 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -24920,6 +24920,59 @@ TEST_F(FormatTest, IndentAccessModifiers) {
Style);
}
+TEST_F(FormatTest, IndentImplicitAccessModifiers) {
+ FormatStyle Style = getLLVMStyle();
+ Style.IndentAccessModifiers = true;
+ Style.IndentImplicitAccessModifiers = false;
+ Style.IndentWidth = 4;
+ Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Never;
+ Style.BreakBeforeBraces = FormatStyle::BS_Allman;
+
+ verifyFormat("class Outer\n"
+ "{\n"
+ " public:\n"
+ " struct Inner\n"
+ " {\n"
+ " bool first;\n"
+ " bool second;\n"
+ " };\n"
+ "};",
+ Style);
+ verifyFormat("struct S\n"
+ "{\n"
+ " int before;\n"
+ " private:\n"
+ " int after;\n"
+ "};",
+ Style);
+ verifyFormat("union U\n"
+ "{\n"
+ " int first;\n"
+ " class Inner\n"
+ " {\n"
+ " public:\n"
+ " int member;\n"
+ " };\n"
+ " int last;\n"
+ "};",
+ Style);
+ verifyFormat("class QtObject\n"
+ "{\n"
+ " signals:\n"
+ " void changed();\n"
+ "};",
+ Style);
+
+ Style.BreakBeforeBraces = FormatStyle::BS_Whitesmiths;
+ verifyFormat("struct S\n"
+ " {\n"
+ " int before;\n"
+ " public:\n"
+ " int after;\n"
+ " };",
+ Style);
+}
+
TEST_F(FormatTest, LimitlessStringsAndComments) {
auto Style = getLLVMStyleWithColumns(0);
constexpr StringRef Code(
diff --git a/clang/unittests/Format/FormatTestJava.cpp b/clang/unittests/Format/FormatTestJava.cpp
index a11fce963d820..86e4b2fc361b2 100644
--- a/clang/unittests/Format/FormatTestJava.cpp
+++ b/clang/unittests/Format/FormatTestJava.cpp
@@ -28,6 +28,18 @@ class FormatTestJava : public test::FormatTestBase {
}
};
+TEST_F(FormatTestJava, IndentImplicitAccessModifiersDoesNotAffectJava) {
+ FormatStyle Style = getDefaultStyle();
+ Style.IndentWidth = 4;
+ Style.IndentAccessModifiers = true;
+ Style.IndentImplicitAccessModifiers = false;
+ verifyFormat("class C {\n"
+ " int before;\n"
+ " public int after;\n"
+ "}",
+ Style);
+}
+
TEST_F(FormatTestJava, NoAlternativeOperatorNames) {
verifyFormat("someObject.and();");
}
>From beb4dc8d614e0d71afcd45298dbbbe7dd1857e63 Mon Sep 17 00:00:00 2001
From: Noam Bouillet <noam.bouillet at epitech.eu>
Date: Thu, 1 Oct 2026 18:15:19 +0200
Subject: [PATCH 2/5] refactor(format): use enum for access indent
Preserve existing YAML true/false values while replacing the added boolean with a three-mode enum. C++ FormatStyle callers must use enum values instead of bool assignments.
Refs #61631
---
clang/docs/ClangFormatStyleOptions.md | 69 ++++++++++++++-------
clang/docs/ReleaseNotes.md | 7 +--
clang/include/clang/Format/Format.h | 61 +++++++++++-------
clang/lib/Format/Format.cpp | 18 ++++--
clang/lib/Format/UnwrappedLineFormatter.cpp | 5 +-
clang/lib/Format/UnwrappedLineParser.cpp | 43 ++++++++-----
clang/unittests/Format/ConfigParseTest.cpp | 15 ++++-
clang/unittests/Format/FormatTest.cpp | 46 ++++++++++++--
clang/unittests/Format/FormatTestCSharp.cpp | 11 ++++
clang/unittests/Format/FormatTestJava.cpp | 5 +-
10 files changed, 200 insertions(+), 80 deletions(-)
diff --git a/clang/docs/ClangFormatStyleOptions.md b/clang/docs/ClangFormatStyleOptions.md
index d831bcac07c9c..5aabda184da0f 100644
--- a/clang/docs/ClangFormatStyleOptions.md
+++ b/clang/docs/ClangFormatStyleOptions.md
@@ -4744,21 +4744,24 @@ the configuration (without a prefix: `Auto`).
(indentaccessmodifiers)=
-**IndentAccessModifiers** (`Boolean`) {versionbadge}`clang-format 13` {ref}`¶ <IndentAccessModifiers>`
-
-: Specify whether access modifiers should have their own indentation level.
-
- When `false`, access modifiers are indented (or outdented) relative to
- the record members, respecting the `AccessModifierOffset`. Record
- members are indented one level below the record.
- When `true`, access modifiers get their own indentation level. As a
- consequence, record members are by default indented 2 levels below the
- record, regardless of the access modifier presence. Value of the
- `AccessModifierOffset` is ignored. `IndentImplicitAccessModifiers` can
- change the indentation before the first explicit access modifier.
+**IndentAccessModifiers** (`IndentAccessModifierStyle`) {versionbadge}`clang-format 13` {ref}`¶ <IndentAccessModifiers>`
+
+: Specify how access modifiers and record members are indented.
+
+ `Never` (also `false` in configuration) uses `AccessModifierOffset` for
+ access modifiers and indents members one level below the record. `Always`
+ (also `true` in configuration) gives access modifiers their own
+ indentation level and indents all members two levels below the record,
+ even if there is no explicit access modifier.
+ `AfterFirstAccessModifier` gives members one level before the first
+ explicit access modifier and two levels afterwards. If there is no
+ explicit access modifier, members stay at one level. This behavior applies
+ to C, C++, and Objective-C; other languages treat it like `Always`.
+ `AccessModifierOffset` is ignored by `Always` and
+ `AfterFirstAccessModifier`.
```c++
- false: true:
+ Never (false): Always (true):
class C { vs. class C {
class D { class D {
void bar(); void bar();
@@ -4773,6 +4776,36 @@ the configuration (without a prefix: `Auto`).
} }
```
+ ```yaml
+ IndentAccessModifiers: AfterFirstAccessModifier
+ ```
+
+ ```c++
+ struct S {
+ int before;
+
+ public:
+ int after;
+ };
+ ```
+
+ Possible values:
+
+ - `IAMS_Never` (in configuration: `Never`)
+ Use `AccessModifierOffset` for access modifiers and indent members one
+ level below the record.
+
+ - `IAMS_Always` (in configuration: `Always`)
+ Give access modifiers their own indentation level and indent all
+ members two levels below the record.
+
+ - `IAMS_AfterFirstAccessModifier` (in configuration: `AfterFirstAccessModifier`)
+ In C, C++, and Objective-C, indent members one level until the first
+ explicit access modifier, then two levels. Other languages use the
+ `Always` behavior. Available since clang-format 24.
+
+
+
(indentcaseblocks)=
**IndentCaseBlocks** (`Boolean`) {versionbadge}`clang-format 11` {ref}`¶ <IndentCaseBlocks>`
@@ -4953,16 +4986,6 @@ the configuration (without a prefix: `Auto`).
-(indentimplicitaccessmodifiers)=
-
-**IndentImplicitAccessModifiers** (`Boolean`) {versionbadge}`clang-format 24` {ref}`¶ <IndentImplicitAccessModifiers>`
-
-: When `IndentAccessModifiers` is `true`, indent members before the first
- explicit access modifier by two levels. Set this option to `false` to
- indent those members by one level. Members after an explicit access
- modifier still use two levels. This option has no effect if
- `IndentAccessModifiers` is false.
-
(indentppdirectives)=
**IndentPPDirectives** (`PPDirectiveIndentStyle`) {versionbadge}`clang-format 6` {ref}`¶ <IndentPPDirectives>`
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a2090bb98df30..edbdf4f5dd31b 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -944,10 +944,6 @@ features cannot lower the translation-unit ABI level;
### clang-format
-- Add `IndentImplicitAccessModifiers` to allow members before the first
- explicit access modifier to use one indentation level when
- `IndentAccessModifiers` is enabled. The default preserves existing formatting.
-
- Add `SpacesInBlockComments` option to control spacing after `/*` and
before `*/` in ordinary block comments.
- Add `AfterRequiresExpression` sub-option of `BraceWrapping` to wrap the
@@ -957,6 +953,9 @@ features cannot lower the translation-unit ABI level;
- `QualifierOrder` now supports `typedef`, `consteval`, `constinit`,
`thread_local`, `extern`, `mutable`, `signed`, `unsigned`, `long`, `short`,
and `explicit` declaration specifiers.
+- Extend `IndentAccessModifiers` with `AfterFirstAccessModifier` to indent
+ members before the first explicit access modifier by one level. Existing
+ configuration values `true` and `false` remain supported.
### libclang
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 40ae52a0148e8..e6c8aa1f90604 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3181,18 +3181,35 @@ struct FormatStyle {
/// \version 13
std::vector<std::string> IfMacros;
- /// Specify whether access modifiers should have their own indentation level.
- ///
- /// When `false`, access modifiers are indented (or outdented) relative to
- /// the record members, respecting the `AccessModifierOffset`. Record
- /// members are indented one level below the record.
- /// When `true`, access modifiers get their own indentation level. As a
- /// consequence, record members are by default indented 2 levels below the
- /// record, regardless of the access modifier presence. Value of the
- /// `AccessModifierOffset` is ignored. `IndentImplicitAccessModifiers` can
- /// change the indentation before the first explicit access modifier.
+ /// Modes for indenting access modifiers and record members.
+ enum IndentAccessModifierStyle : int8_t {
+ /// Use `AccessModifierOffset` for access modifiers and indent members one
+ /// level below the record.
+ IAMS_Never,
+ /// Give access modifiers their own indentation level and indent all
+ /// members two levels below the record.
+ IAMS_Always,
+ /// In C, C++, and Objective-C, indent members one level until the first
+ /// explicit access modifier, then two levels. Other languages use the
+ /// `Always` behavior. Available since clang-format 24.
+ IAMS_AfterFirstAccessModifier,
+ };
+
+ /// Specify how access modifiers and record members are indented.
+ ///
+ /// `Never` (also `false` in configuration) uses `AccessModifierOffset` for
+ /// access modifiers and indents members one level below the record. `Always`
+ /// (also `true` in configuration) gives access modifiers their own
+ /// indentation level and indents all members two levels below the record,
+ /// even if there is no explicit access modifier.
+ /// `AfterFirstAccessModifier` gives members one level before the first
+ /// explicit access modifier and two levels afterwards. If there is no
+ /// explicit access modifier, members stay at one level. This behavior applies
+ /// to C, C++, and Objective-C; other languages treat it like `Always`.
+ /// `AccessModifierOffset` is ignored by `Always` and
+ /// `AfterFirstAccessModifier`.
/// \code
- /// false: true:
+ /// Never (false): Always (true):
/// class C { vs. class C {
/// class D { class D {
/// void bar(); void bar();
@@ -3206,16 +3223,19 @@ struct FormatStyle {
/// return 1; return 1;
/// } }
/// \endcode
+ /// \code{.yaml}
+ /// IndentAccessModifiers: AfterFirstAccessModifier
+ /// \endcode
+ /// \code
+ /// struct S {
+ /// int before;
+ ///
+ /// public:
+ /// int after;
+ /// };
+ /// \endcode
/// \version 13
- bool IndentAccessModifiers;
-
- /// When `IndentAccessModifiers` is `true`, indent members before the first
- /// explicit access modifier by two levels. Set this option to `false` to
- /// indent those members by one level. Members after an explicit access
- /// modifier still use two levels. This option has no effect if
- /// `IndentAccessModifiers` is false.
- /// \version 24
- bool IndentImplicitAccessModifiers;
+ IndentAccessModifierStyle IndentAccessModifiers;
/// Indent case label blocks one level from the case label.
///
@@ -6251,7 +6271,6 @@ struct FormatStyle {
R.IncludeStyle.IncludeIsMainSourceRegex &&
IncludeStyle.MainIncludeChar == R.IncludeStyle.MainIncludeChar &&
IndentAccessModifiers == R.IndentAccessModifiers &&
- IndentImplicitAccessModifiers == R.IndentImplicitAccessModifiers &&
IndentCaseBlocks == R.IndentCaseBlocks &&
IndentCaseLabels == R.IndentCaseLabels &&
IndentExportBlock == R.IndentExportBlock &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 2f6d70d2bf4e2..bdb15326a1603 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -477,6 +477,19 @@ struct ScalarEnumerationTraits<FormatStyle::EnumTrailingCommaStyle> {
}
};
+template <>
+struct ScalarEnumerationTraits<FormatStyle::IndentAccessModifierStyle> {
+ static void enumeration(IO &IO,
+ FormatStyle::IndentAccessModifierStyle &Value) {
+ IO.enumCase(Value, "Never", FormatStyle::IAMS_Never);
+ IO.enumCase(Value, "Always", FormatStyle::IAMS_Always);
+ IO.enumCase(Value, "AfterFirstAccessModifier",
+ FormatStyle::IAMS_AfterFirstAccessModifier);
+ IO.enumCase(Value, "false", FormatStyle::IAMS_Never);
+ IO.enumCase(Value, "true", FormatStyle::IAMS_Always);
+ }
+};
+
template <>
struct ScalarEnumerationTraits<FormatStyle::IndentExternBlockStyle> {
static void enumeration(IO &IO, FormatStyle::IndentExternBlockStyle &Value) {
@@ -1399,8 +1412,6 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("IncludeIsMainSourceRegex",
Style.IncludeStyle.IncludeIsMainSourceRegex);
IO.mapOptional("IndentAccessModifiers", Style.IndentAccessModifiers);
- IO.mapOptional("IndentImplicitAccessModifiers",
- Style.IndentImplicitAccessModifiers);
IO.mapOptional("IndentCaseBlocks", Style.IndentCaseBlocks);
IO.mapOptional("IndentCaseLabels", Style.IndentCaseLabels);
IO.mapOptional("IndentExportBlock", Style.IndentExportBlock);
@@ -1978,8 +1989,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
{".*", 1, 0, false}};
LLVMStyle.IncludeStyle.IncludeIsMainRegex = "(Test)?$";
LLVMStyle.IncludeStyle.MainIncludeChar = tooling::IncludeStyle::MICD_Quote;
- LLVMStyle.IndentAccessModifiers = false;
- LLVMStyle.IndentImplicitAccessModifiers = true;
+ LLVMStyle.IndentAccessModifiers = FormatStyle::IAMS_Never;
LLVMStyle.IndentCaseBlocks = false;
LLVMStyle.IndentCaseLabels = false;
LLVMStyle.IndentExportBlock = true;
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index f005f228328ce..4d27ddde33ff0 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -136,8 +136,9 @@ class LevelIndentTracker {
// The AccessModifierOffset may be overridden by IndentAccessModifiers,
// in which case we take a negative value of the IndentWidth to simulate
// the upper indent level.
- return Style.IndentAccessModifiers ? -Style.IndentWidth
- : Style.AccessModifierOffset;
+ return Style.IndentAccessModifiers != FormatStyle::IAMS_Never
+ ? -Style.IndentWidth
+ : Style.AccessModifierOffset;
}
return 0;
}
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 0e0825112b513..11c70db50435e 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -380,15 +380,15 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace,
auto ParseDefault = [this, OpeningBrace, IfKind, &IfLBrace, &HasDoWhile,
&HasLabel, &StatementCount,
SeenExplicitAccessModifier] {
- const bool IsQtAccessLabel =
- SeenExplicitAccessModifier && !*SeenExplicitAccessModifier &&
- FormatTok->isOneOf(Keywords.kw_signals, Keywords.kw_qsignals,
- Keywords.kw_slots, Keywords.kw_qslots) &&
- Tokens->peekNextToken(/*SkipComment=*/true)->is(tok::colon);
- if (SeenExplicitAccessModifier && !*SeenExplicitAccessModifier &&
- (FormatTok->isAccessSpecifierKeyword() || IsQtAccessLabel)) {
- ++Line->Level;
- *SeenExplicitAccessModifier = true;
+ if (SeenExplicitAccessModifier && !*SeenExplicitAccessModifier) {
+ const bool IsQtAccessLabel =
+ FormatTok->isOneOf(Keywords.kw_signals, Keywords.kw_qsignals,
+ Keywords.kw_slots, Keywords.kw_qslots) &&
+ Tokens->peekNextToken(/*SkipComment=*/true)->is(tok::colon);
+ if (FormatTok->isAccessSpecifierKeyword() || IsQtAccessLabel) {
+ ++Line->Level;
+ *SeenExplicitAccessModifier = true;
+ }
}
parseStructuralElement(OpeningBrace, IfKind, &IfLBrace,
HasDoWhile ? nullptr : &HasDoWhile,
@@ -894,8 +894,9 @@ FormatToken *UnwrappedLineParser::parseBlock(
size_t PPEndHash = computePPHash();
// Munch the closing brace.
- nextToken(/*LevelDifference=*/
- -static_cast<int>(AddLevels + SeenExplicitAccessModifier));
+ if (SeenExplicitAccessModifier)
+ ++AddLevels;
+ nextToken(/*LevelDifference=*/-static_cast<int>(AddLevels));
// When this is a function block and there is an unnecessary semicolon
// afterwards then mark it as optional (so the RemoveSemi pass can get rid of
@@ -4315,12 +4316,20 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) {
}
const bool IndentAfterExplicitAccessModifier =
- Style.isCpp() && Style.IndentAccessModifiers &&
- !Style.IndentImplicitAccessModifiers;
- unsigned AddLevels =
- Style.IndentAccessModifiers && !IndentAfterExplicitAccessModifier
- ? 2u
- : 1u;
+ Style.isCpp() && Style.IndentAccessModifiers ==
+ FormatStyle::IAMS_AfterFirstAccessModifier;
+ unsigned AddLevels = 1u;
+ switch (Style.IndentAccessModifiers) {
+ case FormatStyle::IAMS_Never:
+ break;
+ case FormatStyle::IAMS_Always:
+ AddLevels = 2u;
+ break;
+ case FormatStyle::IAMS_AfterFirstAccessModifier:
+ // Other languages keep the indentation of the old true setting.
+ AddLevels = IndentAfterExplicitAccessModifier ? 1u : 2u;
+ break;
+ }
parseBlock(/*MustBeDeclaration=*/true, AddLevels, /*MunchSemi=*/false,
/*KeepBraces=*/true, /*IfKind=*/nullptr,
/*UnindentWhitesmithsBraces=*/false,
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index ccbc346901460..c0afbfab9ede2 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -189,8 +189,6 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
CHECK_PARSE_BOOL(DerivePointerAlignment);
CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding");
CHECK_PARSE_BOOL(DisableFormat);
- CHECK_PARSE_BOOL(IndentAccessModifiers);
- CHECK_PARSE_BOOL(IndentImplicitAccessModifiers);
CHECK_PARSE_BOOL(IndentCaseBlocks);
CHECK_PARSE_BOOL(IndentCaseLabels);
CHECK_PARSE_BOOL(IndentExportBlock);
@@ -1030,6 +1028,19 @@ TEST(ConfigParseTest, ParsesConfiguration) {
AllowShortIfStatementsOnASingleLine,
FormatStyle::SIS_WithoutElse);
+ Style.IndentAccessModifiers = FormatStyle::IAMS_Always;
+ CHECK_PARSE("IndentAccessModifiers: Never", IndentAccessModifiers,
+ FormatStyle::IAMS_Never);
+ CHECK_PARSE("IndentAccessModifiers: Always", IndentAccessModifiers,
+ FormatStyle::IAMS_Always);
+ CHECK_PARSE("IndentAccessModifiers: AfterFirstAccessModifier",
+ IndentAccessModifiers,
+ FormatStyle::IAMS_AfterFirstAccessModifier);
+ CHECK_PARSE("IndentAccessModifiers: false", IndentAccessModifiers,
+ FormatStyle::IAMS_Never);
+ CHECK_PARSE("IndentAccessModifiers: true", IndentAccessModifiers,
+ FormatStyle::IAMS_Always);
+
Style.IndentExternBlock = FormatStyle::IEBS_NoIndent;
CHECK_PARSE("IndentExternBlock: AfterExternBlock", IndentExternBlock,
FormatStyle::IEBS_AfterExternBlock);
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 656a352ecb005..e2bb45a7e67d4 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -24839,7 +24839,7 @@ TEST_F(FormatTest, StatementAttributeLikeMacros) {
TEST_F(FormatTest, IndentAccessModifiers) {
FormatStyle Style = getLLVMStyle();
- Style.IndentAccessModifiers = true;
+ Style.IndentAccessModifiers = FormatStyle::IAMS_Always;
// Members are *two* levels below the record;
// Style.IndentWidth == 2, thus yielding a 4 spaces wide indentation.
verifyFormat("class C {\n"
@@ -24920,10 +24920,48 @@ TEST_F(FormatTest, IndentAccessModifiers) {
Style);
}
-TEST_F(FormatTest, IndentImplicitAccessModifiers) {
+TEST_F(FormatTest, IndentAccessModifiersAfterFirst) {
FormatStyle Style = getLLVMStyle();
- Style.IndentAccessModifiers = true;
- Style.IndentImplicitAccessModifiers = false;
+ verifyFormat("struct S {\n"
+ " int before;\n"
+ "\n"
+ "public:\n"
+ " int after;\n"
+ "};",
+ Style);
+
+ Style.IndentAccessModifiers = FormatStyle::IAMS_Always;
+ verifyFormat("struct S {\n"
+ " int before;\n"
+ "\n"
+ " public:\n"
+ " int after;\n"
+ "};",
+ Style);
+ Style.IndentAccessModifiers = FormatStyle::IAMS_AfterFirstAccessModifier;
+ verifyFormat("struct S {\n"
+ " int member;\n"
+ "};",
+ Style);
+ verifyFormat("struct S {\n"
+ " int before;\n"
+ "\n"
+ " public:\n"
+ " int after;\n"
+ "};",
+ Style);
+ verifyFormat("struct S {\n"
+ " int before;\n"
+ "\n"
+ " public:\n"
+ " int after;\n"
+ "\n"
+ " private:\n"
+ " int last;\n"
+ "};",
+ Style);
+
+ // Also cover the Allman layout from issue #61631.
Style.IndentWidth = 4;
Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Never;
Style.BreakBeforeBraces = FormatStyle::BS_Allman;
diff --git a/clang/unittests/Format/FormatTestCSharp.cpp b/clang/unittests/Format/FormatTestCSharp.cpp
index 805fe6f9bd3c2..be98501db166f 100644
--- a/clang/unittests/Format/FormatTestCSharp.cpp
+++ b/clang/unittests/Format/FormatTestCSharp.cpp
@@ -124,6 +124,17 @@ TEST_F(FormatTestCSharp, AccessModifiers) {
"}");
}
+TEST_F(FormatTestCSharp, IndentAccessModifiersAfterFirstUsesAlways) {
+ FormatStyle Style = getDefaultStyle();
+ Style.IndentAccessModifiers = FormatStyle::IAMS_AfterFirstAccessModifier;
+ verifyFormat("class C\n"
+ "{\n"
+ " int Before;\n"
+ " public int After;\n"
+ "}",
+ Style);
+}
+
TEST_F(FormatTestCSharp, NoStringLiteralBreaks) {
// Breaking of interpolated strings is not implemented.
auto Style = getDefaultStyle();
diff --git a/clang/unittests/Format/FormatTestJava.cpp b/clang/unittests/Format/FormatTestJava.cpp
index 86e4b2fc361b2..53168ef6a19e1 100644
--- a/clang/unittests/Format/FormatTestJava.cpp
+++ b/clang/unittests/Format/FormatTestJava.cpp
@@ -28,11 +28,10 @@ class FormatTestJava : public test::FormatTestBase {
}
};
-TEST_F(FormatTestJava, IndentImplicitAccessModifiersDoesNotAffectJava) {
+TEST_F(FormatTestJava, IndentAccessModifiersAfterFirstDoesNotAffectJava) {
FormatStyle Style = getDefaultStyle();
Style.IndentWidth = 4;
- Style.IndentAccessModifiers = true;
- Style.IndentImplicitAccessModifiers = false;
+ Style.IndentAccessModifiers = FormatStyle::IAMS_AfterFirstAccessModifier;
verifyFormat("class C {\n"
" int before;\n"
" public int after;\n"
>From b27ce0fd6f1dc72cfc0474155ba3de3f1af3cd72 Mon Sep 17 00:00:00 2001
From: Noam Bouillet <noam.bouillet at epitech.eu>
Date: Thu, 1 Oct 2026 18:28:18 +0200
Subject: [PATCH 3/5] test(format): isolate access indent style cases
Keep the plain LLVM style checks focused on the new option. Test the Allman reproduction and Whitesmiths behavior separately.
---
clang/unittests/Format/FormatTest.cpp | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index e2bb45a7e67d4..358fb03bf4141 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -24960,8 +24960,11 @@ TEST_F(FormatTest, IndentAccessModifiersAfterFirst) {
" int last;\n"
"};",
Style);
+}
- // Also cover the Allman layout from issue #61631.
+TEST_F(FormatTest, IndentAccessModifiersAfterFirstAllman) {
+ FormatStyle Style = getLLVMStyle();
+ Style.IndentAccessModifiers = FormatStyle::IAMS_AfterFirstAccessModifier;
Style.IndentWidth = 4;
Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Never;
Style.BreakBeforeBraces = FormatStyle::BS_Allman;
@@ -25000,7 +25003,13 @@ TEST_F(FormatTest, IndentAccessModifiersAfterFirst) {
" void changed();\n"
"};",
Style);
+}
+TEST_F(FormatTest, IndentAccessModifiersAfterFirstWhitesmiths) {
+ FormatStyle Style = getLLVMStyle();
+ Style.IndentAccessModifiers = FormatStyle::IAMS_AfterFirstAccessModifier;
+ Style.IndentWidth = 4;
+ Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Never;
Style.BreakBeforeBraces = FormatStyle::BS_Whitesmiths;
verifyFormat("struct S\n"
" {\n"
>From 2175d322497f3bf201af6b25b7d3855eac4555f7 Mon Sep 17 00:00:00 2001
From: Noam Bouillet <noam.bouillet at epitech.eu>
Date: Fri, 2 Oct 2026 00:30:26 +0200
Subject: [PATCH 4/5] refactor(format): address access indent review
---
clang/docs/ClangFormatStyleOptions.md | 72 ++++++++++--------------
clang/include/clang/Format/Format.h | 66 +++++++++-------------
clang/lib/Format/Format.cpp | 2 +
clang/lib/Format/UnwrappedLineParser.cpp | 14 +++--
4 files changed, 65 insertions(+), 89 deletions(-)
diff --git a/clang/docs/ClangFormatStyleOptions.md b/clang/docs/ClangFormatStyleOptions.md
index 5aabda184da0f..e982c26929ccc 100644
--- a/clang/docs/ClangFormatStyleOptions.md
+++ b/clang/docs/ClangFormatStyleOptions.md
@@ -4748,61 +4748,47 @@ the configuration (without a prefix: `Auto`).
: Specify how access modifiers and record members are indented.
- `Never` (also `false` in configuration) uses `AccessModifierOffset` for
- access modifiers and indents members one level below the record. `Always`
- (also `true` in configuration) gives access modifiers their own
- indentation level and indents all members two levels below the record,
- even if there is no explicit access modifier.
- `AfterFirstAccessModifier` gives members one level before the first
- explicit access modifier and two levels afterwards. If there is no
- explicit access modifier, members stay at one level. This behavior applies
- to C, C++, and Objective-C; other languages treat it like `Always`.
- `AccessModifierOffset` is ignored by `Always` and
- `AfterFirstAccessModifier`.
-
- ```c++
- Never (false): Always (true):
- class C { vs. class C {
- class D { class D {
- void bar(); void bar();
- protected: protected:
- D(); D();
- }; };
- public: public:
- C(); C();
- }; };
- void foo() { void foo() {
- return 1; return 1;
- } }
- ```
-
- ```yaml
- IndentAccessModifiers: AfterFirstAccessModifier
- ```
-
- ```c++
- struct S {
- int before;
-
- public:
- int after;
- };
- ```
-
Possible values:
- `IAMS_Never` (in configuration: `Never`)
Use `AccessModifierOffset` for access modifiers and indent members one
level below the record.
+ ```c++
+ struct S {
+ int before;
+
+ public:
+ int after;
+ };
+ ```
+
- `IAMS_Always` (in configuration: `Always`)
Give access modifiers their own indentation level and indent all
- members two levels below the record.
+ members two levels below the record. Ignore `AccessModifierOffset`.
+
+ ```c++
+ struct S {
+ int before;
+
+ public:
+ int after;
+ };
+ ```
- `IAMS_AfterFirstAccessModifier` (in configuration: `AfterFirstAccessModifier`)
In C, C++, and Objective-C, indent members one level until the first
explicit access modifier, then two levels. Other languages use the
- `Always` behavior. Available since clang-format 24.
+ `Always` behavior. Ignore `AccessModifierOffset`.
+
+ ```c++
+ struct S {
+ int before;
+
+ public:
+ int after;
+ };
+ ```
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index e6c8aa1f90604..a67a8596e661c 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3185,55 +3185,41 @@ struct FormatStyle {
enum IndentAccessModifierStyle : int8_t {
/// Use `AccessModifierOffset` for access modifiers and indent members one
/// level below the record.
+ /// \code
+ /// struct S {
+ /// int before;
+ ///
+ /// public:
+ /// int after;
+ /// };
+ /// \endcode
IAMS_Never,
/// Give access modifiers their own indentation level and indent all
- /// members two levels below the record.
+ /// members two levels below the record. Ignore `AccessModifierOffset`.
+ /// \code
+ /// struct S {
+ /// int before;
+ ///
+ /// public:
+ /// int after;
+ /// };
+ /// \endcode
IAMS_Always,
/// In C, C++, and Objective-C, indent members one level until the first
/// explicit access modifier, then two levels. Other languages use the
- /// `Always` behavior. Available since clang-format 24.
+ /// `Always` behavior. Ignore `AccessModifierOffset`.
+ /// \code
+ /// struct S {
+ /// int before;
+ ///
+ /// public:
+ /// int after;
+ /// };
+ /// \endcode
IAMS_AfterFirstAccessModifier,
};
/// Specify how access modifiers and record members are indented.
- ///
- /// `Never` (also `false` in configuration) uses `AccessModifierOffset` for
- /// access modifiers and indents members one level below the record. `Always`
- /// (also `true` in configuration) gives access modifiers their own
- /// indentation level and indents all members two levels below the record,
- /// even if there is no explicit access modifier.
- /// `AfterFirstAccessModifier` gives members one level before the first
- /// explicit access modifier and two levels afterwards. If there is no
- /// explicit access modifier, members stay at one level. This behavior applies
- /// to C, C++, and Objective-C; other languages treat it like `Always`.
- /// `AccessModifierOffset` is ignored by `Always` and
- /// `AfterFirstAccessModifier`.
- /// \code
- /// Never (false): Always (true):
- /// class C { vs. class C {
- /// class D { class D {
- /// void bar(); void bar();
- /// protected: protected:
- /// D(); D();
- /// }; };
- /// public: public:
- /// C(); C();
- /// }; };
- /// void foo() { void foo() {
- /// return 1; return 1;
- /// } }
- /// \endcode
- /// \code{.yaml}
- /// IndentAccessModifiers: AfterFirstAccessModifier
- /// \endcode
- /// \code
- /// struct S {
- /// int before;
- ///
- /// public:
- /// int after;
- /// };
- /// \endcode
/// \version 13
IndentAccessModifierStyle IndentAccessModifiers;
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index bdb15326a1603..e1b7f7804b5c9 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -485,6 +485,8 @@ struct ScalarEnumerationTraits<FormatStyle::IndentAccessModifierStyle> {
IO.enumCase(Value, "Always", FormatStyle::IAMS_Always);
IO.enumCase(Value, "AfterFirstAccessModifier",
FormatStyle::IAMS_AfterFirstAccessModifier);
+
+ // For backward compatibility.
IO.enumCase(Value, "false", FormatStyle::IAMS_Never);
IO.enumCase(Value, "true", FormatStyle::IAMS_Always);
}
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 11c70db50435e..9e81efc286a5e 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -896,7 +896,7 @@ FormatToken *UnwrappedLineParser::parseBlock(
// Munch the closing brace.
if (SeenExplicitAccessModifier)
++AddLevels;
- nextToken(/*LevelDifference=*/-static_cast<int>(AddLevels));
+ nextToken(/*LevelDifference=*/-AddLevels);
// When this is a function block and there is an unnecessary semicolon
// afterwards then mark it as optional (so the RemoveSemi pass can get rid of
@@ -4315,9 +4315,7 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) {
addUnwrappedLine();
}
- const bool IndentAfterExplicitAccessModifier =
- Style.isCpp() && Style.IndentAccessModifiers ==
- FormatStyle::IAMS_AfterFirstAccessModifier;
+ bool IndentAfterExplicitAccessModifier = false;
unsigned AddLevels = 1u;
switch (Style.IndentAccessModifiers) {
case FormatStyle::IAMS_Never:
@@ -4326,8 +4324,12 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) {
AddLevels = 2u;
break;
case FormatStyle::IAMS_AfterFirstAccessModifier:
- // Other languages keep the indentation of the old true setting.
- AddLevels = IndentAfterExplicitAccessModifier ? 1u : 2u;
+ if (Style.isCpp()) {
+ IndentAfterExplicitAccessModifier = true;
+ } else {
+ // Other languages use the same indentation as IAMS_Always.
+ AddLevels = 2u;
+ }
break;
}
parseBlock(/*MustBeDeclaration=*/true, AddLevels, /*MunchSemi=*/false,
>From 58173480d3e9c4bb13d007e06057e33bef2b3d1c Mon Sep 17 00:00:00 2001
From: Noam Bouillet <noam.bouillet at epitech.eu>
Date: Sat, 3 Oct 2026 00:02:41 +0200
Subject: [PATCH 5/5] refactor(format): simplify access indent handling
---
clang/docs/ClangFormatStyleOptions.md | 5 +++--
clang/include/clang/Format/Format.h | 5 +++--
clang/lib/Format/UnwrappedLineParser.cpp | 13 ++++++-------
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/clang/docs/ClangFormatStyleOptions.md b/clang/docs/ClangFormatStyleOptions.md
index e982c26929ccc..6ff29082c8a7a 100644
--- a/clang/docs/ClangFormatStyleOptions.md
+++ b/clang/docs/ClangFormatStyleOptions.md
@@ -4765,7 +4765,8 @@ the configuration (without a prefix: `Auto`).
- `IAMS_Always` (in configuration: `Always`)
Give access modifiers their own indentation level and indent all
- members two levels below the record. Ignore `AccessModifierOffset`.
+ members two levels below the record. Value of the `AccessModifierOffset`
+ is ignored.
```c++
struct S {
@@ -4779,7 +4780,7 @@ the configuration (without a prefix: `Auto`).
- `IAMS_AfterFirstAccessModifier` (in configuration: `AfterFirstAccessModifier`)
In C, C++, and Objective-C, indent members one level until the first
explicit access modifier, then two levels. Other languages use the
- `Always` behavior. Ignore `AccessModifierOffset`.
+ `Always` behavior. Value of the `AccessModifierOffset` is ignored.
```c++
struct S {
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index a67a8596e661c..036353752e831 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3195,7 +3195,8 @@ struct FormatStyle {
/// \endcode
IAMS_Never,
/// Give access modifiers their own indentation level and indent all
- /// members two levels below the record. Ignore `AccessModifierOffset`.
+ /// members two levels below the record. Value of the `AccessModifierOffset`
+ /// is ignored.
/// \code
/// struct S {
/// int before;
@@ -3207,7 +3208,7 @@ struct FormatStyle {
IAMS_Always,
/// In C, C++, and Objective-C, indent members one level until the first
/// explicit access modifier, then two levels. Other languages use the
- /// `Always` behavior. Ignore `AccessModifierOffset`.
+ /// `Always` behavior. Value of the `AccessModifierOffset` is ignored.
/// \code
/// struct S {
/// int before;
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 9e81efc286a5e..901335950b9fa 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -893,9 +893,9 @@ FormatToken *UnwrappedLineParser::parseBlock(
size_t PPEndHash = computePPHash();
- // Munch the closing brace.
if (SeenExplicitAccessModifier)
++AddLevels;
+ // Munch the closing brace.
nextToken(/*LevelDifference=*/-AddLevels);
// When this is a function block and there is an unnecessary semicolon
@@ -4320,16 +4320,15 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) {
switch (Style.IndentAccessModifiers) {
case FormatStyle::IAMS_Never:
break;
- case FormatStyle::IAMS_Always:
- AddLevels = 2u;
- break;
case FormatStyle::IAMS_AfterFirstAccessModifier:
if (Style.isCpp()) {
IndentAfterExplicitAccessModifier = true;
- } else {
- // Other languages use the same indentation as IAMS_Always.
- AddLevels = 2u;
+ break;
}
+ // Other languages use the same indentation as IAMS_Always.
+ [[fallthrough]];
+ case FormatStyle::IAMS_Always:
+ AddLevels = 2u;
break;
}
parseBlock(/*MustBeDeclaration=*/true, AddLevels, /*MunchSemi=*/false,
More information about the cfe-commits
mailing list