From 1462e8440f429d61d48f3161f89e5f3336d45dfc Mon Sep 17 00:00:00 2001 From: Ben Hamilton Date: Thu, 5 Apr 2018 15:26:25 +0000 Subject: [PATCH] [clang-format] Support lightweight Objective-C generics Summary: Previously, `clang-format` didn't understand lightweight Objective-C generics, which have the form: ``` @interface Foo , ... > ... ``` The lightweight generic specifier list appears before the base class, if present, but because it starts with < like the protocol specifier list, `UnwrappedLineParser` was getting confused and failed to parse interfaces with both generics and protocol lists: ``` @interface Foo : NSObject ``` Since the parsed line would be incomplete, the format result would be very confused (e.g., https://bugs.llvm.org/show_bug.cgi?id=24381). This fixes the issue by explicitly parsing the ObjC lightweight generic conformance list, so the line is fully parsed. Fixes: https://bugs.llvm.org/show_bug.cgi?id=24381 Test Plan: New tests added. Ran tests with: % make -j16 FormatTests && ./tools/clang/unittests/Format/FormatTests Reviewers: djasper, jolesiak Reviewed By: djasper Subscribers: klimek, cfe-commits Differential Revision: https://reviews.llvm.org/D45185 llvm-svn: 329298 --- clang/lib/Format/UnwrappedLineParser.cpp | 35 ++++++++++++++++++++++++++++--- clang/unittests/Format/FormatTestObjC.cpp | 12 +++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 5ab5cc4..be7e2bb 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -2122,9 +2122,13 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr) { void UnwrappedLineParser::parseObjCProtocolList() { assert(FormatTok->Tok.is(tok::less) && "'<' expected."); - do + do { nextToken(); - while (!eof() && FormatTok->Tok.isNot(tok::greater)); + // Early exit in case someone forgot a close angle. + if (FormatTok->isOneOf(tok::semi, tok::l_brace) || + FormatTok->Tok.isObjCAtKeyword(tok::objc_end)) + return; + } while (!eof() && FormatTok->Tok.isNot(tok::greater)); nextToken(); // Skip '>'. } @@ -2155,7 +2159,32 @@ void UnwrappedLineParser::parseObjCInterfaceOrImplementation() { nextToken(); nextToken(); // interface name - // @interface can be followed by either a base class, or a category. + // @interface can be followed by a lightweight generic + // specialization list, then either a base class or a category. + if (FormatTok->Tok.is(tok::less)) { + // Unlike protocol lists, generic parameterizations support + // nested angles: + // + // @interface Foo> : + // NSObject + // + // so we need to count how many open angles we have left. + unsigned NumOpenAngles = 1; + do { + nextToken(); + // Early exit in case someone forgot a close angle. + if (FormatTok->isOneOf(tok::semi, tok::l_brace) || + FormatTok->Tok.isObjCAtKeyword(tok::objc_end)) + break; + if (FormatTok->Tok.is(tok::less)) + ++NumOpenAngles; + else if (FormatTok->Tok.is(tok::greater)) { + assert(NumOpenAngles > 0 && "'>' makes NumOpenAngles negative"); + --NumOpenAngles; + } + } while (!eof() && NumOpenAngles != 0); + nextToken(); // Skip '>'. + } if (FormatTok->Tok.is(tok::colon)) { nextToken(); nextToken(); // base class name diff --git a/clang/unittests/Format/FormatTestObjC.cpp b/clang/unittests/Format/FormatTestObjC.cpp index 5ea05f4..fe86cfe 100644 --- a/clang/unittests/Format/FormatTestObjC.cpp +++ b/clang/unittests/Format/FormatTestObjC.cpp @@ -299,6 +299,18 @@ TEST_F(FormatTestObjC, FormatObjCInterface) { "+ (id)init;\n" "@end"); + verifyFormat("@interface Foo : Bar {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + + verifyFormat("@interface Foo > : Xyzzy {\n" + " int _i;\n" + "}\n" + "+ (id)init;\n" + "@end"); + verifyFormat("@interface Foo (HackStuff) {\n" " int _i;\n" "}\n" -- 2.7.4