Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -994,9 +994,9 @@ .. code-block:: c++ - Constructor() - : initializer1(), - initializer2() + Constructor() + : initializer1(), + initializer2() * ``BCIS_BeforeComma`` (in configuration: ``BeforeComma``) Break constructor initializers before the colon and commas, and align @@ -1004,18 +1004,18 @@ .. code-block:: c++ - Constructor() - : initializer1() - , initializer2() + Constructor() + : initializer1() + , initializer2() * ``BCIS_AfterColon`` (in configuration: ``AfterColon``) Break constructor initializers after the colon and commas. .. code-block:: c++ - Constructor() : - initializer1(), - initializer2() + Constructor() : + initializer1(), + initializer2() @@ -1201,7 +1201,8 @@ * ``IBS_Regroup`` (in configuration: ``Regroup``) Merge multiple ``#include`` blocks together and sort as one. - Then split into groups based on category priority. See ``IncludeCategories``. + Then split into groups based on category priority. See + ``IncludeCategories``. .. code-block:: c++ @@ -1416,6 +1417,9 @@ Should be used for Protocol Buffer messages in text format (https://developers.google.com/protocol-buffers/). + * ``LK_End`` (in configuration: ``End``) + Do not use. Keep at last position. + **MacroBlockBegin** (``std::string``) @@ -1577,24 +1581,39 @@ **RawStringFormats** (``std::vector``) - Raw string delimiters denoting that the raw string contents are - code in a particular language and can be reformatted. + Defines hints for detecting supported languages code blocks in + raw strings. + + A raw string with a matching delimiter or a matching enclosing function + name will be reformatted assuming the specified language based on a + predefined style for that language defined in the .clang-format file. + + A matching delimiter of a raw string takes precedence over a matching + enclosing function name for determining the language of the raw string + contents. + + If a canonical delimiter is specified, occurences of other delimiters for + the same language will be updated to the canonical if possible. - A raw string with a matching delimiter will be reformatted assuming the - specified language based on a predefined style given by 'BasedOnStyle'. - If 'BasedOnStyle' is not found, the formatting is based on llvm style. + For a particular supported language at most one specification should + exist. To configure this in the .clang-format file, use: .. code-block:: yaml RawStringFormats: - - Delimiter: 'pb' - Language: TextProto - BasedOnStyle: llvm - - Delimiter: 'proto' - Language: TextProto - BasedOnStyle: google + - Language: TextProto + Delimiters: + - 'pb' + - 'proto' + EnclosingFunctionNames: + - 'PARSE_TEXT_PROTO' + - Language: Cpp + Delimiters: + - 'cc' + - 'cpp' + CanonicalDelimiter: 'cc' **ReflowComments** (``bool``) If ``true``, clang-format will attempt to re-flow comments. Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -18,6 +18,7 @@ #include "clang/Basic/LangOptions.h" #include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/ArrayRef.h" +#include #include namespace clang { @@ -1211,7 +1212,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,36 +1366,53 @@ /// 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. + /// \brief The language of this raw string format. 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; + /// \brief A list of raw string delimiters that match this language. + std::vector Delimiters; + /// \brief A list of enclosing function names that match this language. + std::vector EnclosingFunctionNames; + /// \brief The canonical delimiter for this language. + 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; } }; - /// \brief Raw string delimiters denoting that the raw string contents are - /// code in a particular language and can be reformatted. + /// \brief Defines hints for detecting supported languages code blocks in + /// raw strings. + /// + /// A raw string with a matching delimiter or a matching enclosing function + /// name will be reformatted assuming the specified language based on a + /// predefined style for that language defined in the .clang-format file. + /// + /// A matching delimiter of a raw string takes precedence over a matching + /// enclosing function name for determining the language of the raw string + /// contents. /// - /// A raw string with a matching delimiter will be reformatted assuming the - /// specified language based on a predefined style given by 'BasedOnStyle'. - /// If 'BasedOnStyle' is not found, the formatting is based on llvm style. + /// If a canonical delimiter is specified, occurences of other delimiters for + /// the same language will be updated to the canonical if possible. + /// + /// For a particular supported language at most one specification should + /// exist. /// /// To configure this in the .clang-format file, use: /// \code{.yaml} /// RawStringFormats: - /// - Delimiter: 'pb' - /// Language: TextProto - /// BasedOnStyle: llvm - /// - Delimiter: 'proto' - /// Language: TextProto - /// BasedOnStyle: google + /// - Language: TextProto + /// Delimiters: + /// - 'pb' + /// - 'proto' + /// EnclosingFunctionNames: + /// - 'PARSE_TEXT_PROTO' + /// - Language: Cpp + /// Delimiters: + /// - 'cc' + /// - 'cpp' + /// CanonicalDelimiter: 'cc' /// \endcode std::vector RawStringFormats; @@ -1685,8 +1705,12 @@ Standard == R.Standard && TabWidth == R.TabWidth && UseTab == R.UseTab; } -}; + // A mapping from FormatStyle::LanguageKind to a format style to use for + // code blocks in that language. + std::array, FormatStyle::LK_End> + AdditionalLanguageStyles; +}; /// \brief Returns a format style complying with the LLVM coding standards: /// http://llvm.org/docs/CodingStandards.html. FormatStyle getLLVMStyle(); Index: lib/Format/ContinuationIndenter.h =================================================================== --- lib/Format/ContinuationIndenter.h +++ lib/Format/ContinuationIndenter.h @@ -37,11 +37,15 @@ class WhitespaceManager; struct RawStringFormatStyleManager { - llvm::StringMap DelimiterStyle; + llvm::StringMap DelimiterStyle; + llvm::StringMap EnclosingFunctionNameStyle; RawStringFormatStyleManager(const FormatStyle &CodeStyle); - llvm::Optional get(StringRef Delimiter) const; + const FormatStyle *getDelimiterStyle(StringRef Delimiter) const; + + const FormatStyle * + getEnclosingFunctionNameStyle(StringRef EnclosingFunctionName) const; }; class ContinuationIndenter { Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ lib/Format/ContinuationIndenter.cpp @@ -102,25 +102,46 @@ 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()}); + } + for (StringRef EnclosingFunctionName : + RawStringFormat.EnclosingFunctionNames) { + EnclosingFunctionNameStyle.insert( + {EnclosingFunctionName, + CodeStyle.AdditionalLanguageStyles[RawStringFormat.Language].get()}); } - Style.ColumnLimit = CodeStyle.ColumnLimit; - DelimiterStyle.insert({RawStringFormat.Delimiter, Style}); } } -llvm::Optional -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; +} + +const FormatStyle *RawStringFormatStyleManager::getEnclosingFunctionNameStyle( + StringRef EnclosingFunctionName) const { + auto It = EnclosingFunctionNameStyle.find(EnclosingFunctionName); + if (It == EnclosingFunctionNameStyle.end()) + return nullptr; return It->second; } @@ -1291,14 +1312,30 @@ 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; // 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 OldSuffixSize = 2 + OldDelimiter.size(); + std::string RawText = + Current.TokenText.substr(OldPrefixSize).drop_back(OldSuffixSize); + if (NewDelimiter != OldDelimiter) { + // Don't update to the canonical delimiter 'deli' if ')deli"' occurs in the + // raw string. + std::string CanonicalDelimiterSuffix = (")" + NewDelimiter + "\"").str(); + if (StringRef(RawText).contains(CanonicalDelimiterSuffix)) + NewDelimiter = OldDelimiter; + } - // The first start column is the column the raw text starts. - unsigned FirstStartColumn = StartColumn + PrefixSize; + unsigned NewPrefixSize = 3 + NewDelimiter.size(); + unsigned NewSuffixSize = 2 + NewDelimiter.size(); + + // The first start column is the column the raw text starts after formatting. + 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 +1346,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,12 +1360,9 @@ // - 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); - std::pair Fixes = internal::reformat( RawStringStyle, RawText, {tooling::Range(0, RawText.size())}, FirstStartColumn, NextStartColumn, LastStartColumn, "", @@ -1341,8 +1375,33 @@ return 0; } if (!DryRun) { + if (NewDelimiter != OldDelimiter) { + // 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 +1414,7 @@ } unsigned RawLastLineEndColumn = getLastLineEndColumn( *NewCode, FirstStartColumn, Style.TabWidth, Encoding); - State.Column = RawLastLineEndColumn + SuffixSize; + State.Column = RawLastLineEndColumn + NewSuffixSize; return Fixes.second; } @@ -1428,6 +1487,21 @@ return Penalty; } +static StringRef getEnclosingFunctionName(const FormatToken& Current) { + // Look for: 'function(' or 'function(' before Current. + auto Tok = Current.getPreviousNonComment(); + if (!Tok || !Tok->is(tok::l_paren)) return ""; + Tok = Tok->getPreviousNonComment(); + if (!Tok) return ""; + if (Tok->is(TT_TemplateCloser)) { + Tok = Tok->MatchingParen; + if (Tok) + Tok = Tok->getPreviousNonComment(); + } + if (!Tok || !Tok->is(tok::identifier)) return ""; + return Tok->TokenText; +} + llvm::Optional ContinuationIndenter::getRawStringStyle(const FormatToken &Current, const LineState &State) { @@ -1436,11 +1510,16 @@ auto Delimiter = getRawStringDelimiter(Current.TokenText); if (!Delimiter) return None; - auto RawStringStyle = RawStringFormats.get(*Delimiter); + auto RawStringStyle = RawStringFormats.getDelimiterStyle(*Delimiter); + if (!RawStringStyle) + RawStringStyle = RawStringFormats.getEnclosingFunctionNameStyle( + getEnclosingFunctionName(Current)); if (!RawStringStyle) return None; - RawStringStyle->ColumnLimit = getColumnLimit(State); - return RawStringStyle; + + FormatStyle ResultStyle = *RawStringStyle; + ResultStyle.ColumnLimit = getColumnLimit(State); + return ResultStyle; } std::unique_ptr ContinuationIndenter::createBreakableToken( Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -38,7 +38,6 @@ #include "llvm/Support/Regex.h" #include "llvm/Support/YAMLTraits.h" #include -#include #include #define DEBUG_TYPE "format-formatter" @@ -455,9 +454,10 @@ template <> struct MappingTraits { 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 +641,6 @@ LLVMStyle.SpacesBeforeTrailingComments = 1; LLVMStyle.Standard = FormatStyle::LS_Cpp11; LLVMStyle.UseTab = FormatStyle::UT_Never; - LLVMStyle.RawStringFormats = {{"pb", FormatStyle::LK_TextProto, "google"}}; LLVMStyle.ReflowComments = true; LLVMStyle.SpacesInParentheses = false; LLVMStyle.SpacesInSquareBrackets = false; @@ -695,6 +694,25 @@ GoogleStyle.ObjCSpaceAfterProperty = false; GoogleStyle.ObjCSpaceBeforeProtocolList = false; GoogleStyle.PointerAlignment = FormatStyle::PAS_Left; + GoogleStyle.RawStringFormats = { + {FormatStyle::LK_TextProto, + /*Delimiters=*/ + { + "pb", + "PB", + "proto", + "PROTO", + "textproto", + "TEXTPROTO", + }, + /*EnclosingFunctionNames=*/ + { + "EqualsProto", + "PARSE_TEXT_PROTO", + "ParseTextProto", + }, + /*CanonicalDelimiter=*/""}, + }; GoogleStyle.SpacesBeforeTrailingComments = 2; GoogleStyle.Standard = FormatStyle::LS_Auto; @@ -888,13 +906,34 @@ // 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; + break; + } + } + if (LanguageFound) { + for (int i = Styles.size() - 1; i >= 0; --i) { + if (Styles[i].Language == FormatStyle::LK_None) { + for (unsigned AdditionalLanguage = 0; + AdditionalLanguage < FormatStyle::LK_End; ++AdditionalLanguage) { + FormatStyle AdditionalLanguageStyle = Styles[i]; + AdditionalLanguageStyle.Language = + static_cast(AdditionalLanguage); + if (!Style->AdditionalLanguageStyles[AdditionalLanguage]) + Style->AdditionalLanguageStyles[AdditionalLanguage].reset( + new FormatStyle(AdditionalLanguageStyle)); + } + continue; + } + Style->AdditionalLanguageStyles[Styles[i].Language].reset( + new FormatStyle(Styles[i])); } + return make_error_code(ParseError::Success); } return make_error_code(ParseError::Unsuitable); } Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -10402,16 +10402,27 @@ Style.RawStringFormats.clear(); std::vector 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: unittests/Format/FormatTestRawStrings.cpp =================================================================== --- unittests/Format/FormatTestRawStrings.cpp +++ unittests/Format/FormatTestRawStrings.cpp @@ -65,23 +65,39 @@ 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( + 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(BasedOnStyle); + Style.RawStringFormats = {{/*Language=*/FormatStyle::LK_Cpp, + /*Delimiters=*/{"cpp"}, + /*EnclosingFunctionNames=*/{}, + /*CanonicalDelimiter=*/""}}; 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(BasedOnStyle); + Style.RawStringFormats = {{/*Language=*/FormatStyle::LK_Cpp, + /*Delimiters=*/{"cpp"}, + /*EnclosingFunctionNames=*/{}, + /*CanonicalDelimiter=*/""}}; return Style; } @@ -96,17 +112,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 +139,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 +205,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 +510,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 +636,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 +721,41 @@ 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)); + + // Don't update to canonical delimiter if it occurs as a raw string suffix in + // the raw string content. + expect_eq(R"test(a = R"pb(key: ")proto")pb";)test", + format(R"test(a = R"pb(key:")proto")pb";)test", Style)); +} + +TEST_F(FormatTestRawStrings, FormatsRawStringsWithEnclosingFunctionName) { + FormatStyle Style = getRawStringPbStyleWithColumns(40); + Style.RawStringFormats[0].EnclosingFunctionNames.push_back( + "PARSE_TEXT_PROTO"); + Style.RawStringFormats[0].EnclosingFunctionNames.push_back("ParseTextProto"); + expect_eq(R"test(a = PARSE_TEXT_PROTO(R"(key: value)");)test", + format(R"test(a = PARSE_TEXT_PROTO(R"(key:value)");)test", Style)); + + expect_eq(R"test( +a = PARSE_TEXT_PROTO /**/ ( + /**/ R"(key: value)");)test", + format(R"test( +a = PARSE_TEXT_PROTO/**/(/**/R"(key:value)");)test", + Style)); + + expect_eq(R"test( +a = ParseTextProto( + R"(key: value)");)test", + format(R"test( +a = ParseTextProto(R"(key:value)");)test", + Style)); +} + } // end namespace } // end namespace format } // end namespace clang