[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
This commit is contained in:
Ben Hamilton 2018-03-12 15:42:40 +00:00
parent b060ad870f
commit 788a2227b7
3 changed files with 37 additions and 6 deletions

View File

@ -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;

View File

@ -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

View File

@ -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"