[PATCH] D45185: [clang-format] Support lightweight Objective-C generics

Ben Hamilton via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 2 13:50:15 PDT 2018


benhamilton created this revision.
benhamilton added reviewers: djasper, jolesiak.
Herald added subscribers: cfe-commits, klimek.

Previously, `clang-format` didn't understand lightweight
Objective-C generics, which have the form:

  @interface Foo <KeyType,
                  ValueTypeWithConstraint : Foo,
  		AnotherValueTypeWithGenericConstraint: Bar<Baz>, ... > ...

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 <KeyType> : NSObject <NSCopying>

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


Repository:
  rC Clang

https://reviews.llvm.org/D45185

Files:
  lib/Format/UnwrappedLineParser.cpp
  lib/Format/UnwrappedLineParser.h
  unittests/Format/FormatTestObjC.cpp


Index: unittests/Format/FormatTestObjC.cpp
===================================================================
--- unittests/Format/FormatTestObjC.cpp
+++ unittests/Format/FormatTestObjC.cpp
@@ -298,6 +298,18 @@
                "+ (id)init;\n"
                "@end");
 
+  verifyFormat("@interface Foo <Baz : Blech> : Bar <Baz, Quux> {\n"
+               "  int _i;\n"
+               "}\n"
+               "+ (id)init;\n"
+               "@end");
+
+  verifyFormat("@interface Foo <Bar : Baz <Blech>> : Xyzzy <Corge> {\n"
+               "  int _i;\n"
+               "}\n"
+               "+ (id)init;\n"
+               "@end");
+
   verifyFormat("@interface Foo (HackStuff) {\n"
                "  int _i;\n"
                "}\n"
Index: lib/Format/UnwrappedLineParser.h
===================================================================
--- lib/Format/UnwrappedLineParser.h
+++ lib/Format/UnwrappedLineParser.h
@@ -116,6 +116,7 @@
   // parses the record as a child block, i.e. if the class declaration is an
   // expression.
   void parseRecord(bool ParseAsExpr = false);
+  void parseObjCLightweightGenericList();
   void parseObjCProtocolList();
   void parseObjCUntilAtEnd();
   void parseObjCInterfaceOrImplementation();
Index: lib/Format/UnwrappedLineParser.cpp
===================================================================
--- lib/Format/UnwrappedLineParser.cpp
+++ lib/Format/UnwrappedLineParser.cpp
@@ -2120,6 +2120,26 @@
   // "} n, m;" will end up in one unwrapped line.
 }
 
+void UnwrappedLineParser::parseObjCLightweightGenericList() {
+  assert(FormatTok->Tok.is(tok::less) && "'<' expected.");
+  // Unlike protocol lists, generic parameterizations support
+  // nested angles:
+  //
+  // @interface Foo<ValueType : id <NSCopying, NSSecureCoding>> :
+  //     NSObject <NSCopying, NSSecureCoding>
+  //
+  // so we need to count how many open angles we have left.
+  unsigned NumOpenAngles = 1;
+  do {
+    nextToken();
+    if (FormatTok->Tok.is(tok::less))
+      NumOpenAngles++;
+    else if (FormatTok->Tok.is(tok::greater))
+      NumOpenAngles--;
+  } while (!eof() && NumOpenAngles != 0);
+  nextToken(); // Skip '>'.
+}
+
 void UnwrappedLineParser::parseObjCProtocolList() {
   assert(FormatTok->Tok.is(tok::less) && "'<' expected.");
   do
@@ -2155,7 +2175,11 @@
   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))
+    parseObjCLightweightGenericList();
+
   if (FormatTok->Tok.is(tok::colon)) {
     nextToken();
     nextToken(); // base class name


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D45185.140686.patch
Type: text/x-patch
Size: 2728 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20180402/7820fb81/attachment.bin>


More information about the cfe-commits mailing list