diff options
author | Krasimir Georgiev <krasimir@google.com> | 2018-10-25 07:39:30 +0000 |
---|---|---|
committer | Krasimir Georgiev <krasimir@google.com> | 2018-10-25 07:39:30 +0000 |
commit | 7378d7a652df04f180b40d747710f407684c7752 (patch) | |
tree | a3e54ae447dc7fa7805026950f937598fdf455a3 | |
parent | dfa39ffd088e0a66bb88239556b029ebb8fc57fc (diff) |
[clang-format] Break before next parameter after a formatted multiline raw string parameter
Summary:
Currently clang-format breaks before the next parameter after multiline parameters (also recursively for the parent expressions of multiline parameters). However, it fails to do so for formatted multiline raw string literals:
```
$ cat test.cc
// Examples
// Regular multiline tokens
int x = f(R"(multi
line)", 2);
}
int y = g(h(R"(multi
line)"), 2);
// Formatted multiline tokens
int z = f(R"pb(multi: 1 #
line: 2)pb", 2);
int w = g(h(R"pb(multi: 1 #
line: 2)pb"), 2);
$ clang-format -style=google test.cc
// Examples
// Regular multiline tokens
int x = f(R"(multi
line)",
2);
}
int y = g(h(R"(multi
line)"),
2);
// Formatted multiline tokens
int z = f(R"pb(multi: 1 #
line: 2)pb", 2);
int w = g(h(R"pb(multi: 1 #
line: 2)pb"), 2);
```
This patch addresses this inconsistency by forcing breaking after multiline formatted raw string literals. This requires a little tweak to the indentation chosen for the contents of a formatted raw string literal: in case when that's a parameter and not the last one, the indentation is based off of the uniform indentation of all of the parameters.
Reviewers: sammccall
Reviewed By: sammccall
Subscribers: cfe-commits
Differential Revision: https://reviews.llvm.org/D52448
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@345242 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/Format/ContinuationIndenter.cpp | 35 | ||||
-rw-r--r-- | unittests/Format/FormatTestRawStrings.cpp | 97 |
2 files changed, 123 insertions, 9 deletions
diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index c9c96456f4..89d346d8a2 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -1502,10 +1502,25 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( // violate the rectangle rule and visually flows within the surrounding // source. bool ContentStartsOnNewline = Current.TokenText[OldPrefixSize] == '\n'; - unsigned NextStartColumn = - ContentStartsOnNewline - ? State.Stack.back().NestedBlockIndent + Style.IndentWidth - : FirstStartColumn; + // If this token is the last parameter (checked by looking if it's followed by + // `)`, the base the indent off the line's nested block indent. Otherwise, + // base the indent off the arguments indent, so we can achieve: + // fffffffffff(1, 2, 3, R"pb( + // key1: 1 # + // key2: 2)pb"); + // + // fffffffffff(1, 2, 3, + // R"pb( + // key1: 1 # + // key2: 2 + // )pb", + // 5); + unsigned CurrentIndent = (Current.Next && Current.Next->is(tok::r_paren)) + ? State.Stack.back().NestedBlockIndent + : State.Stack.back().Indent; + unsigned NextStartColumn = ContentStartsOnNewline + ? CurrentIndent + Style.IndentWidth + : FirstStartColumn; // The last start column is the column the raw string suffix starts if it is // put on a newline. @@ -1517,7 +1532,7 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( // indent. unsigned LastStartColumn = Current.NewlinesBefore ? FirstStartColumn - NewPrefixSize - : State.Stack.back().NestedBlockIndent; + : CurrentIndent; std::pair<tooling::Replacements, unsigned> Fixes = internal::reformat( RawStringStyle, RawText, {tooling::Range(0, RawText.size())}, @@ -1527,8 +1542,7 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( auto NewCode = applyAllReplacements(RawText, Fixes.first); tooling::Replacements NoFixes; if (!NewCode) { - State.Column += Current.ColumnWidth; - return 0; + return addMultilineToken(Current, State); } if (!DryRun) { if (NewDelimiter != OldDelimiter) { @@ -1577,6 +1591,13 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( unsigned PrefixExcessCharacters = StartColumn + NewPrefixSize > Style.ColumnLimit ? StartColumn + NewPrefixSize - Style.ColumnLimit : 0; + bool IsMultiline = + ContentStartsOnNewline || (NewCode->find('\n') != std::string::npos); + if (IsMultiline) { + // Break before further function parameters on all levels. + for (unsigned i = 0, e = State.Stack.size(); i != e; ++i) + State.Stack[i].BreakBeforeParameter = true; + } return Fixes.second + PrefixExcessCharacters * Style.PenaltyExcessCharacter; } diff --git a/unittests/Format/FormatTestRawStrings.cpp b/unittests/Format/FormatTestRawStrings.cpp index 307394341d..2a8a43dc95 100644 --- a/unittests/Format/FormatTestRawStrings.cpp +++ b/unittests/Format/FormatTestRawStrings.cpp @@ -576,10 +576,13 @@ auto S = auto S = R"pb(item_1:1 item_2:2)pb"+rest;)test", getRawStringPbStyleWithColumns(40))); + // `rest` fits on the line after )pb", but forced on newline since the raw + // string literal is multiline. expect_eq(R"test( auto S = R"pb(item_1: 1, item_2: 2, - item_3: 3)pb" + rest;)test", + item_3: 3)pb" + + rest;)test", format(R"test( auto S = R"pb(item_1:1,item_2:2,item_3:3)pb"+rest;)test", getRawStringPbStyleWithColumns(40))); @@ -615,7 +618,8 @@ auto S = first+R"pb(item_1:1 item_2:2)pb";)test", expect_eq(R"test( auto S = R"pb(item_1: 1, item_2: 2, - item_3: 3)pb" + rest;)test", + item_3: 3)pb" + + rest;)test", format(R"test( auto S = R"pb(item_1:1,item_2:2,item_3:3)pb"+rest;)test", getRawStringPbStyleWithColumns(40))); @@ -889,6 +893,95 @@ item { Style)); } +TEST_F(FormatTestRawStrings, + BreaksBeforeNextParamAfterMultilineRawStringParam) { + FormatStyle Style = getRawStringPbStyleWithColumns(60); + expect_eq(R"test( +int f() { + int a = g(x, R"pb( + key: 1 # + key: 2 + )pb", + 3, 4); +} +)test", + format(R"test( +int f() { + int a = g(x, R"pb( + key: 1 # + key: 2 + )pb", 3, 4); +} +)test", + Style)); + + // Breaks after a parent of a multiline param. + expect_eq(R"test( +int f() { + int a = g(x, h(R"pb( + key: 1 # + key: 2 + )pb"), + 3, 4); +} +)test", + format(R"test( +int f() { + int a = g(x, h(R"pb( + key: 1 # + key: 2 + )pb"), 3, 4); +} +)test", + Style)); + + expect_eq(R"test( +int f() { + int a = g(x, + h(R"pb( + key: 1 # + key: 2 + )pb", + 2), + 3, 4); +} +)test", + format(R"test( +int f() { + int a = g(x, h(R"pb( + key: 1 # + key: 2 + )pb", 2), 3, 4); +} +)test", + Style)); + // Breaks if formatting introduces a multiline raw string. + expect_eq(R"test( +int f() { + int a = g(x, R"pb(key1: value111111111 + key2: value2222222222)pb", + 3, 4); +} +)test", + format(R"test( +int f() { + int a = g(x, R"pb(key1: value111111111 key2: value2222222222)pb", 3, 4); +} +)test", + Style)); + // Does not force a break after an original multiline param that is + // reformatterd as on single line. + expect_eq(R"test( +int f() { + int a = g(R"pb(key: 1)pb", 2); +})test", + format(R"test( +int f() { + int a = g(R"pb(key: + 1)pb", 2); +})test", Style)); +} + } // end namespace } // end namespace format } // end namespace clang |