[clang-format] adds enclosing function detection to raw string formatting

Summary: This patch adds enclosing function detection to raw string formatting.

Reviewers: bkramer

Reviewed By: bkramer

Subscribers: klimek, cfe-commits

Differential Revision: https://reviews.llvm.org/D42167

llvm-svn: 322678
This commit is contained in:
Krasimir Georgiev 2018-01-17 16:17:26 +00:00
parent 2c6fe505b1
commit 2537e22094
7 changed files with 170 additions and 53 deletions

View File

@ -994,9 +994,9 @@ the configuration (without a prefix: ``Auto``).
.. 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 @@ the configuration (without a prefix: ``Auto``).
.. 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 @@ the configuration (without a prefix: ``Auto``).
* ``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++
@ -1577,24 +1578,38 @@ the configuration (without a prefix: ``Auto``).
**RawStringFormats** (``std::vector<RawStringFormat>``)
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 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.
A raw string with a matching delimiter or a matching enclosing function
name will be reformatted assuming the specified language based on the
style for that language defined in the .clang-format file. If no style has
been defined in the .clang-format file for the specific language, a
predefined style given by 'BasedOnStyle' is used. If 'BasedOnStyle' is not
found, the formatting is based on llvm style. A matching delimiter takes
precedence over a matching enclosing function name for determining the
language of the raw string contents.
There should be at most one specification per language and each delimiter
and enclosing function should not occur in multiple specifications.
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'
EnclosingFunctions:
- 'PARSE_TEXT_PROTO'
BasedOnStyle: google
- Language: Cpp
Delimiters:
- 'cc'
- 'cpp'
BasedOnStyle: llvm
**ReflowComments** (``bool``)
If ``true``, clang-format will attempt to re-flow comments.

View File

