From e021987273bece6e94bc6f43b6b5232de10637c8 Mon Sep 17 00:00:00 2001 From: Marek Kurdej Date: Fri, 18 Feb 2022 18:24:14 +0100 Subject: [PATCH] [clang-format] Avoid inserting space after C++ casts. Fixes https://github.com/llvm/llvm-project/issues/53876. This is a solution for standard C++ casts: const_cast, dynamic_cast, reinterpret_cast, static_cast. A general approach handling all possible casts is not possible without semantic information. Consider the code: ``` static_cast(*function_pointer_variable)(arguments); ``` vs. ``` some_return_type (*function_pointer_variable)(parameters); // Later used as: function_pointer_variable = &some_function; return function_pointer_variable(args); ``` In the latter case, it's not a cast but a variable declaration of a pointer to function. Without knowing what `some_return_type` is (and clang-format does not know it), it's hard to distinguish between the two cases. Theoretically, one could check whether "parameters" are types (not a cast) and "arguments" are value/expressions (a cast), but that might be inefficient (needs lots of lookahead). Reviewed By: MyDeveloperDay, HazardyKnusperkeks, owenpan Differential Revision: https://reviews.llvm.org/D120140 --- clang/lib/Format/TokenAnnotator.cpp | 19 +++++++++++++++++-- clang/unittests/Format/FormatTest.cpp | 7 +++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 9a020eb..51e7c32 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1734,8 +1734,11 @@ private: else Current.setType(TT_LineComment); } else if (Current.is(tok::r_paren)) { - if (rParenEndsCast(Current)) + if (rParenEndsCast(Current)) { Current.setType(TT_CastRParen); + assert(Current.MatchingParen); + Current.MatchingParen->setType(TT_Unknown); + } if (Current.MatchingParen && Current.Next && !Current.Next->isBinaryOperator() && !Current.Next->isOneOf(tok::semi, tok::colon, tok::l_brace, @@ -1938,8 +1941,20 @@ private: // Certain other tokens right before the parentheses are also signals that // this cannot be a cast. + if (LeftOfParens->is(TT_TemplateCloser)) { + if (LeftOfParens->MatchingParen) { + auto *Prev = LeftOfParens->MatchingParen->getPreviousNonComment(); + if (Prev && + Prev->isOneOf(tok::kw_const_cast, tok::kw_dynamic_cast, + tok::kw_reinterpret_cast, tok::kw_static_cast)) + // FIXME: Maybe we should handle identifiers ending with "_cast", + // e.g. any_cast? + return true; + } + return false; + } if (LeftOfParens->isOneOf(tok::at, tok::r_square, TT_OverloadedOperator, - TT_TemplateCloser, tok::ellipsis)) + tok::ellipsis)) return false; } diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index f71f8dc..d45146d 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -10565,6 +10565,13 @@ TEST_F(FormatTest, FormatsBinaryOperatorsPrecedingEquals) { TEST_F(FormatTest, FormatsCasts) { verifyFormat("Type *A = static_cast(P);"); + verifyFormat("static_cast(P);"); + verifyFormat("static_cast(Fun)(Args);"); + verifyFormat("static_cast(*Fun)(Args);"); + verifyFormat("a = static_cast(*Fun)(Args);"); + verifyFormat("const_cast(*Fun)(Args);"); + verifyFormat("dynamic_cast(*Fun)(Args);"); + verifyFormat("reinterpret_cast(*Fun)(Args);"); verifyFormat("Type *A = (Type *)P;"); verifyFormat("Type *A = (vector)P;"); verifyFormat("int a = (int)(2.0f);"); -- 2.7.4