[clang-format] Add StatementAttributeLikeMacros option

This allows to ignore for example Qts emit when
AlignConsecutiveDeclarations is set, otherwise it is parsed as a type
and it results in some misformating:

unsigned char MyChar = 'x';
emit          signal(MyChar);

Differential Revision: https://reviews.llvm.org/D93776
This commit is contained in:
Björn Schäpers 2020-12-23 22:03:39 +01:00
parent 26a396c4ef
commit bcc1dee600
9 changed files with 86 additions and 4 deletions

View File

@ -3074,6 +3074,20 @@ the configuration (without a prefix: ``Auto``).
**StatementAttributeLikeMacros** (``std::vector<std::string>``)
Macros which are ignored in front of a statement, as if they were an
attribute. So that they are not parsed as identifier, for example for Qts
emit. \code
AlignConsecutiveDeclarations: true
StatementAttributeLikeMacros: []
unsigned char data = 'x';
emit signal(data); // This is parsed as variable declaration.
AlignConsecutiveDeclarations: true
StatementAttributeLikeMacros: [emit]
unsigned char data = 'x';
emit signal(data); // Now it's fine again.
**StatementMacros** (``std::vector<std::string>``)
A vector of macros that should be interpreted as complete
statements.

View File

@ -296,6 +296,10 @@ clang-format
- Option ``SpaceBeforeCaseColon`` has been added to add a space before the
colon in a case or default statement.
- Option ``StatementAttributeLikeMacros`` has been added to declare
macros which are not parsed as a type in front of a statement. See
the documentation for an example.
libclang
--------

View File

@ -2676,6 +2676,21 @@ struct FormatStyle {
/// \endcode
LanguageStandard Standard;
/// Macros which are ignored in front of a statement, as if they were an
/// attribute. So that they are not parsed as identifier, for example for Qts
/// emit. \code
/// AlignConsecutiveDeclarations: true
/// StatementAttributeLikeMacros: []
/// unsigned char data = 'x';
/// emit signal(data); // This is parsed as variable declaration.
///
/// AlignConsecutiveDeclarations: true
/// StatementAttributeLikeMacros: [emit]
/// unsigned char data = 'x';
/// emit signal(data); // Now it's fine again.
/// \endcode
std::vector<std::string> StatementAttributeLikeMacros;
/// The number of columns used for tab stops.
unsigned TabWidth;
@ -2825,9 +2840,11 @@ struct FormatStyle {
SpacesInSquareBrackets == R.SpacesInSquareBrackets &&
SpaceBeforeSquareBrackets == R.SpaceBeforeSquareBrackets &&
BitFieldColonSpacing == R.BitFieldColonSpacing &&
Standard == R.Standard && TabWidth == R.TabWidth &&
StatementMacros == R.StatementMacros && UseTab == R.UseTab &&
UseCRLF == R.UseCRLF && TypenameMacros == R.TypenameMacros;
Standard == R.Standard &&
StatementAttributeLikeMacros == R.StatementAttributeLikeMacros &&
StatementMacros == R.StatementMacros && TabWidth == R.TabWidth &&
UseTab == R.UseTab && UseCRLF == R.UseCRLF &&
TypenameMacros == R.TypenameMacros;
}
llvm::Optional<FormatStyle> GetLanguageStyle(LanguageKind Language) const;

View File

@ -549,6 +549,8 @@ template <> struct MappingTraits<FormatStyle> {
Style.ExperimentalAutoDetectBinPacking);
IO.mapOptional("FixNamespaceComments", Style.FixNamespaceComments);
IO.mapOptional("ForEachMacros", Style.ForEachMacros);
IO.mapOptional("StatementAttributeLikeMacros",
Style.StatementAttributeLikeMacros);
IO.mapOptional("IncludeBlocks", Style.IncludeStyle.IncludeBlocks);
IO.mapOptional("IncludeCategories", Style.IncludeStyle.IncludeCategories);
IO.mapOptional("IncludeIsMainRegex", Style.IncludeStyle.IncludeIsMainRegex);
@ -983,6 +985,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.SortIncludes = true;
LLVMStyle.SortJavaStaticImport = FormatStyle::SJSIO_Before;
LLVMStyle.SortUsingDeclarations = true;
LLVMStyle.StatementAttributeLikeMacros.push_back("Q_EMIT");
LLVMStyle.StatementMacros.push_back("Q_UNUSED");
LLVMStyle.StatementMacros.push_back("QT_REQUIRE_VERSION");
LLVMStyle.WhitespaceSensitiveMacros.push_back("STRINGIZE");

View File

@ -96,6 +96,7 @@ namespace format {
TYPE(RegexLiteral) \
TYPE(SelectorName) \
TYPE(StartOfName) \
TYPE(StatementAttributeLikeMacro) \
TYPE(StatementMacro) \
TYPE(StructuredBindingLSquare) \
TYPE(TemplateCloser) \

View File

@ -52,6 +52,10 @@ FormatTokenLexer::FormatTokenLexer(
Macros.insert(
{&IdentTable.get(WhitespaceSensitiveMacro), TT_UntouchableMacroFunc});
}
for (const std::string &StatementAttributeLikeMacro :
Style.StatementAttributeLikeMacros)
Macros.insert({&IdentTable.get(StatementAttributeLikeMacro),
TT_StatementAttributeLikeMacro});
}
ArrayRef<FormatToken *> FormatTokenLexer::lex() {

View File

@ -1368,7 +1368,8 @@ private:
TT_ImplicitStringLiteral, TT_InlineASMBrace, TT_JsFatArrow,
TT_LambdaArrow, TT_NamespaceMacro, TT_OverloadedOperator,
TT_RegexLiteral, TT_TemplateString, TT_ObjCStringLiteral,
TT_UntouchableMacroFunc, TT_ConstraintJunctions))
TT_UntouchableMacroFunc, TT_ConstraintJunctions,
TT_StatementAttributeLikeMacro))
CurrentToken->setType(TT_Unknown);
CurrentToken->Role.reset();
CurrentToken->MatchingParen = nullptr;

View File

@ -655,6 +655,9 @@ void WhitespaceManager::alignConsecutiveDeclarations() {
return true;
if (C.Tok->isNot(TT_StartOfName))
return false;
if (C.Tok->Previous &&
C.Tok->Previous->is(TT_StatementAttributeLikeMacro))
return false;
// Check if there is a subsequent name that starts the same declaration.
for (FormatToken *Next = C.Tok->Next; Next; Next = Next->Next) {
if (Next->is(tok::comment))

View File

@ -14731,6 +14731,11 @@ TEST_F(FormatTest, ParsesConfiguration) {
CHECK_PARSE("AttributeMacros: [attr1, attr2]", AttributeMacros,
std::vector<std::string>({"attr1", "attr2"}));
Style.StatementAttributeLikeMacros.clear();
CHECK_PARSE("StatementAttributeLikeMacros: [emit,Q_EMIT]",
StatementAttributeLikeMacros,
std::vector<std::string>({"emit", "Q_EMIT"}));
Style.StatementMacros.clear();
CHECK_PARSE("StatementMacros: [QUNUSED]", StatementMacros,
std::vector<std::string>{"QUNUSED"});
@ -17803,6 +17808,36 @@ TEST_F(FormatTest, ConceptsAndRequires) {
"struct constant;",
Style);
}
TEST_F(FormatTest, StatementAttributeLikeMacros) {
FormatStyle Style = getLLVMStyle();
StringRef Source = "void Foo::slot() {\n"
" unsigned char MyChar = 'x';\n"
" emit signal(MyChar);\n"
" Q_EMIT signal(MyChar);\n"
"}";
EXPECT_EQ(Source, format(Source, Style));
Style.AlignConsecutiveDeclarations = true;
EXPECT_EQ("void Foo::slot() {\n"
" unsigned char MyChar = 'x';\n"
" emit signal(MyChar);\n"
" Q_EMIT signal(MyChar);\n"
"}",
format(Source, Style));
Style.StatementAttributeLikeMacros.push_back("emit");
EXPECT_EQ(Source, format(Source, Style));
Style.StatementAttributeLikeMacros = {};
EXPECT_EQ("void Foo::slot() {\n"
" unsigned char MyChar = 'x';\n"
" emit signal(MyChar);\n"
" Q_EMIT signal(MyChar);\n"
"}",
format(Source, Style));
}
} // namespace
} // namespace format
} // namespace clang