From 7eecf4b6e3c7d9ccc29df528fc746b1e9d18ec88 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 9 Jan 2013 20:25:35 +0000 Subject: [PATCH] Formatter: Add support for @interface. Previously: @interface Foo + (id)init; @end Now: @interface Foo + (id)init; @end Some tweaking remains, but this is a good first step. llvm-svn: 171995 --- clang/lib/Format/UnwrappedLineParser.cpp | 42 ++++++++ clang/lib/Format/UnwrappedLineParser.h | 1 + clang/unittests/Format/FormatTest.cpp | 125 ++++++++++++++++++----- 3 files changed, 140 insertions(+), 28 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 99d5f6279df3..131d982906b6 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 500054fe9a2e..28ef23519001 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 c21ff24834bb..4decf94c766f 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"); -- GitLab