[clang-format] Fix ObjC message arguments formatting.

Summary:
Fixes formatting of ObjC message arguments when inline block is a first
argument.

Having inline block as a first argument when method has multiple parameters is
discouraged by Apple:
"It’s best practice to use only one block argument to a method. If the
method also needs other non-block arguments, the block should come last"
(https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html#//apple_ref/doc/uid/TP40011210-CH8-SW7),
it should be correctly formatted nevertheless.

Current formatting:
```
[object blockArgument:^{
  a = 42;
}
    anotherArg:42];
```

Fixed (colon alignment):
```
[object
  blockArgument:^{
    a = 42;
  }
     anotherArg:42];
```

Test Plan: make -j12 FormatTests && tools/clang/unittests/Format/FormatTests

Reviewers: krasimir, benhamilton

Reviewed By: krasimir, benhamilton

Subscribers: benhamilton, klimek, cfe-commits

Differential Revision: https://reviews.llvm.org/D42493

llvm-svn: 324469
This commit is contained in:
Jacek Olesiak 2018-02-07 10:35:08 +00:00
parent 7244159b82
commit fb7f5c08b9
4 changed files with 53 additions and 1 deletions

View File

@ -266,6 +266,11 @@ bool ContinuationIndenter::mustBreak(const LineState &State) {
return true;
if (Previous.is(tok::semi) && State.LineContainsContinuedForLoopSection)
return true;
if (Style.Language == FormatStyle::LK_ObjC &&
Current.ObjCSelectorNameParts > 1 &&
Current.startsSequence(TT_SelectorName, tok::colon, tok::caret)) {
return true;
}
if ((startsNextParameter(Current, Style) || Previous.is(tok::semi) ||
(Previous.is(TT_TemplateCloser) && Current.is(TT_StartOfName) &&
Style.isCpp() &&

View File

@ -240,6 +240,10 @@ struct FormatToken {
/// e.g. because several of them are block-type.
unsigned LongestObjCSelectorName = 0;
/// \brief How many parts ObjC selector have (i.e. how many parameters method
/// has).
unsigned ObjCSelectorNameParts = 0;
/// \brief Stores the number of required fake parentheses and the
/// corresponding operator precedence.
///

View File

@ -411,6 +411,8 @@ private:
if (Contexts.back().FirstObjCSelectorName) {
Contexts.back().FirstObjCSelectorName->LongestObjCSelectorName =
Contexts.back().LongestObjCSelectorName;
Contexts.back().FirstObjCSelectorName->ObjCSelectorNameParts =
Left->ParameterCount;
if (Left->BlockParameterCount > 1)
Contexts.back().FirstObjCSelectorName->LongestObjCSelectorName = 0;
}
@ -424,6 +426,11 @@ private:
TT_DesignatedInitializerLSquare)) {
Left->Type = TT_ObjCMethodExpr;
StartsObjCMethodExpr = true;
// ParameterCount might have been set to 1 before expression was
// recognized as ObjCMethodExpr (as '1 + number of commas' formula is
// used for other expression types). Parameter counter has to be,
// therefore, reset to 0.
Left->ParameterCount = 0;
Contexts.back().ColonIsObjCMethodExpr = true;
if (Parent && Parent->is(tok::r_paren))
Parent->Type = TT_CastRParen;
@ -498,7 +505,10 @@ private:
void updateParameterCount(FormatToken *Left, FormatToken *Current) {
if (Current->is(tok::l_brace) && Current->BlockKind == BK_Block)
++Left->BlockParameterCount;
if (Current->is(tok::comma)) {
if (Left->Type == TT_ObjCMethodExpr) {
if (Current->is(tok::colon))
++Left->ParameterCount;
} else if (Current->is(tok::comma)) {
++Left->ParameterCount;
if (!Left->Role)
Left->Role.reset(new CommaSeparatedList(Style));

View File

@ -693,6 +693,39 @@ TEST_F(FormatTestObjC, FormatObjCMethodExpr) {
" ofSize:aa:bbb\n"
" atOrigin:cc:dd];");
// Inline block as a first argument.
verifyFormat("[object justBlock:^{\n"
" a = 42;\n"
"}];");
verifyFormat("[object\n"
" justBlock:^{\n"
" a = 42;\n"
" }\n"
" notBlock:42\n"
" a:42];");
verifyFormat("[object\n"
" firstBlock:^{\n"
" a = 42;\n"
" }\n"
" blockWithLongerName:^{\n"
" a = 42;\n"
" }];");
verifyFormat("[object\n"
" blockWithLongerName:^{\n"
" a = 42;\n"
" }\n"
" secondBlock:^{\n"
" a = 42;\n"
" }];");
verifyFormat("[object\n"
" firstBlock:^{\n"
" a = 42;\n"
" }\n"
" notBlock:42\n"
" secondBlock:^{\n"
" a = 42;\n"
" }];");
Style.ColumnLimit = 70;
verifyFormat(
"void f() {\n"