Index: clang/lib/Format/ContinuationIndenter.cpp =================================================================== --- clang/lib/Format/ContinuationIndenter.cpp +++ clang/lib/Format/ContinuationIndenter.cpp @@ -1335,6 +1335,8 @@ if ((PreviousNonComment && (PreviousNonComment->ClosesTemplateDeclaration || PreviousNonComment->ClosesRequiresClause || + (PreviousNonComment->is(TT_AttributeMacro) && + Current.isNot(tok::l_paren)) || PreviousNonComment->isOneOf( TT_AttributeRParen, TT_AttributeSquare, TT_FunctionAnnotationRParen, TT_JavaAnnotation, TT_LeadingJavaAnnotation))) || Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -4393,8 +4393,10 @@ return false; } // Space in __attribute__((attr)) ::type. - if (Left.is(TT_AttributeRParen) && Right.is(tok::coloncolon)) + if (Left.isOneOf(TT_AttributeRParen, TT_AttributeMacro) && + Right.is(tok::coloncolon)) { return true; + } if (Left.is(tok::kw_operator)) return Right.is(tok::coloncolon); @@ -4709,7 +4711,8 @@ if (Line.Type == LT_ObjCMethodDecl) { if (Left.is(TT_ObjCMethodSpecifier)) return true; - if (Left.is(tok::r_paren) && canBeObjCSelectorComponent(Right)) { + if (Left.is(tok::r_paren) && Left.isNot(TT_AttributeRParen) && + canBeObjCSelectorComponent(Right)) { // Don't space between ')' and or ')' and 'new'. 'new' is not a // keyword in Objective-C, and '+ (instancetype)new;' is a standard class // method declaration. @@ -5222,8 +5225,10 @@ } // Ensure wrapping after __attribute__((XX)) and @interface etc. - if (Left.is(TT_AttributeRParen) && Right.is(TT_ObjCDecl)) + if (Left.isOneOf(TT_AttributeRParen, TT_AttributeMacro) && + Right.is(TT_ObjCDecl)) { return true; + } if (Left.is(TT_LambdaLBrace)) { if (IsFunctionArgument(Left) && Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -11674,6 +11674,9 @@ verifyFormat("vector v;", CustomAttrs); verifyFormat("vector v;", CustomAttrs); verifyFormat("vector v;", CustomAttrs); + verifyFormat("__attr1 ::qualified_type f();", CustomAttrs); + verifyFormat("__attr1() ::qualified_type f();", CustomAttrs); + verifyFormat("__attr1(nodebug) ::qualified_type f();", CustomAttrs); // Check that these are not parsed as function declarations: CustomAttrs.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; Index: clang/unittests/Format/FormatTestObjC.cpp =================================================================== --- clang/unittests/Format/FormatTestObjC.cpp +++ clang/unittests/Format/FormatTestObjC.cpp @@ -1527,7 +1527,10 @@ " [obj func:arg2];"); } -TEST_F(FormatTestObjC, Attributes) { +TEST_F(FormatTestObjC, AttributesOnObjCDecl) { + Style.AttributeMacros.push_back("ATTRIBUTE_MACRO"); + + // Check '__attribute__' macro directly. verifyFormat("__attribute__((objc_subclassing_restricted))\n" "@interface Foo\n" "@end"); @@ -1537,6 +1540,215 @@ 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. + // Try each of the three kinds paired with each of the other kind. + + // Column limit, but no reflow. + verifyFormat("ATTRIBUTE_MACRO(X) ATTRIBUTE_MACRO\n" + "@interface Foo\n" + "@end"); + verifyFormat("ATTRIBUTE_MACRO ATTRIBUTE_MACRO(X)\n" + "@interface Foo\n" + "@end"); + verifyFormat("__attribute__((X)) ATTRIBUTE_MACRO\n" + "@interface Foo\n" + "@end"); + verifyFormat("ATTRIBUTE_MACRO __attribute__((X))\n" + "@interface Foo\n" + "@end"); + verifyFormat("__attribute__((X)) ATTRIBUTE_MACRO(X)\n" + "@interface Foo\n" + "@end"); + verifyFormat("ATTRIBUTE_MACRO(X) __attribute__((X))\n" + "@interface Foo\n" + "@end"); + + // Column limit that requires reflow. + Style.ColumnLimit = 30; + verifyFormat("ATTRIBUTE_MACRO(X)\n" + "ATTRIBUTE_MACRO\n" + "@interface Foo\n" + "@end"); + verifyFormat("ATTRIBUTE_MACRO\n" + "ATTRIBUTE_MACRO(X)\n" + "@interface Foo\n" + "@end"); + verifyFormat("__attribute__((X))\n" + "ATTRIBUTE_MACRO\n" + "@interface Foo\n" + "@end"); + verifyFormat("ATTRIBUTE_MACRO\n" + "__attribute__((X))\n" + "@interface Foo\n" + "@end"); + verifyFormat("__attribute__((X))\n" + "ATTRIBUTE_MACRO(X)\n" + "@interface Foo\n" + "@end"); + verifyFormat("ATTRIBUTE_MACRO(X)\n" + "__attribute__((X))\n" + "@interface Foo\n" + "@end"); + + // No column limit + 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"); + verifyFormat("__attribute__((X)) ATTRIBUTE_MACRO\n" + "@interface Foo\n" + "@end"); + verifyFormat("ATTRIBUTE_MACRO __attribute__((X))\n" + "@interface Foo\n" + "@end"); + verifyFormat("__attribute__((X)) ATTRIBUTE_MACRO(X)\n" + "@interface Foo\n" + "@end"); + verifyFormat("ATTRIBUTE_MACRO(X) __attribute__((X))\n" + "@interface Foo\n" + "@end"); +} + +TEST_F(FormatTestObjC, AttributesOnObjCMethodDecl) { + Style.AttributeMacros.push_back("ATTRIBUTE_MACRO"); + + // Check '__attribute__' macro directly. + verifyFormat("- (id)init __attribute__((objc_designated_initializer));"); + + // Check AttributeMacro gets treated the same, with or without parentheses. + verifyFormat("- (id)init ATTRIBUTE_MACRO;"); + verifyFormat("- (id)init ATTRIBUTE_MACRO(X);"); + + // Indenter also needs to understand multiple attribute macros. + + // Column limit (default), but no reflow. + verifyFormat("- (id)init ATTRIBUTE_MACRO(X) ATTRIBUTE_MACRO;"); + verifyFormat("- (id)init ATTRIBUTE_MACRO ATTRIBUTE_MACRO(X);"); + verifyFormat("- (id)init __attribute__((X)) ATTRIBUTE_MACRO;"); + verifyFormat("- (id)init ATTRIBUTE_MACRO __attribute__((X));"); + verifyFormat("- (id)init __attribute__((X)) ATTRIBUTE_MACRO(X);"); + verifyFormat("- (id)init ATTRIBUTE_MACRO(X) __attribute__((X));"); + + // Column limit that requires reflow. + Style.ColumnLimit = 30; + + // Reflow after method name. + verifyFormat("- (id)initWithReallyLongName\n" + " __attribute__((X))\n" + " ATTRIBUTE_MACRO;"); + verifyFormat("- (id)initWithReallyLongName\n" + " ATTRIBUTE_MACRO(X)\n" + " ATTRIBUTE_MACRO;"); + verifyFormat("- (id)initWithReallyLongName\n" + " ATTRIBUTE_MACRO\n" + " ATTRIBUTE_MACRO;"); + // Reflow after first macro. + // FIXME: these should indent but don't. +#if 0 + verifyFormat("- (id)init ATTRIBUTE_MACRO(X)\n" + " ATTRIBUTE_MACRO;"); + verifyFormat("- (id)init ATTRIBUTE_MACRO\n" + " ATTRIBUTE_MACRO(X);"); + verifyFormat("- (id)init __attribute__((X))\n" + " ATTRIBUTE_MACRO;"); + verifyFormat("- (id)init ATTRIBUTE_MACRO\n" + " __attribute__((X));"); + verifyFormat("- (id)init __attribute__((X))\n" + " ATTRIBUTE_MACRO(X);"); + verifyFormat("- (id)init ATTRIBUTE_MACRO(X)\n" + " __attribute__((X));"); +#endif + + // No column limit. + Style.ColumnLimit = 0; + verifyFormat("- (id)init ATTRIBUTE_MACRO(X) ATTRIBUTE_MACRO;"); + verifyFormat("- (id)init ATTRIBUTE_MACRO ATTRIBUTE_MACRO(X);"); + verifyFormat("- (id)init __attribute__((X)) ATTRIBUTE_MACRO;"); + verifyFormat("- (id)init ATTRIBUTE_MACRO __attribute__((X));"); + verifyFormat("- (id)init __attribute__((X)) ATTRIBUTE_MACRO(X);"); + verifyFormat("- (id)init ATTRIBUTE_MACRO(X) __attribute__((X));"); +} + +TEST_F(FormatTestObjC, AttributesOnObjCProperty) { + Style.AttributeMacros.push_back("ATTRIBUTE_MACRO"); + + // Check '__attribute__' macro directly. + verifyFormat("@property(weak) id delegate " + "__attribute__((objc_designated_initializer));"); + + // Check AttributeMacro gets treated the same, with or without parentheses. + verifyFormat("@property(weak) id delegate ATTRIBUTE_MACRO;"); + verifyFormat("@property(weak) id delegate ATTRIBUTE_MACRO(X);"); + + // Indenter also needs to understand multiple attribute macros. + + // Column limit (default), but no reflow. + verifyFormat( + "@property(weak) id delegate ATTRIBUTE_MACRO(X) ATTRIBUTE_MACRO;"); + verifyFormat( + "@property(weak) id delegate ATTRIBUTE_MACRO ATTRIBUTE_MACRO(X);"); + verifyFormat( + "@property(weak) id delegate __attribute__((X)) ATTRIBUTE_MACRO;"); + verifyFormat( + "@property(weak) id delegate ATTRIBUTE_MACRO __attribute__((X));"); + verifyFormat( + "@property(weak) id delegate __attribute__((X)) ATTRIBUTE_MACRO(X);"); + verifyFormat( + "@property(weak) id delegate ATTRIBUTE_MACRO(X) __attribute__((X));"); + + // Column limit that requires reflow. + Style.ColumnLimit = 50; + + // Reflow after method name. + verifyFormat("@property(weak) id delegateWithLongName\n" + " __attribute__((X)) ATTRIBUTE_MACRO;"); + verifyFormat("@property(weak) id delegateWithLongName\n" + " ATTRIBUTE_MACRO(X) ATTRIBUTE_MACRO;"); + verifyFormat("@property(weak) id delegateWithLongName\n" + " ATTRIBUTE_MACRO ATTRIBUTE_MACRO;"); + // Reflow after first macro. + // FIXME: these should indent but don't. +#if 0 + verifyFormat("@property(weak) id delegate ATTRIBUTE_MACRO(X)\n" + " ATTRIBUTE_MACRO;"); + verifyFormat("@property(weak) id delegate ATTRIBUTE_MACRO\n" + " ATTRIBUTE_MACRO(X);"); + verifyFormat("@property(weak) id delegate __attribute__((X))\n" + " ATTRIBUTE_MACRO;"); + verifyFormat("@property(weak) id delegate ATTRIBUTE_MACRO\n" + " __attribute__((X));"); + verifyFormat("@property(weak) id delegate __attribute__((X))\n" + " ATTRIBUTE_MACRO(X);"); + verifyFormat("@property(weak) id delegate ATTRIBUTE_MACRO(X)\n" + " __attribute__((X));"); +#endif + + // No column limit. + Style.ColumnLimit = 0; + verifyFormat( + "@property(weak) id delegate ATTRIBUTE_MACRO(X) ATTRIBUTE_MACRO;"); + verifyFormat( + "@property(weak) id delegate ATTRIBUTE_MACRO ATTRIBUTE_MACRO(X);"); + verifyFormat( + "@property(weak) id delegate __attribute__((X)) ATTRIBUTE_MACRO;"); + verifyFormat( + "@property(weak) id delegate ATTRIBUTE_MACRO __attribute__((X));"); + verifyFormat( + "@property(weak) id delegate __attribute__((X)) ATTRIBUTE_MACRO(X);"); + verifyFormat( + "@property(weak) id delegate ATTRIBUTE_MACRO(X) __attribute__((X));"); } } // end namespace Index: clang/unittests/Format/TokenAnnotatorTest.cpp =================================================================== --- clang/unittests/Format/TokenAnnotatorTest.cpp +++ clang/unittests/Format/TokenAnnotatorTest.cpp @@ -1795,6 +1795,116 @@ EXPECT_TOKEN(Tokens[13], tok::arrow, TT_Unknown); } +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_AttributeLParen); + EXPECT_TOKEN(Tokens[3], tok::r_paren, TT_AttributeRParen); + + // 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); + + // 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_AttributeLParen); + EXPECT_TOKEN(Tokens[3], tok::r_paren, TT_AttributeRParen); +} + +TEST_F(TokenAnnotatorTest, UnderstandsAttributeMacrosOnObjCDecl) { + // '__attribute__' has special handling. + auto Tokens = annotate("__attribute__(X) @interface Foo"); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::kw___attribute, TT_Unknown); + EXPECT_TOKEN(Tokens[1], tok::l_paren, TT_AttributeLParen); + EXPECT_TOKEN(Tokens[3], tok::r_paren, TT_AttributeRParen); + + // Generic macro has no special handling in this location. + Tokens = annotate("A(X) @interface Foo"); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + // Note: Don't check token-type as a random token in this position is hard to + // reason about. + EXPECT_TOKEN_KIND(Tokens[0], tok::identifier); + EXPECT_TOKEN_KIND(Tokens[1], tok::l_paren); + + // 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) @interface Foo", Style); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::identifier, TT_AttributeMacro); + EXPECT_TOKEN(Tokens[1], tok::l_paren, TT_AttributeLParen); + EXPECT_TOKEN(Tokens[3], tok::r_paren, TT_AttributeRParen); +} + +TEST_F(TokenAnnotatorTest, UnderstandsAttributeMacrosOnObjCMethodDecl) { + // '__attribute__' has special handling. + auto Tokens = annotate("- (id)init __attribute__(X);"); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[5], tok::kw___attribute, TT_Unknown); + EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_AttributeLParen); + EXPECT_TOKEN(Tokens[8], tok::r_paren, TT_AttributeRParen); + + // Generic macro has no special handling in this location. + Tokens = annotate("- (id)init A(X);"); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + // Note: Don't check token-type as a random token in this position is hard to + // reason about. + EXPECT_TOKEN_KIND(Tokens[5], tok::identifier); + EXPECT_TOKEN_KIND(Tokens[6], tok::l_paren); + + // 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("- (id)init A(X);", Style); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[5], tok::identifier, TT_AttributeMacro); + EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_AttributeLParen); + EXPECT_TOKEN(Tokens[8], tok::r_paren, TT_AttributeRParen); +} + +TEST_F(TokenAnnotatorTest, UnderstandsAttributeMacrosOnObjCProperty) { + // '__attribute__' has special handling. + auto Tokens = annotate("@property(weak) id delegate __attribute__(X);"); + ASSERT_EQ(Tokens.size(), 13u) << Tokens; + EXPECT_TOKEN(Tokens[7], tok::kw___attribute, TT_Unknown); + EXPECT_TOKEN(Tokens[8], tok::l_paren, TT_AttributeLParen); + EXPECT_TOKEN(Tokens[10], tok::r_paren, TT_AttributeRParen); + + // Generic macro has no special handling in this location. + Tokens = annotate("@property(weak) id delegate A(X);"); + ASSERT_EQ(Tokens.size(), 13u) << Tokens; + // Note: Don't check token-type as a random token in this position is hard to + // reason about. + EXPECT_TOKEN_KIND(Tokens[7], tok::identifier); + EXPECT_TOKEN_KIND(Tokens[8], tok::l_paren); + + // 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("@property(weak) id delegate A(X);", Style); + ASSERT_EQ(Tokens.size(), 13u) << Tokens; + EXPECT_TOKEN(Tokens[7], tok::identifier, TT_AttributeMacro); + EXPECT_TOKEN(Tokens[8], tok::l_paren, TT_AttributeLParen); + EXPECT_TOKEN(Tokens[10], tok::r_paren, TT_AttributeRParen); +} + TEST_F(TokenAnnotatorTest, UnderstandsVerilogOperators) { auto Annotate = [this](llvm::StringRef Code) { return annotate(Code, getLLVMStyle(FormatStyle::LK_Verilog));