diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index fe8dfa406bc32..e58023ccc1274 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -7503,6 +7503,16 @@ the configuration (without a prefix: ``Auto``). ``SpacesInParensOptions`` to ``true`` except for ``InCStyleCasts`` and ``InEmptyParentheses``. +.. _SpacesInSplicers: + +**SpacesInSplicers** (``Boolean``) :versionbadge:`clang-format 23` :ref:`¶ ` + If ``true``, spaces will be inserted after ``[:`` and before ``:]``. + + .. code-block:: c++ + + true: false: + [: ^^int :] i; vs. [:^^int:] i; + .. _SpacesInSquareBrackets: **SpacesInSquareBrackets** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`¶ ` diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 03362cf4e0f8a..3e9ca8a24316b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -691,6 +691,7 @@ clang-format - Extend ``BreakBinaryOperations`` to accept a structured configuration with per-operator break rules and minimum chain length gating via ``PerOperator``. - Add ``AllowShortRecordOnASingleLine`` option and set it to ``EmptyAndAttached`` for LLVM style. +- Add initial support for C++26 reflection, including the new option ``SpacesInSplicers``. libclang -------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index f89323c71713b..d567da3215019 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -5734,6 +5734,14 @@ struct FormatStyle { /// \version 17 SpacesInParensCustom SpacesInParensOptions; + /// If ``true``, spaces will be inserted after ``[:`` and before ``:]``. + /// \code + /// true: false: + /// [: ^^int :] i; vs. [:^^int:] i; + /// \endcode + /// \version 23 + bool SpacesInSplicers; + /// If ``true``, spaces will be inserted after ``[`` and before ``]``. /// Lambdas without arguments or unspecified size array declarations will not /// be affected. @@ -6201,6 +6209,7 @@ struct FormatStyle { R.SpacesInLineCommentPrefix.Maximum && SpacesInParens == R.SpacesInParens && SpacesInParensOptions == R.SpacesInParensOptions && + SpacesInSplicers == R.SpacesInSplicers && SpacesInSquareBrackets == R.SpacesInSquareBrackets && Standard == R.Standard && StatementAttributeLikeMacros == R.StatementAttributeLikeMacros && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 8d447177a18a7..e01076d379a6d 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1450,6 +1450,7 @@ template <> struct MappingTraits { Style.SpacesInLineCommentPrefix); IO.mapOptional("SpacesInParens", Style.SpacesInParens); IO.mapOptional("SpacesInParensOptions", Style.SpacesInParensOptions); + IO.mapOptional("SpacesInSplicers", Style.SpacesInSplicers); IO.mapOptional("SpacesInSquareBrackets", Style.SpacesInSquareBrackets); IO.mapOptional("Standard", Style.Standard); IO.mapOptional("StatementAttributeLikeMacros", @@ -1970,6 +1971,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.SpacesInLineCommentPrefix = { /*Minimum=*/1, /*Maximum=*/std::numeric_limits::max()}; LLVMStyle.SpacesInParens = FormatStyle::SIPO_Never; + LLVMStyle.SpacesInSplicers = false; LLVMStyle.SpacesInSquareBrackets = false; LLVMStyle.Standard = FormatStyle::LS_Latest; LLVMStyle.StatementAttributeLikeMacros.push_back("Q_EMIT"); @@ -4405,12 +4407,11 @@ tooling::Replacements sortUsingDeclarations(const FormatStyle &Style, LangOptions getFormattingLangOpts(const FormatStyle &Style) { LangOptions LangOpts; - auto LexingStd = Style.Standard; - if (LexingStd == FormatStyle::LS_Auto || LexingStd == FormatStyle::LS_Latest) - LexingStd = FormatStyle::LS_Cpp20; + const auto LexingStd = Style.Standard; const bool SinceCpp11 = LexingStd >= FormatStyle::LS_Cpp11; const bool SinceCpp20 = LexingStd >= FormatStyle::LS_Cpp20; + const bool SinceCpp26 = LexingStd >= FormatStyle::LS_Cpp26; switch (Style.Language) { case FormatStyle::LK_C: @@ -4425,7 +4426,7 @@ LangOptions getFormattingLangOpts(const FormatStyle &Style) { LangOpts.CPlusPlus17 = LexingStd >= FormatStyle::LS_Cpp17; LangOpts.CPlusPlus20 = SinceCpp20; LangOpts.CPlusPlus23 = LexingStd >= FormatStyle::LS_Cpp23; - LangOpts.CPlusPlus26 = LexingStd >= FormatStyle::LS_Cpp26; + LangOpts.CPlusPlus26 = SinceCpp26; [[fallthrough]]; default: LangOpts.CPlusPlus = 1; @@ -4437,6 +4438,7 @@ LangOptions getFormattingLangOpts(const FormatStyle &Style) { // the sequence "<::" will be unconditionally treated as "[:". // Cf. Lexer::LexTokenInternal. LangOpts.Digraphs = SinceCpp11; + LangOpts.Reflection = SinceCpp26; LangOpts.LineComment = 1; LangOpts.Bool = 1; diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 68d94b087136d..9ce7c5d7bc6a8 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -150,6 +150,7 @@ namespace format { TYPE(RangeBasedForLoopColon) \ TYPE(RecordLBrace) \ TYPE(RecordRBrace) \ + TYPE(ReflectionOperator) \ TYPE(RegexLiteral) \ TYPE(RequiresClause) \ TYPE(RequiresClauseInARequiresExpression) \ @@ -160,6 +161,8 @@ namespace format { * field name in the C++ struct literal. Also the method or parameter name \ * in the Objective-C method declaration or call. */ \ TYPE(SelectorName) \ + TYPE(SpliceCloser) \ + TYPE(SpliceOpener) \ TYPE(StartOfName) \ TYPE(StatementAttributeLikeMacro) \ TYPE(StatementMacro) \ diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index bda591ab1027e..4e0fc0aad0b63 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -164,6 +164,9 @@ void FormatTokenLexer::tryMergePreviousTokens() { if (tryMergeForEach()) return; + if (LangOpts.Reflection && tryMergeSplicer()) + return; + if ((Style.Language == FormatStyle::LK_Cpp || Style.Language == FormatStyle::LK_ObjC) && tryMergeUserDefinedLiteral()) { @@ -182,6 +185,7 @@ void FormatTokenLexer::tryMergePreviousTokens() { if (tryMergeTokens(NullishCoalescingOperator, TT_NullCoalescingOperator)) { // Treat like the "||" operator (as opposed to the ternary ?). Tokens.back()->Tok.setKind(tok::pipepipe); + Tokens.back()->overwriteFixedType(TT_NullCoalescingOperator); return; } if (tryMergeTokens(NullPropagatingOperator, TT_NullPropagatingOperator)) { @@ -272,8 +276,9 @@ void FormatTokenLexer::tryMergePreviousTokens() { // already a merged token. if (Tokens.back()->TokenText.size() == 1 && tryMergeTokensAny({{tok::caret, tok::tilde}, {tok::tilde, tok::caret}}, - TT_BinaryOperator)) { + TT_Unknown)) { Tokens.back()->Tok.setKind(tok::caret); + Tokens.back()->overwriteFixedType(TT_Unknown); return; } // Signed shift and distribution weight. @@ -468,7 +473,8 @@ bool FormatTokenLexer::tryMergeNullishCoalescingEqual() { StringRef(NullishCoalescing->TokenText.begin(), Equal->TokenText.end() - NullishCoalescing->TokenText.begin()); NullishCoalescing->ColumnWidth += Equal->ColumnWidth; - NullishCoalescing->setType(TT_NullCoalescingEqual); + NullishCoalescing->overwriteFixedType(TT_NullCoalescingEqual); + NullishCoalescing->setFinalizedType(TT_NullCoalescingEqual); Tokens.erase(Tokens.end() - 1); return true; } @@ -606,6 +612,15 @@ bool FormatTokenLexer::tryMergeUserDefinedLiteral() { return true; } +bool FormatTokenLexer::tryMergeSplicer() { + if (tryMergeTokens({tok::l_square, tok::colon}, TT_SpliceOpener)) + return true; + if (!tryMergeTokens({tok::colon, tok::r_square}, TT_SpliceCloser)) + return false; + Tokens.back()->Tok.setKind(tok::r_square); + return true; +} + bool FormatTokenLexer::tryMergeTokens(ArrayRef Kinds, TokenType NewType) { if (Tokens.size() < Kinds.size()) @@ -637,7 +652,7 @@ bool FormatTokenLexer::tryMergeTokens(size_t Count, TokenType NewType) { First[0]->TokenText = StringRef(First[0]->TokenText.data(), First[0]->TokenText.size() + AddLength); First[0]->ColumnWidth += AddLength; - First[0]->setType(NewType); + First[0]->setFinalizedType(NewType); return true; } diff --git a/clang/lib/Format/FormatTokenLexer.h b/clang/lib/Format/FormatTokenLexer.h index 9f5b735efe1d0..29c6d06a2a7da 100644 --- a/clang/lib/Format/FormatTokenLexer.h +++ b/clang/lib/Format/FormatTokenLexer.h @@ -56,6 +56,7 @@ class FormatTokenLexer { bool tryMergeNullishCoalescingEqual(); bool tryTransformCSharpForEach(); bool tryMergeForEach(); + bool tryMergeSplicer(); // Merge the most recently lexed tokens into a single token if their kinds are // correct. diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 898759cb8ea1b..bea63a3702bc7 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -711,7 +711,8 @@ class AnnotatingParser { bool StartsObjCMethodExpr = !IsCppStructuredBinding && !InsideInlineASM && !CppArrayTemplates && IsCpp && !IsCpp11AttributeSpecifier && !IsCSharpAttributeSpecifier && - Contexts.back().CanBeExpression && Left->isNot(TT_LambdaLSquare) && + Contexts.back().CanBeExpression && + Left->isNoneOf(TT_LambdaLSquare, TT_SpliceOpener) && CurrentToken->isNoneOf(tok::l_brace, tok::r_square) && // Do not consider '[' after a comma inside a braced initializer the // start of an ObjC method expression. In braced initializer lists, @@ -1836,6 +1837,9 @@ class AnnotatingParser { if (Style.isTableGen() && !parseTableGenValue()) return false; break; + case tok::caretcaret: + Tok->setFinalizedType(TT_ReflectionOperator); + break; default: break; } @@ -4902,6 +4906,8 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, LSquareTok.endsSequence(tok::l_square, tok::colon, TT_SelectorName)); }; + if (Left.is(TT_SpliceOpener) || Right.is(TT_SpliceCloser)) + return Style.SpacesInSplicers; if (Left.is(tok::l_square)) { return (Left.is(TT_ArrayInitializerLSquare) && Right.isNot(tok::r_square) && SpaceRequiredForArrayInitializerLSquare(Left, Style)) || @@ -4920,9 +4926,9 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, TT_LambdaLSquare))); } if (Right.is(tok::l_square) && - Right.isNoneOf(TT_ObjCMethodExpr, TT_LambdaLSquare, - TT_DesignatedInitializerLSquare, - TT_StructuredBindingLSquare, TT_AttributeLSquare) && + Right.isNoneOf( + TT_ObjCMethodExpr, TT_LambdaLSquare, TT_DesignatedInitializerLSquare, + TT_StructuredBindingLSquare, TT_AttributeLSquare, TT_SpliceOpener) && Left.isNoneOf(tok::numeric_constant, TT_DictLiteral) && !(Left.isNot(tok::r_square) && Style.SpaceBeforeSquareBrackets && Right.is(TT_ArraySubscriptLSquare))) { @@ -5093,6 +5099,8 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, // void Fn() const &; return getTokenReferenceAlignment(Right) != FormatStyle::PAS_Left; } + if (Left.is(TT_ReflectionOperator)) + return false; return true; } @@ -6641,10 +6649,11 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, } return Left.isOneOf(tok::comma, tok::coloncolon, tok::semi, tok::l_brace, - tok::kw_class, tok::kw_struct, tok::comment) || + tok::kw_class, tok::kw_struct, tok::comment, + TT_SpliceCloser) || Right.isMemberAccess() || Right.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow, tok::lessless, - tok::colon, tok::l_square, tok::at) || + tok::colon, tok::l_square, tok::at, TT_SpliceOpener) || (Left.is(tok::r_paren) && Right.isOneOf(tok::identifier, tok::kw_const)) || (Left.is(tok::l_paren) && Right.isNot(tok::r_paren)) || diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index 498e44b190ef2..b374813e880d8 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -208,6 +208,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) { CHECK_PARSE_BOOL(RemoveEmptyLinesInUnwrappedLines); CHECK_PARSE_BOOL(RemoveSemicolon); CHECK_PARSE_BOOL(SkipMacroDefinitionBody); + CHECK_PARSE_BOOL(SpacesInSplicers); CHECK_PARSE_BOOL(SpacesInSquareBrackets); CHECK_PARSE_BOOL(SpacesInContainerLiterals); CHECK_PARSE_BOOL(SpaceAfterCStyleCast); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 44c52034f83cb..bac965f7563b9 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -26306,6 +26306,19 @@ TEST_F(FormatTest, LambdaArrowAsTrailingReturnArrow) { verifyNoCrash("void foo()([] consteval -> int {}())"); } +TEST_F(FormatTest, Reflection) { + verifyFormat("typename [:^^int:] i = 42;"); + verifyFormat("obj.[:sub:]"); + verifyFormat("auto foo() { return [:bar:]; }"); + verifyFormat("auto x = ^^Bar;"); + + auto Style = getLLVMStyle(); + Style.SpacesInSplicers = true; + verifyFormat("typename [: ^^int :] i = 42;", Style); + verifyFormat("obj.[: sub :]", Style); + verifyFormat("auto foo() { return [: bar :]; }", Style); +} + } // namespace } // namespace test } // namespace format diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index cf2bd9f8bd76a..172625b715180 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -4505,6 +4505,45 @@ TEST_F(TokenAnnotatorTest, AttributeSquares) { EXPECT_TRUE(Tokens[15]->EndsCppAttributeGroup); } +TEST_F(TokenAnnotatorTest, UnderstandsReflection) { + auto Tokens = annotate("auto x = ^^int;"); + ASSERT_EQ(Tokens.size(), 7u) << Tokens; + EXPECT_TOKEN(Tokens[3], tok::caretcaret, TT_ReflectionOperator); + + Tokens = annotate("auto x = ^^static_cast(&foo);"); + ASSERT_EQ(Tokens.size(), 20u) << Tokens; + EXPECT_TOKEN(Tokens[3], tok::caretcaret, TT_ReflectionOperator); + + Tokens = annotate("[: x :]"); + ASSERT_EQ(Tokens.size(), 4u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::l_square, TT_SpliceOpener); + EXPECT_TOKEN(Tokens[2], tok::r_square, TT_SpliceCloser); + + Tokens = annotate("[: ^^int :]"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::l_square, TT_SpliceOpener); + EXPECT_TOKEN(Tokens[1], tok::caretcaret, TT_ReflectionOperator); + EXPECT_TOKEN(Tokens[3], tok::r_square, TT_SpliceCloser); + + Tokens = annotate("[: ^^&T::member :]"); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::l_square, TT_SpliceOpener); + EXPECT_TOKEN(Tokens[1], tok::caretcaret, TT_ReflectionOperator); + EXPECT_TOKEN(Tokens[6], tok::r_square, TT_SpliceCloser); + + Tokens = annotate("[: func() :]"); + ASSERT_EQ(Tokens.size(), 6u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::l_square, TT_SpliceOpener); + EXPECT_TOKEN(Tokens[4], tok::r_square, TT_SpliceCloser); + + Tokens = annotate("[: condition ? ^^int : ^^double :]"); + ASSERT_EQ(Tokens.size(), 10u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::l_square, TT_SpliceOpener); + EXPECT_TOKEN(Tokens[3], tok::caretcaret, TT_ReflectionOperator); + EXPECT_TOKEN(Tokens[6], tok::caretcaret, TT_ReflectionOperator); + EXPECT_TOKEN(Tokens[8], tok::r_square, TT_SpliceCloser); +} + } // namespace } // namespace format } // namespace clang