Page MenuHomePhabricator

[clang-format] Support lightweight Objective-C generics

Authored by benhamilton on Apr 2 2018, 1:49 PM.



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.,

This fixes the issue by explicitly parsing the ObjC lightweight
generic conformance list, so the line is fully parsed.


Test Plan: New tests added. Ran tests with:

% make -j16 FormatTests && ./tools/clang/unittests/Format/FormatTests

Diff Detail

rC Clang

Event Timeline

benhamilton created this revision.Apr 2 2018, 1:49 PM
djasper added inline comments.Apr 3 2018, 11:44 PM

The UnwrappedLineParser is very much about error recovery. Implemented like this, it will consume the rest of the file if someone forgets to add the closing ">", which can very easily happen when formatting in an editor while coding.

Are there things (e.g. semicolons and braces) that clearly point to this having happened so that clang-format can recover?


I think we generally prefer:


I am slightly worried that this loop might be getting more complicated and it will be harder to determine that NumOpenAngles cannot be 0 here. Might be worth adding an assert. But might be overkill.


Are there more places where we might want to parse a lightweight generic list? If this is going to remain they only instance, I think I'd prefer a local lambda or just inlining the code. But up to you.


nitpick: As the comment refers to this following block as well, I'd remove the empty line.

benhamilton marked 3 inline comments as done.

Thanks, fixed!


Good call. I had modeled this after UnwrappedLineParser::parseObjCProtocolList(), which has the same issue.

Fixed both that method and this one to exit early on semicolon, l_brace, and @end.




Assert added.


It's just the one place, so inlined the code.

Protocol lists can appear both after @interface and @protocol, but lightweight generics are only for @interface. I confirmed this with a quick test — changing @protocol to @interface in the example below allows it to compile:

% cat test.m
#import <Foundation/Foundation.h>

@protocol Foo <KeyType> : NSObject
- (id)foo:(KeyType)aKey;

lang -c test.m                      
cattest.m:3:16: error: cannot find protocol declaration for 'KeyType'
@protocol Foo <KeyType> : NSObject
test.m:3:25: error: expected identifier or '('
@protocol Foo <KeyType> : NSObject
test.m:4:12: error: expected a type
- (id)foo:(KeyType)aKey;


djasper accepted this revision.Apr 5 2018, 8:14 AM

Looks good, thank you!

This revision is now accepted and ready to land.Apr 5 2018, 8:14 AM
This revision was automatically updated to reflect the committed changes.