diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -3074,6 +3074,20 @@ +**StatementAttributeLikeMacros** (``std::vector``) + 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``) A vector of macros that should be interpreted as complete statements. diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -296,6 +296,10 @@ - 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 -------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2676,6 +2676,21 @@ /// \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 StatementAttributeLikeMacros; + /// The number of columns used for tab stops. unsigned TabWidth; @@ -2825,9 +2840,11 @@ 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 GetLanguageStyle(LanguageKind Language) const; diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -549,6 +549,8 @@ 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 @@ 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"); diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -96,6 +96,7 @@ TYPE(RegexLiteral) \ TYPE(SelectorName) \ TYPE(StartOfName) \ + TYPE(StatementAttributeLikeMacro) \ TYPE(StatementMacro) \ TYPE(StructuredBindingLSquare) \ TYPE(TemplateCloser) \ diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -52,6 +52,10 @@ Macros.insert( {&IdentTable.get(WhitespaceSensitiveMacro), TT_UntouchableMacroFunc}); } + for (const std::string &StatementAttributeLikeMacro : + Style.StatementAttributeLikeMacros) + Macros.insert({&IdentTable.get(StatementAttributeLikeMacro), + TT_StatementAttributeLikeMacro}); } ArrayRef FormatTokenLexer::lex() { diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1368,7 +1368,8 @@ 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; diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -655,6 +655,9 @@ 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)) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -14731,6 +14731,11 @@ CHECK_PARSE("AttributeMacros: [attr1, attr2]", AttributeMacros, std::vector({"attr1", "attr2"})); + Style.StatementAttributeLikeMacros.clear(); + CHECK_PARSE("StatementAttributeLikeMacros: [emit,Q_EMIT]", + StatementAttributeLikeMacros, + std::vector({"emit", "Q_EMIT"})); + Style.StatementMacros.clear(); CHECK_PARSE("StatementMacros: [QUNUSED]", StatementMacros, std::vector{"QUNUSED"}); @@ -17803,6 +17808,36 @@ "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