[AST][ObjC] Fix crash when printing invalid objc categories

Summary:
If no valid interface definition was found previously we would crash.

With this change instead we just print `<<error-type>>` in place
of the NULL interface. In the future this could be improved by
saving the invalid interface's name and using that.

Reviewers: sammccall, gribozavr

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D83513
This commit is contained in:
David Goldman 2020-07-09 17:32:05 -04:00
parent 9bf6354301
commit ea201e83e2
2 changed files with 34 additions and 13 deletions

View File

@ -1374,7 +1374,12 @@ void DeclPrinter::VisitObjCProtocolDecl(ObjCProtocolDecl *PID) {
} }
void DeclPrinter::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *PID) { void DeclPrinter::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *PID) {
Out << "@implementation " << *PID->getClassInterface() << '(' << *PID <<")\n"; Out << "@implementation ";
if (const auto *CID = PID->getClassInterface())
Out << *CID;
else
Out << "<<error-type>>";
Out << '(' << *PID << ")\n";
VisitDeclContext(PID, false); VisitDeclContext(PID, false);
Out << "@end"; Out << "@end";
@ -1382,7 +1387,11 @@ void DeclPrinter::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *PID) {
} }
void DeclPrinter::VisitObjCCategoryDecl(ObjCCategoryDecl *PID) { void DeclPrinter::VisitObjCCategoryDecl(ObjCCategoryDecl *PID) {
Out << "@interface " << *PID->getClassInterface(); Out << "@interface ";
if (const auto *CID = PID->getClassInterface())
Out << *CID;
else
Out << "<<error-type>>";
if (auto TypeParams = PID->getTypeParamList()) { if (auto TypeParams = PID->getTypeParamList()) {
PrintObjCTypeParams(TypeParams); PrintObjCTypeParams(TypeParams);
} }

View File

@ -76,14 +76,16 @@ public:
PrintedDeclMatches(StringRef Code, const std::vector<std::string> &Args, PrintedDeclMatches(StringRef Code, const std::vector<std::string> &Args,
const DeclarationMatcher &NodeMatch, const DeclarationMatcher &NodeMatch,
StringRef ExpectedPrinted, StringRef FileName, StringRef ExpectedPrinted, StringRef FileName,
PrintingPolicyModifier PolicyModifier = nullptr) { PrintingPolicyModifier PolicyModifier = nullptr,
bool AllowError = false) {
PrintMatch Printer(PolicyModifier); PrintMatch Printer(PolicyModifier);
MatchFinder Finder; MatchFinder Finder;
Finder.addMatcher(NodeMatch, &Printer); Finder.addMatcher(NodeMatch, &Printer);
std::unique_ptr<FrontendActionFactory> Factory( std::unique_ptr<FrontendActionFactory> Factory(
newFrontendActionFactory(&Finder)); newFrontendActionFactory(&Finder));
if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName)) if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName) &&
!AllowError)
return testing::AssertionFailure() return testing::AssertionFailure()
<< "Parsing error in \"" << Code.str() << "\""; << "Parsing error in \"" << Code.str() << "\"";
@ -170,16 +172,12 @@ PrintedDeclCXX1ZMatches(StringRef Code, const DeclarationMatcher &NodeMatch,
"input.cc"); "input.cc");
} }
::testing::AssertionResult PrintedDeclObjCMatches( ::testing::AssertionResult
StringRef Code, PrintedDeclObjCMatches(StringRef Code, const DeclarationMatcher &NodeMatch,
const DeclarationMatcher &NodeMatch, StringRef ExpectedPrinted, bool AllowError = false) {
StringRef ExpectedPrinted) {
std::vector<std::string> Args(1, ""); std::vector<std::string> Args(1, "");
return PrintedDeclMatches(Code, return PrintedDeclMatches(Code, Args, NodeMatch, ExpectedPrinted, "input.m",
Args, /*PolicyModifier=*/nullptr, AllowError);
NodeMatch,
ExpectedPrinted,
"input.m");
} }
} // unnamed namespace } // unnamed namespace
@ -1321,3 +1319,17 @@ TEST(DeclPrinter, TestObjCProtocol2) {
namedDecl(hasName("P1")).bind("id"), namedDecl(hasName("P1")).bind("id"),
"@protocol P1<P2>\n@end")); "@protocol P1<P2>\n@end"));
} }
TEST(DeclPrinter, TestObjCCategoryInvalidInterface) {
ASSERT_TRUE(PrintedDeclObjCMatches(
"@interface I (Extension) @end",
namedDecl(hasName("Extension")).bind("id"),
"@interface <<error-type>>(Extension)\n@end", /*AllowError=*/true));
}
TEST(DeclPrinter, TestObjCCategoryImplInvalidInterface) {
ASSERT_TRUE(PrintedDeclObjCMatches(
"@implementation I (Extension) @end",
namedDecl(hasName("Extension")).bind("id"),
"@implementation <<error-type>>(Extension)\n@end", /*AllowError=*/true));
}