forked from OSchip/llvm-project
clang-format: fix a crash in comment wraps.
Summary: Previously, clang-format would crash if it tried to wrap an overlong single line comment, because two parts of the code inserted a break in the same location. /** heregoesalongcommentwithnospace */ This wasn't previously noticed as it could only trigger for an overlong single line comment that did have no breaking opportunities except for a whitespace at the very beginning. This also introduces a check for JavaScript to not ever wrap a comment before an opening curly brace: /** @mods {donotbreakbeforethecurly} */ This is because some machinery parsing these tags sometimes supports breaks before a possible `{`, but in some other cases does not. Previously clang-format was careful never to wrap a line with certain tags on it. The better solution is to specifically disable wrapping before the problematic token: this allows wrapping and aligning comments but still avoids the problem. Reviewers: krasimir Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D50177 llvm-svn: 338706
This commit is contained in:
parent
1a721eb3a2
commit
9d7178139c
|
@ -67,10 +67,11 @@ static BreakableToken::Split getCommentSplit(StringRef Text,
|
||||||
unsigned ContentStartColumn,
|
unsigned ContentStartColumn,
|
||||||
unsigned ColumnLimit,
|
unsigned ColumnLimit,
|
||||||
unsigned TabWidth,
|
unsigned TabWidth,
|
||||||
encoding::Encoding Encoding) {
|
encoding::Encoding Encoding,
|
||||||
LLVM_DEBUG(llvm::dbgs() << "Comment split: \"" << Text << ", " << ColumnLimit
|
const FormatStyle &Style) {
|
||||||
<< "\", Content start: " << ContentStartColumn
|
LLVM_DEBUG(llvm::dbgs() << "Comment split: \"" << Text
|
||||||
<< "\n");
|
<< "\", Column limit: " << ColumnLimit
|
||||||
|
<< ", Content start: " << ContentStartColumn << "\n");
|
||||||
if (ColumnLimit <= ContentStartColumn + 1)
|
if (ColumnLimit <= ContentStartColumn + 1)
|
||||||
return BreakableToken::Split(StringRef::npos, 0);
|
return BreakableToken::Split(StringRef::npos, 0);
|
||||||
|
|
||||||
|
@ -95,6 +96,13 @@ static BreakableToken::Split getCommentSplit(StringRef Text,
|
||||||
if (SpaceOffset != StringRef::npos &&
|
if (SpaceOffset != StringRef::npos &&
|
||||||
kNumberedListRegexp->match(Text.substr(SpaceOffset).ltrim(Blanks)))
|
kNumberedListRegexp->match(Text.substr(SpaceOffset).ltrim(Blanks)))
|
||||||
SpaceOffset = Text.find_last_of(Blanks, SpaceOffset);
|
SpaceOffset = Text.find_last_of(Blanks, SpaceOffset);
|
||||||
|
// In JavaScript, some @tags can be followed by {, and machinery that parses
|
||||||
|
// these comments will fail to understand the comment if followed by a line
|
||||||
|
// break. So avoid ever breaking before a {.
|
||||||
|
if (Style.Language == FormatStyle::LK_JavaScript &&
|
||||||
|
SpaceOffset != StringRef::npos && SpaceOffset + 1 < Text.size() &&
|
||||||
|
Text[SpaceOffset + 1] == '{')
|
||||||
|
SpaceOffset = Text.find_last_of(Blanks, SpaceOffset);
|
||||||
|
|
||||||
if (SpaceOffset == StringRef::npos ||
|
if (SpaceOffset == StringRef::npos ||
|
||||||
// Don't break at leading whitespace.
|
// Don't break at leading whitespace.
|
||||||
|
@ -109,6 +117,12 @@ static BreakableToken::Split getCommentSplit(StringRef Text,
|
||||||
Blanks, std::max<unsigned>(MaxSplitBytes, FirstNonWhitespace));
|
Blanks, std::max<unsigned>(MaxSplitBytes, FirstNonWhitespace));
|
||||||
}
|
}
|
||||||
if (SpaceOffset != StringRef::npos && SpaceOffset != 0) {
|
if (SpaceOffset != StringRef::npos && SpaceOffset != 0) {
|
||||||
|
// adaptStartOfLine will break after lines starting with /** if the comment
|
||||||
|
// is broken anywhere. Avoid emitting this break twice here.
|
||||||
|
// Example: in /** longtextcomesherethatbreaks */ (with ColumnLimit 20) will
|
||||||
|
// insert a break after /**, so this code must not insert the same break.
|
||||||
|
if (SpaceOffset == 1 && Text[SpaceOffset - 1] == '*')
|
||||||
|
return BreakableToken::Split(StringRef::npos, 0);
|
||||||
StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim(Blanks);
|
StringRef BeforeCut = Text.substr(0, SpaceOffset).rtrim(Blanks);
|
||||||
StringRef AfterCut = Text.substr(SpaceOffset).ltrim(Blanks);
|
StringRef AfterCut = Text.substr(SpaceOffset).ltrim(Blanks);
|
||||||
return BreakableToken::Split(BeforeCut.size(),
|
return BreakableToken::Split(BeforeCut.size(),
|
||||||
|
@ -260,7 +274,7 @@ BreakableComment::getSplit(unsigned LineIndex, unsigned TailOffset,
|
||||||
return Split(StringRef::npos, 0);
|
return Split(StringRef::npos, 0);
|
||||||
return getCommentSplit(Content[LineIndex].substr(TailOffset),
|
return getCommentSplit(Content[LineIndex].substr(TailOffset),
|
||||||
ContentStartColumn, ColumnLimit, Style.TabWidth,
|
ContentStartColumn, ColumnLimit, Style.TabWidth,
|
||||||
Encoding);
|
Encoding, Style);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BreakableComment::compressWhitespace(
|
void BreakableComment::compressWhitespace(
|
||||||
|
@ -620,6 +634,8 @@ void BreakableBlockComment::adaptStartOfLine(
|
||||||
if (DelimitersOnNewline) {
|
if (DelimitersOnNewline) {
|
||||||
// Since we're breaking at index 1 below, the break position and the
|
// Since we're breaking at index 1 below, the break position and the
|
||||||
// break length are the same.
|
// break length are the same.
|
||||||
|
// Note: this works because getCommentSplit is careful never to split at
|
||||||
|
// the beginning of a line.
|
||||||
size_t BreakLength = Lines[0].substr(1).find_first_not_of(Blanks);
|
size_t BreakLength = Lines[0].substr(1).find_first_not_of(Blanks);
|
||||||
if (BreakLength != StringRef::npos)
|
if (BreakLength != StringRef::npos)
|
||||||
insertBreak(LineIndex, 0, Split(1, BreakLength), /*ContentIndent=*/0,
|
insertBreak(LineIndex, 0, Split(1, BreakLength), /*ContentIndent=*/0,
|
||||||
|
|
|
@ -1254,6 +1254,12 @@ TEST_F(FormatTestComments, SplitsLongLinesInComments) {
|
||||||
" */",
|
" */",
|
||||||
getLLVMStyleWithColumns(20)));
|
getLLVMStyleWithColumns(20)));
|
||||||
|
|
||||||
|
// This reproduces a crashing bug where both adaptStartOfLine and
|
||||||
|
// getCommentSplit were trying to wrap after the "/**".
|
||||||
|
EXPECT_EQ("/** multilineblockcommentwithnowrapopportunity */",
|
||||||
|
format("/** multilineblockcommentwithnowrapopportunity */",
|
||||||
|
getLLVMStyleWithColumns(20)));
|
||||||
|
|
||||||
EXPECT_EQ("/*\n"
|
EXPECT_EQ("/*\n"
|
||||||
"\n"
|
"\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
|
@ -191,17 +191,19 @@ TEST_F(FormatTestJS, JSDocComments) {
|
||||||
|
|
||||||
// Break a single line long jsdoc comment pragma.
|
// Break a single line long jsdoc comment pragma.
|
||||||
EXPECT_EQ("/**\n"
|
EXPECT_EQ("/**\n"
|
||||||
" * @returns\n"
|
" * @returns {string} jsdoc line 12\n"
|
||||||
" * {string}\n"
|
|
||||||
" * jsdoc line 12\n"
|
|
||||||
" */",
|
" */",
|
||||||
format("/** @returns {string} jsdoc line 12 */",
|
format("/** @returns {string} jsdoc line 12 */",
|
||||||
getGoogleJSStyleWithColumns(20)));
|
getGoogleJSStyleWithColumns(20)));
|
||||||
|
EXPECT_EQ("/**\n"
|
||||||
|
" * @returns {string}\n"
|
||||||
|
" * jsdoc line 12\n"
|
||||||
|
" */",
|
||||||
|
format("/** @returns {string} jsdoc line 12 */",
|
||||||
|
getGoogleJSStyleWithColumns(25)));
|
||||||
|
|
||||||
EXPECT_EQ("/**\n"
|
EXPECT_EQ("/**\n"
|
||||||
" * @returns\n"
|
" * @returns {string} jsdoc line 12\n"
|
||||||
" * {string}\n"
|
|
||||||
" * jsdoc line 12\n"
|
|
||||||
" */",
|
" */",
|
||||||
format("/** @returns {string} jsdoc line 12 */",
|
format("/** @returns {string} jsdoc line 12 */",
|
||||||
getGoogleJSStyleWithColumns(20)));
|
getGoogleJSStyleWithColumns(20)));
|
||||||
|
@ -209,13 +211,12 @@ TEST_F(FormatTestJS, JSDocComments) {
|
||||||
// FIXME: this overcounts the */ as a continuation of the 12 when breaking.
|
// FIXME: this overcounts the */ as a continuation of the 12 when breaking.
|
||||||
// Related to the FIXME in BreakableBlockComment::getRangeLength.
|
// Related to the FIXME in BreakableBlockComment::getRangeLength.
|
||||||
EXPECT_EQ("/**\n"
|
EXPECT_EQ("/**\n"
|
||||||
" * @returns\n"
|
" * @returns {string}\n"
|
||||||
" * {string}\n"
|
" * jsdoc line line\n"
|
||||||
" * jsdoc line\n"
|
|
||||||
" * 12\n"
|
" * 12\n"
|
||||||
" */",
|
" */",
|
||||||
format("/** @returns {string} jsdoc line 12*/",
|
format("/** @returns {string} jsdoc line line 12*/",
|
||||||
getGoogleJSStyleWithColumns(20)));
|
getGoogleJSStyleWithColumns(25)));
|
||||||
|
|
||||||
// Fix a multiline jsdoc comment ending in a comment pragma.
|
// Fix a multiline jsdoc comment ending in a comment pragma.
|
||||||
EXPECT_EQ("/**\n"
|
EXPECT_EQ("/**\n"
|
||||||
|
@ -2038,27 +2039,32 @@ TEST_F(FormatTestJS, WrapAfterParen) {
|
||||||
|
|
||||||
TEST_F(FormatTestJS, JSDocAnnotations) {
|
TEST_F(FormatTestJS, JSDocAnnotations) {
|
||||||
verifyFormat("/**\n"
|
verifyFormat("/**\n"
|
||||||
" * @exports\n"
|
" * @exports {this.is.a.long.path.to.a.Type}\n"
|
||||||
" * {this.is.a.long.path.to.a.Type}\n"
|
|
||||||
" */",
|
" */",
|
||||||
"/**\n"
|
"/**\n"
|
||||||
" * @exports {this.is.a.long.path.to.a.Type}\n"
|
" * @exports {this.is.a.long.path.to.a.Type}\n"
|
||||||
" */",
|
" */",
|
||||||
getGoogleJSStyleWithColumns(20));
|
getGoogleJSStyleWithColumns(20));
|
||||||
verifyFormat("/**\n"
|
verifyFormat("/**\n"
|
||||||
" * @mods\n"
|
" * @mods {this.is.a.long.path.to.a.Type}\n"
|
||||||
" * {this.is.a.long.path.to.a.Type}\n"
|
|
||||||
" */",
|
" */",
|
||||||
"/**\n"
|
"/**\n"
|
||||||
" * @mods {this.is.a.long.path.to.a.Type}\n"
|
" * @mods {this.is.a.long.path.to.a.Type}\n"
|
||||||
" */",
|
" */",
|
||||||
getGoogleJSStyleWithColumns(20));
|
getGoogleJSStyleWithColumns(20));
|
||||||
verifyFormat("/**\n"
|
verifyFormat("/**\n"
|
||||||
" * @param\n"
|
" * @mods {this.is.a.long.path.to.a.Type}\n"
|
||||||
" * {this.is.a.long.path.to.a.Type}\n"
|
|
||||||
" */",
|
" */",
|
||||||
"/**\n"
|
"/**\n"
|
||||||
" * @param {this.is.a.long.path.to.a.Type}\n"
|
" * @mods {this.is.a.long.path.to.a.Type}\n"
|
||||||
|
" */",
|
||||||
|
getGoogleJSStyleWithColumns(20));
|
||||||
|
verifyFormat("/**\n"
|
||||||
|
" * @param {canWrap\n"
|
||||||
|
" * onSpace}\n"
|
||||||
|
" */",
|
||||||
|
"/**\n"
|
||||||
|
" * @param {canWrap onSpace}\n"
|
||||||
" */",
|
" */",
|
||||||
getGoogleJSStyleWithColumns(20));
|
getGoogleJSStyleWithColumns(20));
|
||||||
verifyFormat("/**\n"
|
verifyFormat("/**\n"
|
||||||
|
@ -2083,8 +2089,7 @@ TEST_F(FormatTestJS, JSDocAnnotations) {
|
||||||
" /**\n"
|
" /**\n"
|
||||||
" * long long long\n"
|
" * long long long\n"
|
||||||
" * long\n"
|
" * long\n"
|
||||||
" * @param\n"
|
" * @param {this.is.a.long.path.to.a.Type}\n"
|
||||||
" * {this.is.a.long.path.to.a.Type}\n"
|
|
||||||
" * a\n"
|
" * a\n"
|
||||||
" * long long long\n"
|
" * long long long\n"
|
||||||
" * long long\n"
|
" * long long\n"
|
||||||
|
|
Loading…
Reference in New Issue