diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 99d5f6279df3398bae816a42a82f3e174f1f4403..131d982906b6f907bdee82222f8d8010c6238d93 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -208,6 +208,8 @@ void UnwrappedLineParser::parseStructuralElement() { case tok::objc_package: case tok::objc_private: return parseAccessSpecifier(); + case tok::objc_interface: + return parseObjCInterface(); default: break; } @@ -494,6 +496,46 @@ void UnwrappedLineParser::parseStructOrClass() { } while (!eof()); } +void UnwrappedLineParser::parseObjCInterface() { + nextToken(); + nextToken(); // interface name + + // @interface can be followed by either a base class, or a category. + if (FormatTok.Tok.is(tok::colon)) { + nextToken(); + nextToken(); // base class name + } else if (FormatTok.Tok.is(tok::l_paren)) + // Skip category, if present. + parseParens(); + + // Skip protocol list, if present. + if (FormatTok.Tok.is(tok::less)) { + do + nextToken(); + while (!eof() && FormatTok.Tok.isNot(tok::greater)); + nextToken(); // Skip '>'. + } + + // If instance variables are present, keep the '{' on the first line too. + if (FormatTok.Tok.is(tok::l_brace)) + parseBlock(); + + // With instance variables, this puts '}' on its own line. Without instance + // variables, this ends the @interface line. + addUnwrappedLine(); + + // Read everything up to the @end. + do { + if (FormatTok.Tok.isObjCAtKeyword(tok::objc_end)) { + nextToken(); + addUnwrappedLine(); + break; + } + + parseStructuralElement(); + } while (!eof()); +} + void UnwrappedLineParser::addUnwrappedLine() { if (!RootTokenInitialized) return; diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h index 500054fe9a2ede8debfd21b11e0111312ab766de..28ef235190019a19feb31021bc22206bff4be648 100644 --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -142,6 +142,7 @@ private: void parseAccessSpecifier(); void parseEnum(); void parseStructOrClass(); + void parseObjCInterface(); void addUnwrappedLine(); bool eof() const; void nextToken(); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index c21ff24834bbd0f08206f2f4845c995e35a8b2c8..4decf94c766f5dfeaa537883c5958b04e21e8436 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -443,34 +443,6 @@ TEST_F(FormatTest, FormatObjCTryCatch) { "}"); } -TEST_F(FormatTest, FormatObjCInterface) { - verifyFormat("@interface Foo : NSObject {\n" - "@public\n" - " int field1;\n" - "@protected\n" - " int field2;\n" - "@private\n" - " int field3;\n" - "@package\n" - " int field4;\n" - "}\n" - "+ (id)init;\n" - "@end"); - - verifyGoogleFormat("@interface Foo : NSObject {\n" - " @public\n" - " int field1;\n" - " @protected\n" - " int field2;\n" - " @private\n" - " int field3;\n" - " @package\n" - " int field4;\n" - "}\n" - "+ (id)init;\n" - "@end"); -} - TEST_F(FormatTest, StaticInitializers) { verifyFormat("static SomeClass SC = { 1, 'a' };"); @@ -1180,6 +1152,103 @@ TEST_F(FormatTest, FormatObjCBlocks) { verifyFormat("int (^Block1) (int, int) = ^(int i, int j)"); } +TEST_F(FormatTest, FormatObjCInterface) { + // FIXME: Handle comments like in "@interface /* wait for it */ Foo", PR14875 + // FIXME: In google style, it's "+(id) init", not "+ (id)init". + verifyFormat("@interface Foo : NSObject {\n" + "@public\n" + " int field1;\n" + "@protected\n" + " int field2;\n" + "@private\n" + " int field3;\n" + "@package\n" + " int field4;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + // FIXME: In LLVM style, there should be a space before '<' for protocols. + verifyGoogleFormat("@interface Foo : NSObject {\n" + " @public\n" + " int field1;\n" + " @protected\n" + " int field2;\n" + " @private\n" + " int field3;\n" + " @package\n" + " int field4;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo\n" + "+ (id)init;\n" + "// Look, a comment!\n" + "- (int)answerWith:(int)i;\n" + "@end"); + + verifyFormat("@interface Foo\n" + "@end"); + + verifyFormat("@interface Foo : Bar\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo : Bar\n" + "+ (id)init;\n" + "@end"); + + // FIXME: there should be a space before '(' for categories. + verifyFormat("@interface Foo(HackStuff)\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo()\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo(HackStuff)\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo : Bar {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo : Bar {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo(HackStuff) {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo() {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo(HackStuff) {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); +} + TEST_F(FormatTest, ObjCAt) { verifyFormat("@autoreleasepool"); verifyFormat("@catch");