Skip to content

Commit

Permalink
[clang-format] Support lightweight Objective-C generics
Browse files Browse the repository at this point in the history
Summary:
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

Reviewers: djasper, jolesiak

Reviewed By: djasper

Subscribers: klimek, cfe-commits

Differential Revision: https://reviews.llvm.org/D45185

llvm-svn: 329298
  • Loading branch information
bhamiltoncx committed Apr 5, 2018
1 parent f90ad9c commit 1462e84
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 3 deletions.
35 changes: 32 additions & 3 deletions clang/lib/Format/UnwrappedLineParser.cpp
Expand Up @@ -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 '>'.
}

Expand Down Expand Up @@ -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<ValueType : id <NSCopying, NSSecureCoding>> :
// NSObject <NSCopying, NSSecureCoding>
//
// 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
Expand Down
12 changes: 12 additions & 0 deletions clang/unittests/Format/FormatTestObjC.cpp
Expand Up @@ -299,6 +299,18 @@ TEST_F(FormatTestObjC, FormatObjCInterface) {
"+ (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"
Expand Down

0 comments on commit 1462e84

Please sign in to comment.