[clang-format] Fix putting ObjC message arguments in one line for multiline receiver

Summary:
Reapply reverted changes from D46879.

Currently BreakBeforeParameter is set to true everytime message receiver spans multiple lines, e.g.:
```
[[object block:^{
  return 42;
}] aa:42 bb:42];
```
will be formatted:
```
[[object block:^{
  return 42;
}] aa:42
   bb:42];
```
even though arguments could fit into one line. This change fixes this behavior.

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

Reviewers: benhamilton, krasimir

Reviewed By: benhamilton, krasimir

Subscribers: djasper, klimek, cfe-commits

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

llvm-svn: 333171
This commit is contained in:
Jacek Olesiak 2018-05-24 10:50:36 +00:00
parent d0a8ad3ac0
commit 34391f097d
2 changed files with 58 additions and 0 deletions

View File

@ -1387,6 +1387,29 @@ void ContinuationIndenter::moveStatePastScopeCloser(LineState &State) {
(Current.is(tok::greater) && Current.is(TT_DictLiteral))))
State.Stack.pop_back();
// Reevaluate whether ObjC message arguments fit into one line.
// If a receiver spans multiple lines, e.g.:
// [[object block:^{
// return 42;
// }] a:42 b:42];
// BreakBeforeParameter is calculated based on an incorrect assumption
// (it is checked whether the whole expression fits into one line without
// considering a line break inside a message receiver).
// We check whether arguements fit after receiver scope closer (into the same
// line).
if (Current.MatchingParen && Current.MatchingParen->Previous) {
const FormatToken &CurrentScopeOpener = *Current.MatchingParen->Previous;
if (CurrentScopeOpener.is(TT_ObjCMethodExpr) &&
CurrentScopeOpener.MatchingParen) {
int NecessarySpaceInLine =
getLengthToMatchingParen(CurrentScopeOpener, State.Stack) +
CurrentScopeOpener.TotalLength - Current.TotalLength - 1;
if (State.Column + Current.ColumnWidth + NecessarySpaceInLine <=
Style.ColumnLimit)
State.Stack.back().BreakBeforeParameter = false;
}
}
if (Current.is(tok::r_square)) {
// If this ends the array subscript expr, reset the corresponding value.
const FormatToken *NextNonComment = Current.getNextNonComment();

View File

@ -796,6 +796,41 @@ TEST_F(FormatTestObjC, FormatObjCMethodExpr) {
verifyFormat("[((Foo *)foo) bar];");
verifyFormat("[((Foo *)foo) bar:1 blech:2];");
// Message receiver taking multiple lines.
Style.ColumnLimit = 20;
// Non-corner case.
verifyFormat("[[object block:^{\n"
" return 42;\n"
"}] a:42 b:42];");
// Arguments just fit into one line.
verifyFormat("[[object block:^{\n"
" return 42;\n"
"}] aaaaaaa:42 b:42];");
// Arguments just over a column limit.
verifyFormat("[[object block:^{\n"
" return 42;\n"
"}] aaaaaaa:42\n"
" bb:42];");
// Arguments just fit into one line.
Style.ColumnLimit = 23;
verifyFormat("[[obj a:42\n"
" b:42\n"
" c:42\n"
" d:42] e:42 f:42];");
// Arguments do not fit into one line with a receiver.
Style.ColumnLimit = 20;
verifyFormat("[[obj a:42] a:42\n"
" b:42];");
verifyFormat("[[obj a:42] a:42\n"
" b:42\n"
" c:42];");
verifyFormat("[[obj aaaaaa:42\n"
" b:42]\n"
" cc:42\n"
" d:42];");
Style.ColumnLimit = 70;
verifyFormat(
"void f() {\n"