[clang] 583256d - [clang-format] Add AllowBreakBeforeQtProperty option (#159909)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 22 00:07:19 PDT 2025
Author: owenca
Date: 2025-09-22T00:07:14-07:00
New Revision: 583256d165cb8dd9efe144866b245a2e5b7d5125
URL: https://github.com/llvm/llvm-project/commit/583256d165cb8dd9efe144866b245a2e5b7d5125
DIFF: https://github.com/llvm/llvm-project/commit/583256d165cb8dd9efe144866b245a2e5b7d5125.diff
LOG: [clang-format] Add AllowBreakBeforeQtProperty option (#159909)
The test cases are adapted from #131605.
Added:
Modified:
clang/docs/ClangFormatStyleOptions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Format/Format.h
clang/lib/Format/Format.cpp
clang/lib/Format/FormatToken.cpp
clang/lib/Format/FormatToken.h
clang/lib/Format/TokenAnnotator.cpp
clang/unittests/Format/ConfigParseTest.cpp
clang/unittests/Format/FormatTest.cpp
clang/unittests/Format/TokenAnnotatorTest.cpp
Removed:
################################################################################
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..c60ae8f0d2852 100644
--- a/clang/lib/Format/FormatToken.cpp
+++ b/clang/lib/Format/FormatToken.cpp
@@ -33,8 +33,20 @@ const char *getTokenTypeName(TokenType Type) {
return nullptr;
}
+static constexpr std::array<StringRef, 14> 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 = {
+static constexpr std::array<StringRef, 14> CppNonKeywordTypes = {
"clock_t", "int16_t", "int32_t", "int64_t", "int8_t",
"intptr_t", "ptr
diff _t", "size_t", "time_t", "uint16_t",
"uint32_t", "uint64_t", "uint8_t", "uintptr_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
More information about the cfe-commits
mailing list