aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Kurdej <marek.kurdej+llvm.org@gmail.com>2022-02-22 22:32:03 +0100
committerMarek Kurdej <marek.kurdej+llvm.org@gmail.com>2022-02-24 10:21:02 +0100
commitbfb4afee74c8d6e3b1d020564bfe163073f07a04 (patch)
treed3f5d7ca75a3b19a558a038825012953de15cf4f
parent56b5f001869664a1449e5e0fec677089153bf8c8 (diff)
downloadllvm-bfb4afee74c8d6e3b1d020564bfe163073f07a04.zip
llvm-bfb4afee74c8d6e3b1d020564bfe163073f07a04.tar.gz
llvm-bfb4afee74c8d6e3b1d020564bfe163073f07a04.tar.bz2
[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<T>(*function_pointer_variable)(arguments); ``` vs. ``` some_return_type<T> (*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<T>` 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
-rw-r--r--clang/lib/Format/FormatToken.h1
-rw-r--r--clang/lib/Format/TokenAnnotator.cpp25
-rw-r--r--clang/unittests/Format/FormatTest.cpp13
3 files changed, 38 insertions, 1 deletions
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index bd1d447..d1dc3ff 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -43,6 +43,7 @@ namespace format {
TYPE(ConflictAlternative) \
TYPE(ConflictEnd) \
TYPE(ConflictStart) \
+ TYPE(CppCastLParen) \
TYPE(CtorInitializerColon) \
TYPE(CtorInitializerComma) \
TYPE(DesignatedInitializerLSquare) \
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 01c215f..36ff757 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -358,7 +358,8 @@ private:
if (CurrentToken->Previous->is(TT_BinaryOperator))
Contexts.back().IsExpression = true;
if (CurrentToken->is(tok::r_paren)) {
- if (MightBeFunctionType && ProbablyFunctionType && CurrentToken->Next &&
+ if (Left->isNot(TT_CppCastLParen) && MightBeFunctionType &&
+ ProbablyFunctionType && CurrentToken->Next &&
(CurrentToken->Next->is(tok::l_paren) ||
(CurrentToken->Next->is(tok::l_square) && Line.MustBeDeclaration)))
Left->setType(Left->Next->is(tok::caret) ? TT_ObjCBlockLParen
@@ -1733,6 +1734,9 @@ private:
Current.Tok.setKind(tok::unknown);
else
Current.setType(TT_LineComment);
+ } else if (Current.is(tok::l_paren)) {
+ if (lParenStartsCppCast(Current))
+ Current.setType(TT_CppCastLParen);
} else if (Current.is(tok::r_paren)) {
if (rParenEndsCast(Current))
Current.setType(TT_CastRParen);
@@ -1880,6 +1884,25 @@ private:
return Style.isJavaScript() && PreviousNotConst->is(tok::kw_const);
}
+ /// Determine whether '(' is starting a C++ cast.
+ bool lParenStartsCppCast(const FormatToken &Tok) {
+ // C-style casts are only used in C++.
+ if (!Style.isCpp())
+ return false;
+
+ FormatToken *LeftOfParens = Tok.getPreviousNonComment();
+ if (LeftOfParens && LeftOfParens->is(TT_TemplateCloser) &&
+ 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;
+ }
+
/// Determine whether ')' is ending a cast.
bool rParenEndsCast(const FormatToken &Tok) {
// C-style casts are only used in C++, C# and Java.
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 6cd6c4a..624f3a7 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -10570,6 +10570,19 @@ TEST_F(FormatTest, FormatsBinaryOperatorsPrecedingEquals) {
TEST_F(FormatTest, FormatsCasts) {
verifyFormat("Type *A = static_cast<Type *>(P);");
+ verifyFormat("static_cast<Type *>(P);");
+ verifyFormat("static_cast<Type &>(Fun)(Args);");
+ verifyFormat("static_cast<Type &>(*Fun)(Args);");
+ verifyFormat("if (static_cast<int>(A) + B >= 0)\n ;");
+ // Check that static_cast<...>(...) does not require the next token to be on
+ // the same line.
+ verifyFormat("some_loooong_output << something_something__ << "
+ "static_cast<const void *>(R)\n"
+ " << something;");
+ verifyFormat("a = static_cast<Type &>(*Fun)(Args);");
+ verifyFormat("const_cast<Type &>(*Fun)(Args);");
+ verifyFormat("dynamic_cast<Type &>(*Fun)(Args);");
+ verifyFormat("reinterpret_cast<Type &>(*Fun)(Args);");
verifyFormat("Type *A = (Type *)P;");
verifyFormat("Type *A = (vector<Type *, int *>)P;");
verifyFormat("int a = (int)(2.0f);");