Index: clang/lib/Format/ContinuationIndenter.cpp =================================================================== --- clang/lib/Format/ContinuationIndenter.cpp +++ clang/lib/Format/ContinuationIndenter.cpp @@ -1228,8 +1228,9 @@ (PreviousNonComment->ClosesTemplateDeclaration || PreviousNonComment->ClosesRequiresClause || PreviousNonComment->isOneOf( - TT_AttributeParen, TT_AttributeSquare, TT_FunctionAnnotationRParen, - TT_JavaAnnotation, TT_LeadingJavaAnnotation))) || + TT_AttributeParen, TT_AttributeMacro, TT_AttributeSquare, + TT_FunctionAnnotationRParen, TT_JavaAnnotation, + TT_LeadingJavaAnnotation))) || (!Style.IndentWrappedFunctionNames && NextNonComment->isOneOf(tok::kw_operator, TT_FunctionDeclarationName))) { return std::max(CurrentState.LastSpace, CurrentState.Indent); Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -369,7 +369,7 @@ // Infer the role of the l_paren based on the previous token if we haven't // detected one yet. if (PrevNonComment && OpeningParen.is(TT_Unknown)) { - if (PrevNonComment->is(tok::kw___attribute)) { + if (PrevNonComment->isOneOf(tok::kw___attribute, TT_AttributeMacro)) { OpeningParen.setType(TT_AttributeParen); } else if (PrevNonComment->isOneOf(TT_TypenameMacro, tok::kw_decltype, tok::kw_typeof, @@ -4889,8 +4889,10 @@ } // Ensure wrapping after __attribute__((XX)) and @interface etc. - if (Left.is(TT_AttributeParen) && Right.is(TT_ObjCDecl)) + if (Left.isOneOf(TT_AttributeMacro, TT_AttributeParen) && + Right.is(TT_ObjCDecl)) { return true; + } if (Left.is(TT_LambdaLBrace)) { if (IsFunctionArgument(Left) && Index: clang/unittests/Format/FormatTestObjC.cpp =================================================================== --- clang/unittests/Format/FormatTestObjC.cpp +++ clang/unittests/Format/FormatTestObjC.cpp @@ -1489,6 +1489,9 @@ } TEST_F(FormatTestObjC, Attributes) { + Style.AttributeMacros.push_back("ATTRIBUTE_MACRO"); + + // Check '__attribute__' macro directly. verifyFormat("__attribute__((objc_subclassing_restricted))\n" "@interface Foo\n" "@end"); @@ -1498,6 +1501,41 @@ verifyFormat("__attribute__((objc_subclassing_restricted))\n" "@implementation Foo\n" "@end"); + + // Check AttributeMacro gets treated the same, with or without parentheses. + verifyFormat("ATTRIBUTE_MACRO\n" + "@interface Foo\n" + "@end"); + verifyFormat("ATTRIBUTE_MACRO(X)\n" + "@interface Foo\n" + "@end"); + + // Indenter also needs to understand multiple attribute macros. + verifyFormat("ATTRIBUTE_MACRO(X) ATTRIBUTE_MACRO\n" + "@interface Foo\n" + "@end"); + verifyFormat("ATTRIBUTE_MACRO ATTRIBUTE_MACRO(X)\n" + "@interface Foo\n" + "@end"); + + Style.ColumnLimit = 30; + verifyFormat("ATTRIBUTE_MACRO(X)\n" + "ATTRIBUTE_MACRO\n" + "@interface Foo\n" + "@end"); + // Note: the following -should- break across multiple lines, but doesn't. + // This is added to acknowledge the behavior, but it should be improved. + verifyFormat("ATTRIBUTE_MACRO ATTRIBUTE_MACRO(X)\n" + "@interface Foo\n" + "@end"); + + Style.ColumnLimit = 0; + verifyFormat("ATTRIBUTE_MACRO(X) ATTRIBUTE_MACRO\n" + "@interface Foo\n" + "@end"); + verifyFormat("ATTRIBUTE_MACRO ATTRIBUTE_MACRO(X)\n" + "@interface Foo\n" + "@end"); } } // end namespace Index: clang/unittests/Format/TokenAnnotatorTest.cpp =================================================================== --- clang/unittests/Format/TokenAnnotatorTest.cpp +++ clang/unittests/Format/TokenAnnotatorTest.cpp @@ -1292,6 +1292,34 @@ EXPECT_TOKEN(Tokens[9], tok::colon, TT_GenericSelectionColon); } +TEST_F(TokenAnnotatorTest, UnderstandsAttributeMacros) { + // '__attribute__' has special handling. + auto Tokens = annotate("__attribute__(X) void Foo(void);"); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::kw___attribute, TT_Unknown); + EXPECT_TOKEN(Tokens[1], tok::l_paren, TT_AttributeParen); + EXPECT_TOKEN(Tokens[3], tok::r_paren, TT_AttributeParen); + + // Generic macro has no special handling in this location. + Tokens = annotate("A(X) void Foo(void);"); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::identifier, TT_Unknown); + EXPECT_TOKEN(Tokens[1], tok::l_paren, TT_Unknown); + // 'TT_FunctionAnnotationRParen' doesn't seem right; fix? + EXPECT_TOKEN(Tokens[3], tok::r_paren, TT_FunctionAnnotationRParen); + + // Add a custom AttributeMacro. Test that it has the same behavior. + FormatStyle Style = getLLVMStyle(); + Style.AttributeMacros.push_back("A"); + + // An "AttributeMacro" gets annotated like '__attribute__'. + Tokens = annotate("A(X) void Foo(void);", Style); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::identifier, TT_AttributeMacro); + EXPECT_TOKEN(Tokens[1], tok::l_paren, TT_AttributeParen); + EXPECT_TOKEN(Tokens[3], tok::r_paren, TT_AttributeParen); +} + TEST_F(TokenAnnotatorTest, UnderstandsVerilogOperators) { auto Annotate = [this](llvm::StringRef Code) { return annotate(Code, getLLVMStyle(FormatStyle::LK_Verilog));