Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions clang/lib/Format/BreakableToken.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,13 @@ unsigned BreakableStringLiteral::getContentStartColumn(unsigned LineIndex,

BreakableStringLiteral::BreakableStringLiteral(
const FormatToken &Tok, unsigned StartColumn, StringRef Prefix,
StringRef Postfix, unsigned UnbreakableTailLength, bool InPPDirective,
encoding::Encoding Encoding, const FormatStyle &Style)
StringRef Postfix, StringRef ContinuationPrefix,
StringRef ContinuationPostfix, unsigned UnbreakableTailLength,
bool InPPDirective, encoding::Encoding Encoding, const FormatStyle &Style)
: BreakableToken(Tok, InPPDirective, Encoding, Style),
StartColumn(StartColumn), Prefix(Prefix), Postfix(Postfix),
ContinuationPrefix(ContinuationPrefix),
ContinuationPostfix(ContinuationPostfix),
UnbreakableTailLength(UnbreakableTailLength) {
assert(Tok.TokenText.starts_with(Prefix) && Tok.TokenText.ends_with(Postfix));
Line = Tok.TokenText.substr(
Expand All @@ -274,9 +277,14 @@ void BreakableStringLiteral::insertBreak(unsigned LineIndex,
unsigned TailOffset, Split Split,
unsigned ContentIndent,
WhitespaceManager &Whitespaces) const {

const unsigned SplitEnd = TailOffset + Split.first + Split.second;
const bool IsLastFragment = SplitEnd > Line.size() - UnbreakableTailLength;
StringRef LocalPostfix = (IsLastFragment) ? Postfix : ContinuationPostfix;

Whitespaces.replaceWhitespaceInToken(
Tok, Prefix.size() + TailOffset + Split.first, Split.second, Postfix,
Prefix, InPPDirective, 1, StartColumn);
Tok, ContinuationPrefix.size() + TailOffset + Split.first, Split.second,
LocalPostfix, ContinuationPrefix, InPPDirective, 1, StartColumn);
}

BreakableStringLiteralUsingOperators::BreakableStringLiteralUsingOperators(
Expand All @@ -288,6 +296,10 @@ BreakableStringLiteralUsingOperators::BreakableStringLiteralUsingOperators(
: QuoteStyle == AtDoubleQuotes ? "@\""
: "\"",
/*Postfix=*/QuoteStyle == SingleQuotes ? "'" : "\"",
/*ContinuationPrefix=*/QuoteStyle == SingleQuotes ? "'"
: QuoteStyle == AtDoubleQuotes ? "@\""
: "\"",
/*ContinuationPostfix=*/QuoteStyle == SingleQuotes ? "'" : "\"",
UnbreakableTailLength, InPPDirective, Encoding, Style),
BracesNeeded(Tok.isNot(TT_StringInConcatenation)),
QuoteStyle(QuoteStyle) {
Expand Down
12 changes: 10 additions & 2 deletions clang/lib/Format/BreakableToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ class BreakableStringLiteral : public BreakableToken {
/// after formatting.
BreakableStringLiteral(const FormatToken &Tok, unsigned StartColumn,
StringRef Prefix, StringRef Postfix,
StringRef ContinuationPrefix,
StringRef ContinuationPostfix,
unsigned UnbreakableTailLength, bool InPPDirective,
encoding::Encoding Encoding, const FormatStyle &Style);

Expand All @@ -274,15 +276,21 @@ class BreakableStringLiteral : public BreakableToken {
protected:
// The column in which the token starts.
unsigned StartColumn;
// The prefix a line needs after a break in the token.
// The prefix a line needs at the start
StringRef Prefix;
// The postfix a line needs before introducing a break.
// The postfix a line needs at the end
StringRef Postfix;
// The prefix every line except the first line needs
StringRef ContinuationPrefix;
// The postfix every line except the last line needs
StringRef ContinuationPostfix;
// The token text excluding the prefix and postfix.
StringRef Line;
// Length of the sequence of tokens after this string literal that cannot
// contain line breaks.
unsigned UnbreakableTailLength;
// Whether the string prefix and postfix should be repeated on each line
// when breaking the string.
};

class BreakableStringLiteralUsingOperators : public BreakableStringLiteral {
Expand Down
44 changes: 34 additions & 10 deletions clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2540,22 +2540,46 @@ ContinuationIndenter::createBreakableToken(const FormatToken &Current,

StringRef Prefix;
StringRef Postfix;

// FIXME: Handle whitespace between '_T', '(', '"..."', and ')'.
// FIXME: Store Prefix and Suffix (or PrefixLength and SuffixLength to
// reduce the overhead) for each FormatToken, which is a string, so that we
// don't run multiple checks here on the hot path.
if ((Text.ends_with(Postfix = "\"") &&
(Text.starts_with(Prefix = "@\"") || Text.starts_with(Prefix = "\"") ||
Text.starts_with(Prefix = "u\"") ||
Text.starts_with(Prefix = "U\"") ||
Text.starts_with(Prefix = "u8\"") ||
Text.starts_with(Prefix = "L\""))) ||
(Text.starts_with(Prefix = "_T(\"") &&
Text.ends_with(Postfix = "\")"))) {
if (Text.starts_with(Prefix = "_T(\"") && Text.ends_with(Postfix = "\")")) {
// We need to put `_T("` and `")` on each line because it is a macro
llvm::StringRef ContinuationPrefix = Prefix;
llvm::StringRef ContinuationPostfix = Postfix;

return std::make_unique<BreakableStringLiteral>(
Current, StartColumn, Prefix, Postfix, UnbreakableTailLength,
State.Line->InPPDirective, Encoding, Style);
Current, StartColumn, Prefix, Postfix, ContinuationPrefix,
ContinuationPostfix, UnbreakableTailLength, State.Line->InPPDirective,
Encoding, Style);
}

static const auto PostfixRegex =
llvm::Regex(R"("(_[a-zA-Z_][a-zA-Z0-9_]*)?$)");
llvm::SmallVector<llvm::StringRef, 1> Matches;

if (PostfixRegex.match(Text, &Matches)) {
Postfix = Matches.front();

if ((Text.starts_with(Prefix = "@\"") ||
Text.starts_with(Prefix = "\"") ||
Text.starts_with(Prefix = "u\"") ||
Text.starts_with(Prefix = "U\"") ||
Text.starts_with(Prefix = "u8\"") ||
Text.starts_with(Prefix = "L\""))) {

// Repeat the prefix on every line but don't repeat the suffix
llvm::StringRef ContinuationPrefix = Prefix;
llvm::StringRef ContinuationPostfix = "\"";
return std::make_unique<BreakableStringLiteral>(
Current, StartColumn, Prefix, Postfix, ContinuationPrefix,
ContinuationPostfix, UnbreakableTailLength,
State.Line->InPPDirective, Encoding, Style);
}
}

} else if (Current.is(TT_BlockComment)) {
if (Style.ReflowComments == FormatStyle::RCS_Never ||
// If a comment token switches formatting, like
Expand Down
4 changes: 4 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15838,6 +15838,10 @@ TEST_F(FormatTest, BreaksWideAndNSStringLiterals) {
"@\"NSString literal\";", getGoogleStyleWithColumns(19));
verifyFormat(R"(NSString *s = @"那那那那";)", getLLVMStyleWithColumns(26));

EXPECT_EQ("L\"suffixed \"\n"
"L\"string\"_s;",
format("L\"suffixed string\"_s;", getLLVMStyleWithColumns(19)));

// This input makes clang-format try to split the incomplete unicode escape
// sequence, which used to lead to a crasher.
verifyNoCrash(
Expand Down