krasimir created this revision.
Herald added subscribers: cfe-commits, klimek.
Repository:
rC Clang
https://reviews.llvm.org/D40909
Files:
include/clang/Format/Format.h
lib/Format/ContinuationIndenter.cpp
lib/Format/ContinuationIndenter.h
lib/Format/Format.cpp
unittests/Format/FormatTest.cpp
unittests/Format/FormatTestRawStrings.cpp
Index: unittests/Format/FormatTestRawStrings.cpp
===================================================================
--- unittests/Format/FormatTestRawStrings.cpp
+++ unittests/Format/FormatTestRawStrings.cpp
@@ -9,6 +9,8 @@
#include "clang/Format/Format.h"
+#include <memory>
+
#include "../Tooling/ReplacementTest.h"
#include "FormatTestUtils.h"
@@ -19,6 +21,12 @@
#define DEBUG_TYPE "format-test"
+#define DBG(A) \
+ DEBUG({ \
+ llvm::dbgs() << __LINE__ << ":" << __func__ << ":" << #A << " = " << A \
+ << "\n"; \
+ });
+
using clang::tooling::ReplacementTest;
using clang::tooling::toReplacements;
@@ -65,23 +73,40 @@
FormatStyle getRawStringPbStyleWithColumns(unsigned ColumnLimit) {
FormatStyle Style = getLLVMStyle();
Style.ColumnLimit = ColumnLimit;
- Style.RawStringFormats = {{/*Delimiter=*/"pb",
- /*Kind=*/FormatStyle::LK_TextProto,
- /*BasedOnStyle=*/"google"}};
+ Style.AdditionalLanguageStyles[FormatStyle::LK_TextProto] =
+ std::make_shared<FormatStyle>(
+ getGoogleStyle(FormatStyle::LK_TextProto));
+ Style.RawStringFormats = {{/*Language=*/FormatStyle::LK_TextProto,
+ /*Delimiters=*/{"pb"},
+ /*EnclosingFunctionNames=*/{},
+ /*CanonicalDelimiter=*/""}};
return Style;
}
- FormatStyle getRawStringLLVMCppStyleBasedOn(std::string BasedOnStyle) {
+ FormatStyle getRawStringLLVMCppStyleBasedOn(std::string Name) {
FormatStyle Style = getLLVMStyle();
- Style.RawStringFormats = {{/*Delimiter=*/"cpp",
- /*Kind=*/FormatStyle::LK_Cpp, BasedOnStyle}};
+ FormatStyle BasedOnStyle = getLLVMStyle();
+ getPredefinedStyle(Name, FormatStyle::LK_Cpp, &BasedOnStyle);
+ Style.AdditionalLanguageStyles[FormatStyle::LK_Cpp] =
+ std::make_shared<FormatStyle>(BasedOnStyle);
+ Style.RawStringFormats = {{/*Language=*/FormatStyle::LK_Cpp,
+ /*Delimiters=*/{"cpp"},
+ /*EnclosingFunctionNames=*/{},
+ /*CanonicalDelimiter=*/""}};
+ DBG(BasedOnStyle.PointerAlignment);
return Style;
}
- FormatStyle getRawStringGoogleCppStyleBasedOn(std::string BasedOnStyle) {
+ FormatStyle getRawStringGoogleCppStyleBasedOn(std::string Name) {
FormatStyle Style = getGoogleStyle(FormatStyle::LK_Cpp);
- Style.RawStringFormats = {{/*Delimiter=*/"cpp",
- /*Kind=*/FormatStyle::LK_Cpp, BasedOnStyle}};
+ FormatStyle BasedOnStyle = getLLVMStyle();
+ getPredefinedStyle(Name, FormatStyle::LK_Cpp, &BasedOnStyle);
+ Style.AdditionalLanguageStyles[FormatStyle::LK_Cpp] =
+ std::make_shared<FormatStyle>(BasedOnStyle);
+ Style.RawStringFormats = {{/*Language=*/FormatStyle::LK_Cpp,
+ /*Delimiters=*/{"cpp"},
+ /*EnclosingFunctionNames=*/{},
+ /*CanonicalDelimiter=*/""}};
return Style;
}
@@ -96,17 +121,19 @@
// llvm style puts '*' on the right.
// google style puts '*' on the left.
- // Use the llvm style if the raw string style has no BasedOnStyle.
- expect_eq(R"test(int *i = R"cpp(int *p = nullptr;)cpp")test",
- format(R"test(int * i = R"cpp(int * p = nullptr;)cpp")test",
- getRawStringLLVMCppStyleBasedOn("")));
-
- // Use the google style if the raw string style has BasedOnStyle=google.
+ // Use llvm style outside and the google style inside if the raw string style
+ // is based on google.
expect_eq(R"test(int *i = R"cpp(int* p = nullptr;)cpp")test",
format(R"test(int * i = R"cpp(int * p = nullptr;)cpp")test",
getRawStringLLVMCppStyleBasedOn("google")));
- // Use the llvm style if the raw string style has no BasedOnStyle=llvm.
+ // Use llvm style if the raw string style has no BasedOnStyle.
+ expect_eq(R"test(int *i = R"cpp(int *p = nullptr;)cpp")test",
+ format(R"test(int * i = R"cpp(int * p = nullptr;)cpp")test",
+ getRawStringLLVMCppStyleBasedOn("")));
+
+ // Use google style outside and the llvm style inside if the raw string style
+ // is based on llvm.
expect_eq(R"test(int* i = R"cpp(int *p = nullptr;)cpp")test",
format(R"test(int * i = R"cpp(int * p = nullptr;)cpp")test",
getRawStringGoogleCppStyleBasedOn("llvm")));
@@ -121,29 +148,6 @@
s = R"PB(item:1)PB";
t = R"pb(item:1)pb";)test",
getRawStringPbStyleWithColumns(40)));
-
- FormatStyle MixedStyle = getLLVMStyle();
- MixedStyle.RawStringFormats = {
- {/*Delimiter=*/"cpp", /*Kind=*/FormatStyle::LK_Cpp,
- /*BasedOnStyle=*/"llvm"},
- {/*Delimiter=*/"CPP", /*Kind=*/FormatStyle::LK_Cpp,
- /*BasedOnStyle=*/"google"}};
-
- // Format the 'cpp' raw string with '*' on the right.
- // Format the 'CPP' raw string with '*' on the left.
- // Do not format the 'Cpp' raw string.
- // Do not format non-raw strings.
- expect_eq(R"test(
-a = R"cpp(int *i = 0;)cpp";
-b = R"CPP(int* j = 0;)CPP";
-c = R"Cpp(int * k = 0;)Cpp";
-d = R"cpp(int * k = 0;)Cpp";)test",
- format(R"test(
-a = R"cpp(int * i = 0;)cpp";
-b = R"CPP(int * j = 0;)CPP";
-c = R"Cpp(int * k = 0;)Cpp";
-d = R"cpp(int * k = 0;)Cpp";)test",
- MixedStyle));
}
TEST_F(FormatTestRawStrings, ReformatsShortRawStringsOnSingleLine) {
@@ -210,9 +214,9 @@
P p = TP(R"pb(item_1 <1>
item_2: <2>
item_3 {})pb");)test",
- format(R"test(
+ format(R"test(
P p = TP(R"pb(item_1<1> item_2:<2> item_3{ })pb");)test",
- getRawStringPbStyleWithColumns(40)));
+ getRawStringPbStyleWithColumns(40)));
expect_eq(
R"test(
@@ -515,7 +519,6 @@
format(R"test(
ASSERT_TRUE(ParseFromString(R"pb(item_1: 1 item_2: 2)pb"), ptr);)test",
getRawStringPbStyleWithColumns(40)));
-
}
TEST_F(FormatTestRawStrings, RawStringsInOperands) {
@@ -642,7 +645,6 @@
auto S=(count<3)?R"pb(item_1:1)pb":R"pb(item_2:2,item_3:3)pb";
)test",
getRawStringPbStyleWithColumns(40)));
-
}
TEST_F(FormatTestRawStrings, PrefixAndSuffixAlignment) {
@@ -728,6 +730,13 @@
getRawStringPbStyleWithColumns(20)));
}
+TEST_F(FormatTestRawStrings, UpdatesToCanonicalDelimiters) {
+ FormatStyle Style = getRawStringPbStyleWithColumns(25);
+ Style.RawStringFormats[0].CanonicalDelimiter = "proto";
+ expect_eq(R"test(a = R"proto(key: value)proto";)test",
+ format(R"test(a = R"pb(key:value)pb";)test", Style));
+}
+
} // end namespace
} // end namespace format
} // end namespace clang
Index: unittests/Format/FormatTest.cpp
===================================================================
--- unittests/Format/FormatTest.cpp
+++ unittests/Format/FormatTest.cpp
@@ -10402,16 +10402,27 @@
Style.RawStringFormats.clear();
std::vector<FormatStyle::RawStringFormat> ExpectedRawStringFormats = {
- {"pb", FormatStyle::LK_TextProto, "llvm"},
- {"cpp", FormatStyle::LK_Cpp, "google"}};
+ {FormatStyle::LK_TextProto,
+ {"pb", "proto"},
+ {"PARSE_TEXT_PROTO"},
+ "textproto"},
+ {FormatStyle::LK_Cpp, {"cc", "cpp"}, {"C_CODEBLOCK", "CPPEVAL"}, ""}};
CHECK_PARSE("RawStringFormats:\n"
- " - Delimiter: 'pb'\n"
- " Language: TextProto\n"
- " BasedOnStyle: llvm\n"
- " - Delimiter: 'cpp'\n"
- " Language: Cpp\n"
- " BasedOnStyle: google",
+ " - Language: TextProto\n"
+ " Delimiters:\n"
+ " - 'pb'\n"
+ " - 'proto'\n"
+ " EnclosingFunctionNames:\n"
+ " - 'PARSE_TEXT_PROTO'\n"
+ " CanonicalDelimiter: 'textproto'\n"
+ " - Language: Cpp\n"
+ " Delimiters:\n"
+ " - 'cc'\n"
+ " - 'cpp'\n"
+ " EnclosingFunctionNames:\n"
+ " - 'C_CODEBLOCK'\n"
+ " - 'CPPEVAL'",
RawStringFormats, ExpectedRawStringFormats);
}
Index: lib/Format/Format.cpp
===================================================================
--- lib/Format/Format.cpp
+++ lib/Format/Format.cpp
@@ -455,9 +455,10 @@
template <> struct MappingTraits<FormatStyle::RawStringFormat> {
static void mapping(IO &IO, FormatStyle::RawStringFormat &Format) {
- IO.mapOptional("Delimiter", Format.Delimiter);
IO.mapOptional("Language", Format.Language);
- IO.mapOptional("BasedOnStyle", Format.BasedOnStyle);
+ IO.mapOptional("Delimiters", Format.Delimiters);
+ IO.mapOptional("EnclosingFunctionNames", Format.EnclosingFunctionNames);
+ IO.mapOptional("CanonicalDelimiter", Format.CanonicalDelimiter);
}
};
@@ -641,7 +642,10 @@
LLVMStyle.SpacesBeforeTrailingComments = 1;
LLVMStyle.Standard = FormatStyle::LS_Cpp11;
LLVMStyle.UseTab = FormatStyle::UT_Never;
- LLVMStyle.RawStringFormats = {{"pb", FormatStyle::LK_TextProto, "google"}};
+ LLVMStyle.RawStringFormats = {
+ {FormatStyle::LK_TextProto, /*Delimiters=*/{"pb", "PB"},
+ /*EnclosingFunctionNames=*/{}, /*CanonicalDelimiter=*/""},
+ };
LLVMStyle.ReflowComments = true;
LLVMStyle.SpacesInParentheses = false;
LLVMStyle.SpacesInSquareBrackets = false;
@@ -829,6 +833,8 @@
return NoStyle;
}
+/// \brief Gets a predefined style for the specified language by name.
+
bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language,
FormatStyle *Style) {
if (Name.equals_lower("llvm")) {
@@ -888,14 +894,31 @@
// Look for a suitable configuration starting from the end, so we can
// find the configuration for the specific language first, and the default
// configuration (which can only be at slot 0) after it.
+ bool LanguageFound = false;
for (int i = Styles.size() - 1; i >= 0; --i) {
- if (Styles[i].Language == Language ||
- Styles[i].Language == FormatStyle::LK_None) {
+ if (!LanguageFound && (Styles[i].Language == Language ||
+ Styles[i].Language == FormatStyle::LK_None)) {
*Style = Styles[i];
Style->Language = Language;
- return make_error_code(ParseError::Success);
+ LanguageFound = true;
+ }
+ if (Styles[i].Language == FormatStyle::LK_None) {
+ for (unsigned AdditionalLanguage = 0;
+ AdditionalLanguage < FormatStyle::LK_End; ++AdditionalLanguage) {
+ FormatStyle AdditionalLanguageStyle = Styles[i];
+ AdditionalLanguageStyle.Language =
+ static_cast<FormatStyle::LanguageKind>(AdditionalLanguage);
+ if (!Style->AdditionalLanguageStyles[AdditionalLanguage])
+ Style->AdditionalLanguageStyles[AdditionalLanguage].reset(
+ new FormatStyle(AdditionalLanguageStyle));
+ }
+ continue;
}
+ Style->AdditionalLanguageStyles[Styles[i].Language].reset(
+ new FormatStyle(Styles[i]));
}
+ if (LanguageFound)
+ return make_error_code(ParseError::Success);
return make_error_code(ParseError::Unsuitable);
}
Index: lib/Format/ContinuationIndenter.h
===================================================================
--- lib/Format/ContinuationIndenter.h
+++ lib/Format/ContinuationIndenter.h
@@ -37,11 +37,11 @@
class WhitespaceManager;
struct RawStringFormatStyleManager {
- llvm::StringMap<FormatStyle> DelimiterStyle;
+ llvm::StringMap<const FormatStyle*> DelimiterStyle;
RawStringFormatStyleManager(const FormatStyle &CodeStyle);
- llvm::Optional<FormatStyle> get(StringRef Delimiter) const;
+ const FormatStyle *getDelimiterStyle(StringRef Delimiter) const;
};
class ContinuationIndenter {
Index: lib/Format/ContinuationIndenter.cpp
===================================================================
--- lib/Format/ContinuationIndenter.cpp
+++ lib/Format/ContinuationIndenter.cpp
@@ -23,6 +23,11 @@
#define DEBUG_TYPE "format-indenter"
+#define DBG(A) DEBUG({ \
+ llvm::dbgs() << __LINE__ << ":" << __func__ << ":" \
+ << #A << " = " << A << "\n"; \
+});
+
namespace clang {
namespace format {
@@ -102,25 +107,32 @@
return Delimiter;
}
+static StringRef
+getCanonicalRawStringDelimiter(const FormatStyle &Style,
+ FormatStyle::LanguageKind Language) {
+ for (const auto &Format : llvm::reverse(Style.RawStringFormats)) {
+ if (Format.Language == Language)
+ return StringRef(Format.CanonicalDelimiter);
+ }
+ return "";
+}
+
RawStringFormatStyleManager::RawStringFormatStyleManager(
const FormatStyle &CodeStyle) {
for (const auto &RawStringFormat : CodeStyle.RawStringFormats) {
- FormatStyle Style;
- if (!getPredefinedStyle(RawStringFormat.BasedOnStyle,
- RawStringFormat.Language, &Style)) {
- Style = getLLVMStyle();
- Style.Language = RawStringFormat.Language;
+ for (StringRef Delimiter : RawStringFormat.Delimiters) {
+ DelimiterStyle.insert(
+ {Delimiter,
+ CodeStyle.AdditionalLanguageStyles[RawStringFormat.Language].get()});
}
- Style.ColumnLimit = CodeStyle.ColumnLimit;
- DelimiterStyle.insert({RawStringFormat.Delimiter, Style});
}
}
-llvm::Optional<FormatStyle>
-RawStringFormatStyleManager::get(StringRef Delimiter) const {
+const FormatStyle *
+RawStringFormatStyleManager::getDelimiterStyle(StringRef Delimiter) const {
auto It = DelimiterStyle.find(Delimiter);
if (It == DelimiterStyle.end())
- return None;
+ return nullptr;
return It->second;
}
@@ -1291,14 +1303,22 @@
const FormatToken &Current, LineState &State,
const FormatStyle &RawStringStyle, bool DryRun) {
unsigned StartColumn = State.Column - Current.ColumnWidth;
- auto Delimiter = *getRawStringDelimiter(Current.TokenText);
+ StringRef OldDelimiter = *getRawStringDelimiter(Current.TokenText);
+ StringRef NewDelimiter =
+ getCanonicalRawStringDelimiter(Style, RawStringStyle.Language);
+ if (NewDelimiter.empty() || OldDelimiter.empty())
+ NewDelimiter = OldDelimiter;
+ bool UpdateDelimiter = (NewDelimiter != OldDelimiter);
+
// The text of a raw string is between the leading 'R"delimiter(' and the
// trailing 'delimiter)"'.
- unsigned PrefixSize = 3 + Delimiter.size();
- unsigned SuffixSize = 2 + Delimiter.size();
+ unsigned OldPrefixSize = 3 + OldDelimiter.size();
+ unsigned NewPrefixSize = 3 + NewDelimiter.size();
+ unsigned OldSuffixSize = 2 + OldDelimiter.size();
+ unsigned NewSuffixSize = 2 + NewDelimiter.size();
// The first start column is the column the raw text starts.
- unsigned FirstStartColumn = StartColumn + PrefixSize;
+ unsigned FirstStartColumn = StartColumn + NewPrefixSize;
// The next start column is the intended indentation a line break inside
// the raw string at level 0. It is determined by the following rules:
@@ -1309,7 +1329,7 @@
// These rules have the advantage that the formatted content both does not
// violate the rectangle rule and visually flows within the surrounding
// source.
- bool ContentStartsOnNewline = Current.TokenText[PrefixSize] == '\n';
+ bool ContentStartsOnNewline = Current.TokenText[OldPrefixSize] == '\n';
unsigned NextStartColumn = ContentStartsOnNewline
? State.Stack.back().Indent + Style.IndentWidth
: FirstStartColumn;
@@ -1323,11 +1343,11 @@
// - if the raw string prefix does not start on a newline, it is the current
// indent.
unsigned LastStartColumn = Current.NewlinesBefore
- ? FirstStartColumn - PrefixSize
+ ? FirstStartColumn - NewPrefixSize
: State.Stack.back().Indent;
std::string RawText =
- Current.TokenText.substr(PrefixSize).drop_back(SuffixSize);
+ Current.TokenText.substr(OldPrefixSize).drop_back(OldSuffixSize);
std::pair<tooling::Replacements, unsigned> Fixes = internal::reformat(
RawStringStyle, RawText, {tooling::Range(0, RawText.size())},
@@ -1341,8 +1361,33 @@
return 0;
}
if (!DryRun) {
+ if (UpdateDelimiter) {
+ // In 'R"delimiter(...', the delimiter starts 2 characters after the start
+ // of the token.
+ SourceLocation PrefixDelimiterStart =
+ Current.Tok.getLocation().getLocWithOffset(2);
+ auto PrefixErr = Whitespaces.addReplacement(tooling::Replacement(
+ SourceMgr, PrefixDelimiterStart, OldDelimiter.size(), NewDelimiter));
+ if (PrefixErr) {
+ llvm::errs()
+ << "Failed to update the prefix delimiter of a raw string: "
+ << llvm::toString(std::move(PrefixErr)) << "\n";
+ }
+ // In 'R"delimiter(...)delimiter"', the suffix delimiter starts at
+ // position length - 1 - |delimiter|.
+ SourceLocation SuffixDelimiterStart =
+ Current.Tok.getLocation().getLocWithOffset(Current.TokenText.size() -
+ 1 - OldDelimiter.size());
+ auto SuffixErr = Whitespaces.addReplacement(tooling::Replacement(
+ SourceMgr, SuffixDelimiterStart, OldDelimiter.size(), NewDelimiter));
+ if (SuffixErr) {
+ llvm::errs()
+ << "Failed to update the suffix delimiter of a raw string: "
+ << llvm::toString(std::move(SuffixErr)) << "\n";
+ }
+ }
SourceLocation OriginLoc =
- Current.Tok.getLocation().getLocWithOffset(PrefixSize);
+ Current.Tok.getLocation().getLocWithOffset(OldPrefixSize);
for (const tooling::Replacement &Fix : Fixes.first) {
auto Err = Whitespaces.addReplacement(tooling::Replacement(
SourceMgr, OriginLoc.getLocWithOffset(Fix.getOffset()),
@@ -1355,7 +1400,7 @@
}
unsigned RawLastLineEndColumn = getLastLineEndColumn(
*NewCode, FirstStartColumn, Style.TabWidth, Encoding);
- State.Column = RawLastLineEndColumn + SuffixSize;
+ State.Column = RawLastLineEndColumn + NewSuffixSize;
return Fixes.second;
}
@@ -1383,6 +1428,7 @@
// that can be reformatted.
auto RawStringStyle = getRawStringStyle(Current, State);
if (RawStringStyle) {
+ DBG(RawStringStyle->PointerAlignment);
Penalty = reformatRawStringLiteral(Current, State, *RawStringStyle, DryRun);
} else if (Current.IsMultiline && Current.isNot(TT_BlockComment)) {
// Don't break multi-line tokens other than block comments and raw string
@@ -1436,10 +1482,13 @@
auto Delimiter = getRawStringDelimiter(Current.TokenText);
if (!Delimiter)
return None;
- auto RawStringStyle = RawStringFormats.get(*Delimiter);
- if (!RawStringStyle)
+ DBG(*Delimiter);
+ auto DelimiterStyle = RawStringFormats.getDelimiterStyle(*Delimiter);
+ if (!DelimiterStyle)
return None;
- RawStringStyle->ColumnLimit = getColumnLimit(State);
+ DBG(DelimiterStyle->PointerAlignment);
+ FormatStyle RawStringStyle = *DelimiterStyle;
+ RawStringStyle.ColumnLimit = getColumnLimit(State);
return RawStringStyle;
}
Index: include/clang/Format/Format.h
===================================================================
--- include/clang/Format/Format.h
+++ include/clang/Format/Format.h
@@ -18,6 +18,8 @@
#include "clang/Basic/LangOptions.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include <array>
#include <system_error>
namespace clang {
@@ -1211,7 +1213,9 @@
LK_TableGen,
/// Should be used for Protocol Buffer messages in text format
/// (https://developers.google.com/protocol-buffers/).
- LK_TextProto
+ LK_TextProto,
+ /// Do not use. Keep at last position.
+ LK_End,
};
bool isCpp() const { return Language == LK_Cpp || Language == LK_ObjC; }
@@ -1363,17 +1367,16 @@
/// See documentation of ``RawStringFormats``.
struct RawStringFormat {
- /// \brief The delimiter that this raw string format matches.
- std::string Delimiter;
/// \brief The language of this raw string.
LanguageKind Language;
- /// \brief The style name on which this raw string format is based on.
- /// If not specified, the raw string format is based on the style that this
- /// format is based on.
- std::string BasedOnStyle;
+ std::vector<std::string> Delimiters;
+ std::vector<std::string> EnclosingFunctionNames;
+ std::string CanonicalDelimiter;
+
bool operator==(const RawStringFormat &Other) const {
- return Delimiter == Other.Delimiter && Language == Other.Language &&
- BasedOnStyle == Other.BasedOnStyle;
+ return Language == Other.Language && Delimiters == Other.Delimiters &&
+ EnclosingFunctionNames == Other.EnclosingFunctionNames &&
+ CanonicalDelimiter == Other.CanonicalDelimiter;
}
};
@@ -1685,8 +1688,10 @@
Standard == R.Standard && TabWidth == R.TabWidth &&
UseTab == R.UseTab;
}
-};
+ std::array<std::shared_ptr<FormatStyle>, FormatStyle::LK_End>
+ AdditionalLanguageStyles;
+};
/// \brief Returns a format style complying with the LLVM coding standards:
/// http://llvm.org/docs/CodingStandards.html.
FormatStyle getLLVMStyle();
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits