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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
