[clang-format] Add first support for reflection#193794
Open
HazardyKnusperkeks wants to merge 1 commit intollvm:mainfrom
Open
[clang-format] Add first support for reflection#193794HazardyKnusperkeks wants to merge 1 commit intollvm:mainfrom
HazardyKnusperkeks wants to merge 1 commit intollvm:mainfrom
Conversation
Member
|
@llvm/pr-subscribers-clang-format Author: Björn Schäpers (HazardyKnusperkeks) ChangesFixes #192040. Full diff: https://github.com/llvm/llvm-project/pull/193794.diff 11 Files Affected:
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:`¶ <SpacesInSplicers>`
+ 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:`¶ <SpacesInSquareBrackets>`
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<FormatStyle> {
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<unsigned>::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<tok::TokenKind> 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<void(*)(int)>(&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
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #192040.