[clang-format] Add AllowBreakBeforeQtProperty option (#159909)

The test cases are adapted from #131605.
This commit is contained in:
owenca
2025-09-22 00:07:14 -07:00
committed by GitHub
parent 322b990267
commit 583256d165
10 changed files with 100 additions and 2 deletions

View File

@@ -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>`

View File

@@ -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
--------

View File

@@ -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 &&

View File

@@ -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;

View File

@@ -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", "ptrdiff_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 !=

View File

@@ -136,6 +136,7 @@ namespace format {
TYPE(PointerOrReference) \
TYPE(ProtoExtensionLSquare) \
TYPE(PureVirtualSpecifier) \
TYPE(QtProperty) \
TYPE(RangeBasedForLoopColon) \
TYPE(RecordLBrace) \
TYPE(RecordRBrace) \
@@ -703,6 +704,7 @@ public:
isAttribute();
}
[[nodiscard]] bool isQtProperty() const;
[[nodiscard]] bool isTypeName(const LangOptions &LangOpts) const;
[[nodiscard]] bool isTypeOrIdentifier(const LangOptions &LangOpts) const;

View File

@@ -384,6 +384,10 @@ private:
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 @@ private:
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 @@ private:
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))

View File

@@ -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);

View File

@@ -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

View File

@@ -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