@ -1367,12 +1367,15 @@ struct FormatStyle {
LanguageKind Language;
/// \brief A list of raw string delimiters that match this language.
std::vector<std::string> Delimiters;
/// \brief A list of enclosing function names that match this language.
std::vector<std::string> EnclosingFunctions;
/// \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;
bool operator==(const RawStringFormat &Other) const {
return Language == Other.Language && Delimiters == Other.Delimiters &&
EnclosingFunctions == Other.EnclosingFunctions &&
BasedOnStyle == Other.BasedOnStyle;
}
};
@ -1380,12 +1383,17 @@ struct FormatStyle {
/// \brief Defines hints for detecting supported languages code blocks in raw
/// strings.
///
/// A raw string with a matching delimiter will be reformatted assuming the
/// specified language based on the style for that language defined in the
/// .clang-format file. If no style has been defined in the .clang-format file
/// for the specific language, a predefined style given by 'BasedOnStyle' is
/// used. If 'BasedOnStyle' is not found, the formatting is based on llvm
/// style.
/// A raw string with a matching delimiter or a matching enclosing function
/// name will be reformatted assuming the specified language based on the
/// style for that language defined in the .clang-format file. If no style has
/// been defined in the .clang-format file for the specific language, a
/// predefined style given by 'BasedOnStyle' is used. If 'BasedOnStyle' is not
/// found, the formatting is based on llvm style. A matching delimiter takes
/// precedence over a matching enclosing function name for determining the
/// language of the raw string contents.
///
/// There should be at most one specification per language and each delimiter
/// and enclosing function should not occur in multiple specifications.
///
/// To configure this in the .clang-format file, use:
/// \code{.yaml}
@ -1394,6 +1402,8 @@ struct FormatStyle {
/// Delimiters:
/// - 'pb'
/// - 'proto'
/// EnclosingFunctions:
/// - 'PARSE_TEXT_PROTO'
/// BasedOnStyle: google
/// - Language: Cpp
/// Delimiters:

View File

@ -105,32 +105,44 @@ static llvm::Optional<StringRef> getRawStringDelimiter(StringRef TokenText) {
RawStringFormatStyleManager::RawStringFormatStyleManager(
const FormatStyle &CodeStyle) {
for (const auto &RawStringFormat : CodeStyle.RawStringFormats) {
for (StringRef Delimiter : RawStringFormat.Delimiters) {
llvm::Optional<FormatStyle> LanguageStyle =
CodeStyle.GetLanguageStyle(RawStringFormat.Language);
if (!LanguageStyle) {
FormatStyle PredefinedStyle;
if (!getPredefinedStyle(RawStringFormat.BasedOnStyle,
RawStringFormat.Language, &PredefinedStyle)) {
PredefinedStyle = getLLVMStyle();
PredefinedStyle.Language = RawStringFormat.Language;
}
LanguageStyle = PredefinedStyle;
llvm::Optional<FormatStyle> LanguageStyle =
CodeStyle.GetLanguageStyle(RawStringFormat.Language);
if (!LanguageStyle) {
FormatStyle PredefinedStyle;
if (!getPredefinedStyle(RawStringFormat.BasedOnStyle,
RawStringFormat.Language, &PredefinedStyle)) {
PredefinedStyle = getLLVMStyle();
PredefinedStyle.Language = RawStringFormat.Language;
}
LanguageStyle->ColumnLimit = CodeStyle.ColumnLimit;
LanguageStyle = PredefinedStyle;
}
LanguageStyle->ColumnLimit = CodeStyle.ColumnLimit;
for (StringRef Delimiter : RawStringFormat.Delimiters) {
DelimiterStyle.insert({Delimiter, *LanguageStyle});
}
for (StringRef EnclosingFunction : RawStringFormat.EnclosingFunctions) {
EnclosingFunctionStyle.insert({EnclosingFunction, *LanguageStyle});
}
}
}
llvm::Optional<FormatStyle>
RawStringFormatStyleManager::get(StringRef Delimiter) const {
RawStringFormatStyleManager::getDelimiterStyle(StringRef Delimiter) const {
auto It = DelimiterStyle.find(Delimiter);
if (It == DelimiterStyle.end())
return None;
return It->second;
}
llvm::Optional<FormatStyle>
RawStringFormatStyleManager::getEnclosingFunctionStyle(
StringRef EnclosingFunction) const {
auto It = EnclosingFunctionStyle.find(EnclosingFunction);
if (It == EnclosingFunctionStyle.end())
return None;
return It->second;
}
ContinuationIndenter::ContinuationIndenter(const FormatStyle &Style,
const AdditionalKeywords &Keywords,
const SourceManager &SourceMgr,
@ -1437,6 +1449,26 @@ unsigned ContinuationIndenter::handleEndOfLine(const FormatToken &Current,
return Penalty;
}
// Returns the enclosing function name of a token, or the empty string if not
// found.
static StringRef getEnclosingFunctionName(const FormatToken &Current) {
// Look for: 'function(' or 'function<templates>(' 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<FormatStyle>
ContinuationIndenter::getRawStringStyle(const FormatToken &Current,
const LineState &State) {
@ -1445,7 +1477,10 @@ ContinuationIndenter::getRawStringStyle(const FormatToken &Current,
auto Delimiter = getRawStringDelimiter(Current.TokenText);
if (!Delimiter)
return None;
auto RawStringStyle = RawStringFormats.get(*Delimiter);
auto RawStringStyle = RawStringFormats.getDelimiterStyle(*Delimiter);
if (!RawStringStyle)
RawStringStyle = RawStringFormats.getEnclosingFunctionStyle(
getEnclosingFunctionName(Current));
if (!RawStringStyle)
return None;
RawStringStyle->ColumnLimit = getColumnLimit(State);

View File

@ -38,10 +38,14 @@ class WhitespaceManager;
struct RawStringFormatStyleManager {
llvm::StringMap<FormatStyle> DelimiterStyle;
llvm::StringMap<FormatStyle> EnclosingFunctionStyle;
RawStringFormatStyleManager(const FormatStyle &CodeStyle);
llvm::Optional<FormatStyle> get(StringRef Delimiter) const;
llvm::Optional<FormatStyle> getDelimiterStyle(StringRef Delimiter) const;
llvm::Optional<FormatStyle>
getEnclosingFunctionStyle(StringRef EnclosingFunction) const;
};
class ContinuationIndenter {

View File

@ -457,6 +457,7 @@ template <> struct MappingTraits<FormatStyle::RawStringFormat> {
static void mapping(IO &IO, FormatStyle::RawStringFormat &Format) {
IO.mapOptional("Language", Format.Language);
IO.mapOptional("Delimiters", Format.Delimiters);
IO.mapOptional("EnclosingFunctions", Format.EnclosingFunctions);
IO.mapOptional("BasedOnStyle", Format.BasedOnStyle);
}
};
@ -705,6 +706,12 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) {
"textproto",
"TEXTPROTO",
},
/*EnclosingFunctionNames=*/
{
"EqualsProto",
"PARSE_TEXT_PROTO",
"ParseTextProto",
},
/*BasedOnStyle=*/"google",
}};
GoogleStyle.SpacesBeforeTrailingComments = 2;

View File

@ -10408,8 +10408,18 @@ TEST_F(FormatTest, ParsesConfiguration) {
Style.RawStringFormats.clear();
std::vector<FormatStyle::RawStringFormat> ExpectedRawStringFormats = {
{FormatStyle::LK_TextProto, {"pb", "proto"}, "llvm"},
{FormatStyle::LK_Cpp, {"cc", "cpp"}, "google"},
{
FormatStyle::LK_TextProto,
{"pb", "proto"},
{"PARSE_TEXT_PROTO"},
"llvm",
},
{
FormatStyle::LK_Cpp,
{"cc", "cpp"},
{"C_CODEBLOCK", "CPPEVAL"},
"",
},
};
CHECK_PARSE("RawStringFormats:\n"
@ -10417,12 +10427,16 @@ TEST_F(FormatTest, ParsesConfiguration) {
" Delimiters:\n"
" - 'pb'\n"
" - 'proto'\n"
" EnclosingFunctions:\n"
" - 'PARSE_TEXT_PROTO'\n"
" BasedOnStyle: llvm\n"
" - Language: Cpp\n"
" Delimiters:\n"
" - 'cc'\n"
" - 'cpp'\n"
" BasedOnStyle: google\n",
" EnclosingFunctions:\n"
" - 'C_CODEBLOCK'\n"
" - 'CPPEVAL'\n",
RawStringFormats, ExpectedRawStringFormats);
}

View File

@ -65,23 +65,32 @@ protected:
FormatStyle getRawStringPbStyleWithColumns(unsigned ColumnLimit) {
FormatStyle Style = getLLVMStyle();
Style.ColumnLimit = ColumnLimit;
Style.RawStringFormats = {{/*Language=*/FormatStyle::LK_TextProto,
/*Delimiters=*/{"pb"},
/*BasedOnStyle=*/"google"}};
Style.RawStringFormats = {
{/*Language=*/FormatStyle::LK_TextProto,
/*Delimiters=*/{"pb"},
/*EnclosingFunctions=*/{},
/*BasedOnStyle=*/"google"},
};
return Style;
}
FormatStyle getRawStringLLVMCppStyleBasedOn(std::string BasedOnStyle) {
FormatStyle Style = getLLVMStyle();
Style.RawStringFormats = {{/*Language=*/FormatStyle::LK_Cpp,
/*Delimiters=*/{"cpp"}, BasedOnStyle}};
Style.RawStringFormats = {
{/*Language=*/FormatStyle::LK_Cpp,
/*Delimiters=*/{"cpp"},
/*EnclosingFunctions=*/{}, BasedOnStyle},
};
return Style;
}
FormatStyle getRawStringGoogleCppStyleBasedOn(std::string BasedOnStyle) {
FormatStyle Style = getGoogleStyle(FormatStyle::LK_Cpp);
Style.RawStringFormats = {{/*Language=*/FormatStyle::LK_Cpp,
/*Delimiters=*/{"cpp"}, BasedOnStyle}};
Style.RawStringFormats = {
{/*Language=*/FormatStyle::LK_Cpp,
/*Delimiters=*/{"cpp"},
/*EnclosingFunctions=*/{}, BasedOnStyle},
};
return Style;
}
@ -122,7 +131,7 @@ TEST_F(FormatTestRawStrings, UsesConfigurationOverBaseStyle) {
EXPECT_EQ(0, parseConfiguration("---\n"
"Language: Cpp\n"
"BasedOnStyle: Google", &Style).value());
Style.RawStringFormats = {{FormatStyle::LK_Cpp, {"cpp"}, "llvm"}};
Style.RawStringFormats = {{FormatStyle::LK_Cpp, {"cpp"}, {}, "llvm"}};
expect_eq(R"test(int* i = R"cpp(int* j = 0;)cpp";)test",
format(R"test(int * i = R"cpp(int * j = 0;)cpp";)test", Style));
}
@ -720,6 +729,29 @@ TEST_F(FormatTestRawStrings, DontFormatNonRawStrings) {
getRawStringPbStyleWithColumns(20)));
}
TEST_F(FormatTestRawStrings, FormatsRawStringsWithEnclosingFunctionName) {
FormatStyle Style = getRawStringPbStyleWithColumns(40);
Style.RawStringFormats[0].EnclosingFunctions.push_back(
"PARSE_TEXT_PROTO");
Style.RawStringFormats[0].EnclosingFunctions.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<ProtoType>(
R"(key: value)");)test",
format(R"test(
a = ParseTextProto<ProtoType>(R"(key:value)");)test",
Style));
}
} // end namespace
} // end namespace format
} // end namespace clang