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
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
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits