Author: Alex Richardson Date: 2020-09-07T10:09:17+01:00 New Revision: e7bd058c7e2cb2c675a4b78ec770ea725bff8c64
URL: https://github.com/llvm/llvm-project/commit/e7bd058c7e2cb2c675a4b78ec770ea725bff8c64 DIFF: https://github.com/llvm/llvm-project/commit/e7bd058c7e2cb2c675a4b78ec770ea725bff8c64.diff LOG: [clang-format] Allow configuring list of macros that map to attributes This adds a `AttributeMacros` configuration option that causes certain identifiers to be parsed like a __attribute__((foo)) annotation. This is motivated by our CHERI C/C++ fork which adds a __capability qualifier for pointer/reference. Without this change clang-format parses many type declarations as multiplications/bitwise-and instead. I initially considered adding "__capability" as a new clang-format keyword, but having a list of macros that should be treated as attributes is more flexible since it can be used e.g. for static analyzer annotations or other language extensions. Example: std::vector<foo * __capability> -> std::vector<foo *__capability> Depends on D86775 (to apply cleanly) Reviewed By: MyDeveloperDay, jrtc27 Differential Revision: https://reviews.llvm.org/D86782 Added: Modified: clang/docs/ClangFormatStyleOptions.rst clang/include/clang/Format/Format.h clang/lib/Format/Format.cpp clang/lib/Format/FormatToken.h clang/lib/Format/FormatTokenLexer.cpp clang/lib/Format/TokenAnnotator.cpp clang/unittests/Format/FormatTest.cpp Removed: ################################################################################ diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index c35718b51248..72a25032151f 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -758,7 +758,24 @@ the configuration (without a prefix: ``Auto``). int bbbbbbbbbbbbbbbbbbbbb) { } +**AttributeMacros** (``std::vector<std::string>``) + A vector of strings that should be interpreted as attributes/qualifiers + instead of identifiers. This can be useful for language extensions or + static analyzer annotations: + .. code-block:: c++ + + x = (char *__capability)&y; + int function(void) __ununsed; + void only_writes_to_buffer(char *__output buffer); + + In the .clang-format configuration file, this can be configured like: + + .. code-block:: yaml + + AttributeMacros: ['__capability', '__output', '__ununsed'] + + For example: __capability. **BinPackArguments** (``bool``) If ``false``, a function call's arguments will either be all on the diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 269eab971a2c..6bb828d60071 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -583,6 +583,24 @@ struct FormatStyle { /// The template declaration breaking style to use. BreakTemplateDeclarationsStyle AlwaysBreakTemplateDeclarations; + /// A vector of strings that should be interpreted as attributes/qualifiers + /// instead of identifiers. This can be useful for language extensions or + /// static analyzer annotations. + /// + /// For example: + /// \code + /// x = (char *__capability)&y; + /// int function(void) __ununsed; + /// void only_writes_to_buffer(char *__output buffer); + /// \endcode + /// + /// In the .clang-format configuration file, this can be configured like: + /// \code{.yaml} + /// AttributeMacros: ['__capability', '__output', '__ununsed'] + /// \endcode + /// + std::vector<std::string> AttributeMacros; + /// If ``false``, a function call's arguments will either be all on the /// same line or will have one line each. /// \code @@ -2351,6 +2369,7 @@ struct FormatStyle { R.AlwaysBreakBeforeMultilineStrings && AlwaysBreakTemplateDeclarations == R.AlwaysBreakTemplateDeclarations && + AttributeMacros == R.AttributeMacros && BinPackArguments == R.BinPackArguments && BinPackParameters == R.BinPackParameters && BreakBeforeBinaryOperators == R.BreakBeforeBinaryOperators && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index fe11cba9bfdf..5dda2bda06b5 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -475,6 +475,7 @@ template <> struct MappingTraits<FormatStyle> { Style.AlwaysBreakBeforeMultilineStrings); IO.mapOptional("AlwaysBreakTemplateDeclarations", Style.AlwaysBreakTemplateDeclarations); + IO.mapOptional("AttributeMacros", Style.AttributeMacros); IO.mapOptional("BinPackArguments", Style.BinPackArguments); IO.mapOptional("BinPackParameters", Style.BinPackParameters); IO.mapOptional("BraceWrapping", Style.BraceWrapping); @@ -842,6 +843,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None; LLVMStyle.AlwaysBreakBeforeMultilineStrings = false; LLVMStyle.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_MultiLine; + LLVMStyle.AttributeMacros.push_back("__capability"); LLVMStyle.BinPackArguments = true; LLVMStyle.BinPackParameters = true; LLVMStyle.BreakBeforeBinaryOperators = FormatStyle::BOS_None; diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index ad72a95062ab..795c26889629 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -29,6 +29,7 @@ namespace format { TYPE(ArrayInitializerLSquare) \ TYPE(ArraySubscriptLSquare) \ TYPE(AttributeColon) \ + TYPE(AttributeMacro) \ TYPE(AttributeParen) \ TYPE(AttributeSquare) \ TYPE(BinaryOperator) \ @@ -442,7 +443,8 @@ struct FormatToken { bool canBePointerOrReferenceQualifier() const { return isOneOf(tok::kw_const, tok::kw_restrict, tok::kw_volatile, tok::kw___attribute, tok::kw__Nonnull, tok::kw__Nullable, - tok::kw__Null_unspecified, tok::kw___ptr32, tok::kw___ptr64); + tok::kw__Null_unspecified, tok::kw___ptr32, tok::kw___ptr64, + TT_AttributeMacro); } /// Determine whether the token is a simple-type-specifier. diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index 1fd153d1112e..f6db58acd8db 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -39,6 +39,8 @@ FormatTokenLexer::FormatTokenLexer( for (const std::string &ForEachMacro : Style.ForEachMacros) Macros.insert({&IdentTable.get(ForEachMacro), TT_ForEachMacro}); + for (const std::string &AttributeMacro : Style.AttributeMacros) + Macros.insert({&IdentTable.get(AttributeMacro), TT_AttributeMacro}); for (const std::string &StatementMacro : Style.StatementMacros) Macros.insert({&IdentTable.get(StatementMacro), TT_StatementMacro}); for (const std::string &TypenameMacro : Style.TypenameMacros) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index f04f101f0459..fc6a226dc4a1 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1333,11 +1333,12 @@ class AnnotatingParser { // Reset token type in case we have already looked at it and then // recovered from an error (e.g. failure to find the matching >). if (!CurrentToken->isOneOf( - TT_LambdaLSquare, TT_LambdaLBrace, TT_ForEachMacro, - TT_TypenameMacro, TT_FunctionLBrace, TT_ImplicitStringLiteral, - TT_InlineASMBrace, TT_JsFatArrow, TT_LambdaArrow, TT_NamespaceMacro, - TT_OverloadedOperator, TT_RegexLiteral, TT_TemplateString, - TT_ObjCStringLiteral, TT_UntouchableMacroFunc)) + TT_LambdaLSquare, TT_LambdaLBrace, TT_AttributeMacro, + TT_ForEachMacro, TT_TypenameMacro, TT_FunctionLBrace, + TT_ImplicitStringLiteral, TT_InlineASMBrace, TT_JsFatArrow, + TT_LambdaArrow, TT_NamespaceMacro, TT_OverloadedOperator, + TT_RegexLiteral, TT_TemplateString, TT_ObjCStringLiteral, + TT_UntouchableMacroFunc)) CurrentToken->setType(TT_Unknown); CurrentToken->Role.reset(); CurrentToken->MatchingParen = nullptr; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index a2d694947990..f224ab03271d 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -8040,7 +8040,20 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyFormat("vector<a *_Null_unspecified> v;"); verifyFormat("vector<a *__ptr32> v;"); verifyFormat("vector<a *__ptr64> v;"); + verifyFormat("vector<a *__capability> v;"); + FormatStyle CustomQualifier = getLLVMStyle(); + // Add indentifers that should not be parsed as a qualifier by default. + CustomQualifier.AttributeMacros.push_back("__my_qualifier"); + CustomQualifier.AttributeMacros.push_back("_My_qualifier"); + CustomQualifier.AttributeMacros.push_back("my_other_qualifier"); + verifyFormat("vector<a * __my_qualifier> parse_as_multiply;"); + verifyFormat("vector<a *__my_qualifier> v;", CustomQualifier); + verifyFormat("vector<a * _My_qualifier> parse_as_multiply;"); + verifyFormat("vector<a *_My_qualifier> v;", CustomQualifier); + verifyFormat("vector<a * my_other_qualifier> parse_as_multiply;"); + verifyFormat("vector<a *my_other_qualifier> v;", CustomQualifier); verifyFormat("vector<a * _NotAQualifier> v;"); + verifyFormat("vector<a * __not_a_qualifier> v;"); verifyFormat("vector<a * b> v;"); verifyFormat("foo<b && false>();"); verifyFormat("foo<b & 1>();"); @@ -8084,10 +8097,23 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyIndependentOfContext("MACRO(A *[[clang::attr(\"foo\")]] a);"); verifyIndependentOfContext("MACRO(A *__ptr32 a);"); verifyIndependentOfContext("MACRO(A *__ptr64 a);"); + verifyIndependentOfContext("MACRO(A *__capability);"); + verifyIndependentOfContext("MACRO(A &__capability);"); + verifyFormat("MACRO(A *__my_qualifier);"); // type declaration + verifyFormat("void f() { MACRO(A * __my_qualifier); }"); // multiplication + // If we add __my_qualifier to AttributeMacros it should always be parsed as + // a type declaration: + verifyFormat("MACRO(A *__my_qualifier);", CustomQualifier); + verifyFormat("void f() { MACRO(A *__my_qualifier); }", CustomQualifier); + verifyIndependentOfContext("MACRO('0' <= c && c <= '9');"); verifyFormat("void f() { f(float{1}, a * a); }"); // FIXME: Is there a way to make this work? // verifyIndependentOfContext("MACRO(A *a);"); + verifyFormat("MACRO(A &B);"); + verifyFormat("MACRO(A *B);"); + verifyFormat("void f() { MACRO(A * B); }"); + verifyFormat("void f() { MACRO(A & B); }"); verifyFormat("DatumHandle const *operator->() const { return input_; }"); verifyFormat("return options != nullptr && operator==(*options);"); @@ -8137,10 +8163,47 @@ TEST_F(FormatTest, UnderstandsAttributes) { verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa __attribute__((unused))\n" "aaaaaaaaaaaaaaaaaaaaaaa(int i);"); FormatStyle AfterType = getLLVMStyle(); - AfterType.AlwaysBreakAfterReturnType = FormatStyle::RTBS_AllDefinitions; + AfterType.AlwaysBreakAfterReturnType = FormatStyle::RTBS_All; verifyFormat("__attribute__((nodebug)) void\n" "foo() {}\n", AfterType); + verifyFormat("__unused void\n" + "foo() {}", + AfterType); + + FormatStyle CustomAttrs = getLLVMStyle(); + CustomAttrs.AttributeMacros.push_back("__unused"); + CustomAttrs.AttributeMacros.push_back("__attr1"); + CustomAttrs.AttributeMacros.push_back("__attr2"); + CustomAttrs.AttributeMacros.push_back("no_underscore_attr"); + verifyFormat("vector<SomeType *__attribute((foo))> v;"); + verifyFormat("vector<SomeType *__attribute__((foo))> v;"); + verifyFormat("vector<SomeType * __not_attribute__((foo))> v;"); + // Check that it is parsed as a multiplication without AttributeMacros and + // as a pointer qualifier when we add __attr1/__attr2 to AttributeMacros. + verifyFormat("vector<SomeType * __attr1> v;"); + verifyFormat("vector<SomeType __attr1 *> v;"); + verifyFormat("vector<SomeType __attr1 *const> v;"); + verifyFormat("vector<SomeType __attr1 * __attr2> v;"); + verifyFormat("vector<SomeType *__attr1> v;", CustomAttrs); + verifyFormat("vector<SomeType *__attr2> v;", CustomAttrs); + verifyFormat("vector<SomeType *no_underscore_attr> v;", CustomAttrs); + verifyFormat("vector<SomeType __attr1 *> v;", CustomAttrs); + verifyFormat("vector<SomeType __attr1 *const> v;", CustomAttrs); + verifyFormat("vector<SomeType __attr1 *__attr2> v;", CustomAttrs); + verifyFormat("vector<SomeType __attr1 *no_underscore_attr> v;", CustomAttrs); + + // Check that these are not parsed as function declarations: + CustomAttrs.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; + CustomAttrs.BreakBeforeBraces = FormatStyle::BS_Allman; + verifyFormat("SomeType s(InitValue);", CustomAttrs); + verifyFormat("SomeType s{InitValue};", CustomAttrs); + verifyFormat("SomeType *__unused s(InitValue);", CustomAttrs); + verifyFormat("SomeType *__unused s{InitValue};", CustomAttrs); + verifyFormat("SomeType s __unused(InitValue);", CustomAttrs); + verifyFormat("SomeType s __unused{InitValue};", CustomAttrs); + verifyFormat("SomeType *__capability s(InitValue);", CustomAttrs); + verifyFormat("SomeType *__capability s{InitValue};", CustomAttrs); } TEST_F(FormatTest, UnderstandsPointerQualifiersInCast) { @@ -8157,6 +8220,7 @@ TEST_F(FormatTest, UnderstandsPointerQualifiersInCast) { verifyFormat("x = (foo *[[clang::attr(\"foo\")]])*v;"); verifyFormat("x = (foo *__ptr32)*v;"); verifyFormat("x = (foo *__ptr64)*v;"); + verifyFormat("x = (foo *__capability)*v;"); // Check that we handle multiple trailing qualifiers and skip them all to // determine that the expression is a cast to a pointer type. @@ -8165,7 +8229,7 @@ TEST_F(FormatTest, UnderstandsPointerQualifiersInCast) { LongPointerLeft.PointerAlignment = FormatStyle::PAS_Left; StringRef AllQualifiers = "const volatile restrict __attribute__((foo)) _Nonnull _Null_unspecified " - "_Nonnull [[clang::attr]] __ptr32 __ptr64"; + "_Nonnull [[clang::attr]] __ptr32 __ptr64 __capability"; verifyFormat(("x = (foo *" + AllQualifiers + ")*v;").str(), LongPointerRight); verifyFormat(("x = (foo* " + AllQualifiers + ")*v;").str(), LongPointerLeft); @@ -8173,6 +8237,20 @@ TEST_F(FormatTest, UnderstandsPointerQualifiersInCast) { verifyFormat("x = (foo *const)&v;"); verifyFormat(("x = (foo *" + AllQualifiers + ")&v;").str(), LongPointerRight); verifyFormat(("x = (foo* " + AllQualifiers + ")&v;").str(), LongPointerLeft); + + // Check custom qualifiers: + FormatStyle CustomQualifier = getLLVMStyleWithColumns(999); + CustomQualifier.AttributeMacros.push_back("__my_qualifier"); + verifyFormat("x = (foo * __my_qualifier) * v;"); // not parsed as qualifier. + verifyFormat("x = (foo *__my_qualifier)*v;", CustomQualifier); + verifyFormat(("x = (foo *" + AllQualifiers + " __my_qualifier)*v;").str(), + CustomQualifier); + verifyFormat(("x = (foo *" + AllQualifiers + " __my_qualifier)&v;").str(), + CustomQualifier); + + // Check that unknown identifiers result in binary operator parsing: + verifyFormat("x = (foo * __unknown_qualifier) * v;"); + verifyFormat("x = (foo * __unknown_qualifier) & v;"); } TEST_F(FormatTest, UnderstandsSquareAttributes) { @@ -13770,9 +13848,9 @@ TEST_F(FormatTest, GetsCorrectBasedOnStyle) { CHECK_PARSE_NESTED_BOOL_FIELD(STRUCT, FIELD, #FIELD) #define CHECK_PARSE(TEXT, FIELD, VALUE) \ - EXPECT_NE(VALUE, Style.FIELD); \ + EXPECT_NE(VALUE, Style.FIELD) << "Initial value already the same!"; \ EXPECT_EQ(0, parseConfiguration(TEXT, &Style).value()); \ - EXPECT_EQ(VALUE, Style.FIELD) + EXPECT_EQ(VALUE, Style.FIELD) << "Unexpected value after parsing!" TEST_F(FormatTest, ParsesConfigurationBools) { FormatStyle Style = {}; @@ -14162,6 +14240,12 @@ TEST_F(FormatTest, ParsesConfiguration) { CHECK_PARSE("ForEachMacros: [BOOST_FOREACH, Q_FOREACH]", ForEachMacros, BoostAndQForeach); + Style.AttributeMacros.clear(); + CHECK_PARSE("BasedOnStyle: LLVM", AttributeMacros, + std::vector<std::string>{"__capability"}); + CHECK_PARSE("AttributeMacros: [attr1, attr2]", AttributeMacros, + std::vector<std::string>({"attr1", "attr2"})); + Style.StatementMacros.clear(); CHECK_PARSE("StatementMacros: [QUNUSED]", StatementMacros, std::vector<std::string>{"QUNUSED"}); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits