https://github.com/owenca updated 
https://github.com/llvm/llvm-project/pull/159909

>From 813bbca2a229dfcf381e4795775a82d7ec6a1ff2 Mon Sep 17 00:00:00 2001
From: Owen Pan <[email protected]>
Date: Fri, 19 Sep 2025 22:40:35 -0700
Subject: [PATCH 1/2] [clang-format] Add AllowBreakBeforeQtProperty option

Test cases are adpated from #131605.
---
 clang/docs/ClangFormatStyleOptions.rst        |  7 +++++
 clang/docs/ReleaseNotes.rst                   |  1 +
 clang/include/clang/Format/Format.h           |  7 +++++
 clang/lib/Format/Format.cpp                   |  3 ++
 clang/lib/Format/FormatToken.cpp              | 14 +++++++++
 clang/lib/Format/FormatToken.h                |  2 ++
 clang/lib/Format/TokenAnnotator.cpp           | 12 +++++++-
 clang/unittests/Format/ConfigParseTest.cpp    |  1 +
 clang/unittests/Format/FormatTest.cpp         | 30 +++++++++++++++++++
 clang/unittests/Format/TokenAnnotatorTest.cpp | 23 ++++++++++++++
 10 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ClangFormatStyleOptions.rst 
b/clang/docs/ClangFormatStyleOptions.rst
index 9413b9a348b76..b746df5dab264 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -1795,6 +1795,13 @@ the configuration (without a prefix: ``Auto``).
 
 
 
+.. _AllowBreakBeforeQtProperty:
+
+**AllowBreakBeforeQtProperty** (``Boolean``) :versionbadge:`clang-format 22` 
:ref:`¶ <AllowBreakBeforeQtProperty>`
+  Allow breaking before ``Q_Property`` keywords ``READ``, ``WRITE``, etc. as
+  if they were preceded by a comma (``,``). This allows them to be formatted
+  according to ``BinPackParameters``.
+
 .. _AllowShortBlocksOnASingleLine:
 
 **AllowShortBlocksOnASingleLine** (``ShortBlockStyle``) 
:versionbadge:`clang-format 3.5` :ref:`¶ <AllowShortBlocksOnASingleLine>`
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 46d56bb3f07f5..6daf2ab23bbf2 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -530,6 +530,7 @@ clang-format
 - Add ``NumericLiteralCase`` option for enforcing character case in numeric
   literals.
 - Add ``Leave`` suboption to ``IndentPPDirectives``.
+- Add ``AllowBreakBeforeQtProperty`` option.
 
 libclang
 --------
diff --git a/clang/include/clang/Format/Format.h 
b/clang/include/clang/Format/Format.h
index 342fefcfc408c..3df5b92654094 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -732,6 +732,12 @@ struct FormatStyle {
   /// \version 18
   BreakBeforeNoexceptSpecifierStyle AllowBreakBeforeNoexceptSpecifier;
 
+  /// Allow breaking before ``Q_Property`` keywords ``READ``, ``WRITE``, etc. 
as
+  /// if they were preceded by a comma (``,``). This allows them to be 
formatted
+  /// according to ``BinPackParameters``.
+  /// \version 22
+  bool AllowBreakBeforeQtProperty;
+
   /// Different styles for merging short blocks containing at most one
   /// statement.
   enum ShortBlockStyle : int8_t {
@@ -5458,6 +5464,7 @@ struct FormatStyle {
                R.AllowAllParametersOfDeclarationOnNextLine &&
            AllowBreakBeforeNoexceptSpecifier ==
                R.AllowBreakBeforeNoexceptSpecifier &&
+           AllowBreakBeforeQtProperty == R.AllowBreakBeforeQtProperty &&
            AllowShortBlocksOnASingleLine == R.AllowShortBlocksOnASingleLine &&
            AllowShortCaseExpressionOnASingleLine ==
                R.AllowShortCaseExpressionOnASingleLine &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 24a36d97a6fa9..b38f2810c0a74 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1028,6 +1028,8 @@ template <> struct MappingTraits<FormatStyle> {
                    Style.AllowAllParametersOfDeclarationOnNextLine);
     IO.mapOptional("AllowBreakBeforeNoexceptSpecifier",
                    Style.AllowBreakBeforeNoexceptSpecifier);
+    IO.mapOptional("AllowBreakBeforeQtProperty",
+                   Style.AllowBreakBeforeQtProperty);
     IO.mapOptional("AllowShortBlocksOnASingleLine",
                    Style.AllowShortBlocksOnASingleLine);
     IO.mapOptional("AllowShortCaseExpressionOnASingleLine",
@@ -1567,6 +1569,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind 
Language) {
   LLVMStyle.AllowAllArgumentsOnNextLine = true;
   LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true;
   LLVMStyle.AllowBreakBeforeNoexceptSpecifier = FormatStyle::BBNSS_Never;
+  LLVMStyle.AllowBreakBeforeQtProperty = false;
   LLVMStyle.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never;
   LLVMStyle.AllowShortCaseExpressionOnASingleLine = true;
   LLVMStyle.AllowShortCaseLabelsOnASingleLine = false;
diff --git a/clang/lib/Format/FormatToken.cpp b/clang/lib/Format/FormatToken.cpp
index 9c98b7819e61a..8da50bc885c34 100644
--- a/clang/lib/Format/FormatToken.cpp
+++ b/clang/lib/Format/FormatToken.cpp
@@ -33,6 +33,18 @@ const char *getTokenTypeName(TokenType Type) {
   return nullptr;
 }
 
+static SmallVector<StringRef> QtPropertyKeywords = {
+    "BINDABLE",   "CONSTANT", "DESIGNABLE", "FINAL", "MEMBER",
+    "NOTIFY",     "READ",     "REQUIRED",   "RESET", "REVISION",
+    "SCRIPTABLE", "STORED",   "USER",       "WRITE",
+};
+
+bool FormatToken::isQtProperty() const {
+  assert(llvm::is_sorted(QtPropertyKeywords));
+  return std::binary_search(QtPropertyKeywords.begin(),
+                            QtPropertyKeywords.end(), TokenText);
+}
+
 // Sorted common C++ non-keyword types.
 static SmallVector<StringRef> CppNonKeywordTypes = {
     "clock_t",  "int16_t",   "int32_t", "int64_t",   "int8_t",
@@ -330,6 +342,8 @@ bool startsNextParameter(const FormatToken &Current, const 
FormatStyle &Style) {
   }
   if (Style.Language == FormatStyle::LK_Proto && Current.is(TT_SelectorName))
     return true;
+  if (Current.is(TT_QtProperty))
+    return true;
   return Previous.is(tok::comma) && !Current.isTrailingComment() &&
          ((Previous.isNot(TT_CtorInitializerComma) ||
            Style.BreakConstructorInitializers !=
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index 9252a795a0b5e..e04b0e7af10c0 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -136,6 +136,7 @@ namespace format {
   TYPE(PointerOrReference)                                                     
\
   TYPE(ProtoExtensionLSquare)                                                  
\
   TYPE(PureVirtualSpecifier)                                                   
\
+  TYPE(QtProperty)                                                             
\
   TYPE(RangeBasedForLoopColon)                                                 
\
   TYPE(RecordLBrace)                                                           
\
   TYPE(RecordRBrace)                                                           
\
@@ -703,6 +704,7 @@ struct FormatToken {
            isAttribute();
   }
 
+  [[nodiscard]] bool isQtProperty() const;
   [[nodiscard]] bool isTypeName(const LangOptions &LangOpts) const;
   [[nodiscard]] bool isTypeOrIdentifier(const LangOptions &LangOpts) const;
 
diff --git a/clang/lib/Format/TokenAnnotator.cpp 
b/clang/lib/Format/TokenAnnotator.cpp
index d97f56751ea69..4bfb803ebedf7 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -384,6 +384,10 @@ class AnnotatingParser {
                OpeningParen.Previous->is(tok::kw__Generic)) {
       Contexts.back().ContextType = Context::C11GenericSelection;
       Contexts.back().IsExpression = true;
+    } else if (OpeningParen.Previous &&
+               OpeningParen.Previous->TokenText == "Q_PROPERTY") {
+      Contexts.back().ContextType = Context::QtProperty;
+      Contexts.back().IsExpression = false;
     } else if (Line.InPPDirective &&
                (!OpeningParen.Previous ||
                 OpeningParen.Previous->isNot(tok::identifier))) {
@@ -1803,6 +1807,11 @@ class AnnotatingParser {
             return false;
         }
       }
+      if (Style.AllowBreakBeforeQtProperty &&
+          Contexts.back().ContextType == Context::QtProperty &&
+          Tok->isQtProperty()) {
+        Tok->setFinalizedType(TT_QtProperty);
+      }
       break;
     case tok::arrow:
       if (Tok->isNot(TT_LambdaArrow) && Prev && Prev->is(tok::kw_noexcept))
@@ -2169,6 +2178,7 @@ class AnnotatingParser {
       TemplateArgument,
       // C11 _Generic selection.
       C11GenericSelection,
+      QtProperty,
       // Like in the outer parentheses in `ffnand ff1(.q());`.
       VerilogInstancePortList,
     } ContextType = Unknown;
@@ -6254,7 +6264,7 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine 
&Line,
               Right.Next->isOneOf(TT_FunctionDeclarationName, tok::kw_const)));
   }
   if (Right.isOneOf(TT_StartOfName, TT_FunctionDeclarationName,
-                    TT_ClassHeadName, tok::kw_operator)) {
+                    TT_ClassHeadName, TT_QtProperty, tok::kw_operator)) {
     return true;
   }
   if (Left.is(TT_PointerOrReference))
diff --git a/clang/unittests/Format/ConfigParseTest.cpp 
b/clang/unittests/Format/ConfigParseTest.cpp
index 7c993c0f8fd33..bb4d38bb741ec 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -161,6 +161,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
   Style.Language = FormatStyle::LK_Cpp;
   CHECK_PARSE_BOOL(AllowAllArgumentsOnNextLine);
   CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine);
+  CHECK_PARSE_BOOL(AllowBreakBeforeQtProperty);
   CHECK_PARSE_BOOL(AllowShortCaseExpressionOnASingleLine);
   CHECK_PARSE_BOOL(AllowShortCaseLabelsOnASingleLine);
   CHECK_PARSE_BOOL(AllowShortCompoundRequirementOnASingleLine);
diff --git a/clang/unittests/Format/FormatTest.cpp 
b/clang/unittests/Format/FormatTest.cpp
index d9db06667d802..7d550143be5df 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -28772,6 +28772,36 @@ TEST_F(FormatTest, BreakBeforeClassName) {
                "    ArenaSafeUniquePtr {};");
 }
 
+TEST_F(FormatTest, KeywordedFunctionLikeMacros) {
+  constexpr StringRef Code("Q_PROPERTY(int name\n"
+                           "           READ name\n"
+                           "           WRITE setName\n"
+                           "           NOTIFY nameChanged)");
+  constexpr StringRef Code2("class A {\n"
+                            "  Q_PROPERTY(int name\n"
+                            "             READ name\n"
+                            "             WRITE setName\n"
+                            "             NOTIFY nameChanged)\n"
+                            "};");
+
+  auto Style = getLLVMStyle();
+  Style.AllowBreakBeforeQtProperty = true;
+
+  Style.BinPackParameters = FormatStyle::BPPS_AlwaysOnePerLine;
+  verifyFormat(Code, Style);
+  verifyFormat(Code2, Style);
+
+  Style.BinPackParameters = FormatStyle::BPPS_OnePerLine;
+  Style.ColumnLimit = 40;
+  verifyFormat(Code, Style);
+  verifyFormat(Code2, Style);
+  verifyFormat("/* sdf */ Q_PROPERTY(int name\n"
+               "                     READ name\n"
+               "                     WRITE setName\n"
+               "                     NOTIFY nameChanged)",
+               Style);
+}
+
 } // namespace
 } // namespace test
 } // namespace format
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp 
b/clang/unittests/Format/TokenAnnotatorTest.cpp
index f6435f13f0791..4c43a963632a6 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -4159,6 +4159,29 @@ TEST_F(TokenAnnotatorTest, LineCommentTrailingBackslash) 
{
   EXPECT_TOKEN(Tokens[1], tok::comment, TT_LineComment);
 }
 
+TEST_F(TokenAnnotatorTest, KeywordedFunctionLikeMacro) {
+  auto Style = getLLVMStyle();
+  Style.AllowBreakBeforeQtProperty = true;
+
+  auto Tokens = annotate(
+      "Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)",
+      Style);
+  ASSERT_EQ(Tokens.size(), 12u) << Tokens;
+  EXPECT_TOKEN(Tokens[4], tok::identifier, TT_QtProperty);
+  EXPECT_TOKEN(Tokens[6], tok::identifier, TT_QtProperty);
+  EXPECT_TOKEN(Tokens[8], tok::identifier, TT_QtProperty);
+
+  Tokens = annotate(
+      "struct S {\n"
+      "  Q_OBJECT Q_PROPERTY(int value READ value WRITE setValue NOTIFY foo)\n"
+      "};",
+      Style);
+  ASSERT_EQ(Tokens.size(), 18u) << Tokens;
+  EXPECT_TOKEN(Tokens[8], tok::identifier, TT_QtProperty);
+  EXPECT_TOKEN(Tokens[10], tok::identifier, TT_QtProperty);
+  EXPECT_TOKEN(Tokens[12], tok::identifier, TT_QtProperty);
+}
+
 } // namespace
 } // namespace format
 } // namespace clang

>From 60fbefcf12e17830fefbaf750bc1aa4e0f1de0e8 Mon Sep 17 00:00:00 2001
From: Owen Pan <[email protected]>
Date: Sun, 21 Sep 2025 15:30:26 -0700
Subject: [PATCH 2/2] Use `constexpr std::array` for `QtPropertyKeywords` and
 `CppNonKeywordTypes`

---
 clang/lib/Format/FormatToken.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Format/FormatToken.cpp b/clang/lib/Format/FormatToken.cpp
index 8da50bc885c34..c60ae8f0d2852 100644
--- a/clang/lib/Format/FormatToken.cpp
+++ b/clang/lib/Format/FormatToken.cpp
@@ -33,7 +33,7 @@ const char *getTokenTypeName(TokenType Type) {
   return nullptr;
 }
 
-static SmallVector<StringRef> QtPropertyKeywords = {
+static constexpr std::array<StringRef, 14> QtPropertyKeywords = {
     "BINDABLE",   "CONSTANT", "DESIGNABLE", "FINAL", "MEMBER",
     "NOTIFY",     "READ",     "REQUIRED",   "RESET", "REVISION",
     "SCRIPTABLE", "STORED",   "USER",       "WRITE",
@@ -46,7 +46,7 @@ bool FormatToken::isQtProperty() const {
 }
 
 // Sorted common C++ non-keyword types.
-static SmallVector<StringRef> CppNonKeywordTypes = {
+static constexpr std::array<StringRef, 14> CppNonKeywordTypes = {
     "clock_t",  "int16_t",   "int32_t", "int64_t",   "int8_t",
     "intptr_t", "ptrdiff_t", "size_t",  "time_t",    "uint16_t",
     "uint32_t", "uint64_t",  "uint8_t", "uintptr_t",

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

Reply via email to