From 788a2227b7bcbff8384fe0b96e4bd66bfa11ccf1 Mon Sep 17 00:00:00 2001 From: Ben Hamilton Date: Mon, 12 Mar 2018 15:42:40 +0000 Subject: [PATCH] [clang-format] Improve detection of Objective-C block types Summary: Previously, clang-format would detect the following as an Objective-C block type: FOO(^); when it actually must be a C or C++ macro dealing with an XOR statement or an XOR operator overload. According to the Clang Block Language Spec: https://clang.llvm.org/docs/BlockLanguageSpec.html block types are of the form: int (^)(char, float) and block variables of block type are of the form: void (^blockReturningVoidWithVoidArgument)(void); int (^blockReturningIntWithIntAndCharArguments)(int, char); void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int); This tightens up the detection so we don't unnecessarily detect C macros which pass in the XOR operator. Depends On D43904 Test Plan: New tests added. Ran tests with: make -j12 FormatTests && ./tools/clang/unittests/Format/FormatTests Reviewers: krasimir, jolesiak, djasper Reviewed By: djasper Subscribers: djasper, cfe-commits, klimek Differential Revision: https://reviews.llvm.org/D43906 llvm-svn: 327285 --- clang/lib/Format/TokenAnnotator.cpp | 18 ++++++++++++------ clang/unittests/Format/FormatTest.cpp | 16 ++++++++++++++++ clang/unittests/Format/FormatTestObjC.cpp | 9 +++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index b216d72960..3de454c 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -141,10 +141,7 @@ private: Contexts.size() == 2 && Contexts[0].ColonIsForRangeExpr; bool StartsObjCMethodExpr = false; - if (CurrentToken->is(tok::caret)) { - // (^ can start a block type. - Left->Type = TT_ObjCBlockLParen; - } else if (FormatToken *MaybeSel = Left->Previous) { + if (FormatToken *MaybeSel = Left->Previous) { // @selector( starts a selector. if (MaybeSel->isObjCAtKeyword(tok::objc_selector) && MaybeSel->Previous && MaybeSel->Previous->is(tok::at)) { @@ -210,8 +207,16 @@ private: Left->Type = TT_ObjCMethodExpr; } + // MightBeFunctionType and ProbablyFunctionType are used for + // function pointer and reference types as well as Objective-C + // block types: + // + // void (*FunctionPointer)(void); + // void (&FunctionReference)(void); + // void (^ObjCBlock)(void); bool MightBeFunctionType = !Contexts[Contexts.size() - 2].IsExpression; - bool ProbablyFunctionType = CurrentToken->isOneOf(tok::star, tok::amp); + bool ProbablyFunctionType = + CurrentToken->isOneOf(tok::star, tok::amp, tok::caret); bool HasMultipleLines = false; bool HasMultipleParametersOnALine = false; bool MightBeObjCForRangeLoop = @@ -248,7 +253,8 @@ private: if (MightBeFunctionType && ProbablyFunctionType && CurrentToken->Next && (CurrentToken->Next->is(tok::l_paren) || (CurrentToken->Next->is(tok::l_square) && Line.MustBeDeclaration))) - Left->Type = TT_FunctionTypeLParen; + Left->Type = Left->Next->is(tok::caret) ? TT_ObjCBlockLParen + : TT_FunctionTypeLParen; Left->MatchingParen = CurrentToken; CurrentToken->MatchingParen = Left; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 7e0904c..a9fbe4b 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -12128,6 +12128,22 @@ TEST_F(FormatTest, GuessLanguageWithCpp11AttributeSpecifiers) { EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "[[foo::bar, ...]]")); } +TEST_F(FormatTest, GuessLanguageWithCaret) { + EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "FOO(^);")); + EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "FOO(^, Bar);")); + EXPECT_EQ(FormatStyle::LK_ObjC, + guessLanguage("foo.h", "int(^)(char, float);")); + EXPECT_EQ(FormatStyle::LK_ObjC, + guessLanguage("foo.h", "int(^foo)(char, float);")); + EXPECT_EQ(FormatStyle::LK_ObjC, + guessLanguage("foo.h", "int(^foo[10])(char, float);")); + EXPECT_EQ(FormatStyle::LK_ObjC, + guessLanguage("foo.h", "int(^foo[kNumEntries])(char, float);")); + EXPECT_EQ( + FormatStyle::LK_ObjC, + guessLanguage("foo.h", "int(^foo[(kNumEntries + 10)])(char, float);")); +} + } // end namespace } // end namespace format } // end namespace clang diff --git a/clang/unittests/Format/FormatTestObjC.cpp b/clang/unittests/Format/FormatTestObjC.cpp index 8773088..efc227d 100644 --- a/clang/unittests/Format/FormatTestObjC.cpp +++ b/clang/unittests/Format/FormatTestObjC.cpp @@ -845,6 +845,15 @@ TEST_F(FormatTestObjC, ObjCAt) { verifyFormat("@ /*foo*/ interface"); } +TEST_F(FormatTestObjC, ObjCBlockTypesAndVariables) { + verifyFormat("void DoStuffWithBlockType(int (^)(char));"); + verifyFormat("int (^foo)(char, float);"); + verifyFormat("int (^foo[10])(char, float);"); + verifyFormat("int (^foo[kNumEntries])(char, float);"); + verifyFormat("int (^foo[kNumEntries + 10])(char, float);"); + verifyFormat("int (^foo[(kNumEntries + 10)])(char, float);"); +} + TEST_F(FormatTestObjC, ObjCSnippets) { verifyFormat("@autoreleasepool {\n" " foo();\n" -- 2.7.4