Patch to parse objective-c's @try-statement and @throw-statement.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@42148 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/Parse/ParseExpr.cpp b/Parse/ParseExpr.cpp
index 2e2a64e..1a7c403 100644
--- a/Parse/ParseExpr.cpp
+++ b/Parse/ParseExpr.cpp
@@ -172,6 +172,18 @@
return ParseRHSOfBinaryExpression(LHS, prec::Comma);
}
+/// This routine is called when the '@' is seen and consumed.
+/// Current token is an Identifier and is not a 'try'. This
+/// routine is necessary to disambiguate @try-statement from
+/// ,for example, @encode-expression.
+///
+Parser::ExprResult Parser::ParseExpressionWithLeadingAt(SourceLocation &AtLoc) {
+ ExprResult LHS = ParseObjCExpression(AtLoc);
+ if (LHS.isInvalid) return LHS;
+
+ return ParseRHSOfBinaryExpression(LHS, prec::Comma);
+}
+
/// ParseAssignmentExpression - Parse an expr that doesn't include commas.
///
Parser::ExprResult Parser::ParseAssignmentExpression() {
@@ -589,7 +601,10 @@
case tok::kw_static_cast:
return ParseCXXCasts();
case tok::at:
- return ParseObjCExpression();
+ {
+ SourceLocation AtLoc = ConsumeToken();
+ return ParseObjCExpression(AtLoc);
+ }
case tok::l_square:
return ParseObjCMessageExpression ();
default:
diff --git a/Parse/ParseObjc.cpp b/Parse/ParseObjc.cpp
index 356c7a7..0533660 100644
--- a/Parse/ParseObjc.cpp
+++ b/Parse/ParseObjc.cpp
@@ -916,6 +916,77 @@
Diag(Tok, diag::err_expected_semi_after, "@dynamic");
return 0;
}
+
+/// objc-throw-statement:
+/// throw expression[opt];
+///
+Parser::DeclTy *Parser::ParseObjCThrowStmt(SourceLocation &atLoc) {
+ ConsumeToken(); // consume throw
+ if (Tok.getKind() != tok::semi) {
+ ExprResult Res = ParseAssignmentExpression();
+ if (Res.isInvalid) {
+ SkipUntil(tok::semi);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/// objc-try-catch-statement:
+/// @try compound-statement objc-catch-list[opt]
+/// @try compound-statement objc-catch-list[opt] @finally compound-statement
+///
+/// objc-catch-list:
+/// @catch ( parameter-declaration ) compound-statement
+/// objc-catch-list @catch ( catch-parameter-declaration ) compound-statement
+/// catch-parameter-declaration:
+/// parameter-declaration
+/// '...' [OBJC2]
+///
+Parser::DeclTy *Parser::ParseObjCTryStmt(SourceLocation &atLoc) {
+ bool catch_or_finally_seen = false;
+ ConsumeToken(); // consume try
+ if (Tok.getKind() != tok::l_brace) {
+ Diag (Tok, diag::err_expected_lbrace);
+ return 0;
+ }
+ StmtResult TryBody = ParseCompoundStatementBody();
+ while (Tok.getKind() == tok::at) {
+ ConsumeToken();
+ if (Tok.getIdentifierInfo()->getObjCKeywordID() == tok::objc_catch) {
+ SourceLocation catchLoc = ConsumeToken(); // consume catch
+ if (Tok.getKind() == tok::l_paren) {
+ ConsumeParen();
+ if (Tok.getKind() != tok::ellipsis) {
+ DeclSpec DS;
+ ParseDeclarationSpecifiers(DS);
+ // Parse the parameter-declaration.
+ // FIXME: BlockContext may not be the right context!
+ Declarator ParmDecl(DS, Declarator::BlockContext);
+ ParseDeclarator(ParmDecl);
+ }
+ else
+ ConsumeToken(); // consume '...'
+ ConsumeParen();
+ StmtResult CatchMody = ParseCompoundStatementBody();
+ }
+ else {
+ Diag(catchLoc, diag::err_expected_lparen_after, "@catch clause");
+ return 0;
+ }
+ catch_or_finally_seen = true;
+ }
+ else if (Tok.getIdentifierInfo()->getObjCKeywordID() == tok::objc_finally) {
+ ConsumeToken(); // consume finally
+ StmtResult FinallyBody = ParseCompoundStatementBody();
+ catch_or_finally_seen = true;
+ break;
+ }
+ }
+ if (!catch_or_finally_seen)
+ Diag(atLoc, diag::err_missing_catch_finally);
+ return 0;
+}
/// objc-method-def: objc-method-proto ';'[opt] '{' body '}'
///
@@ -954,8 +1025,7 @@
StmtResult FnBody = ParseCompoundStatementBody();
}
-Parser::ExprResult Parser::ParseObjCExpression() {
- SourceLocation AtLoc = ConsumeToken(); // the "@"
+Parser::ExprResult Parser::ParseObjCExpression(SourceLocation &AtLoc) {
switch (Tok.getKind()) {
case tok::string_literal: // primary-expression: string-literal
diff --git a/Parse/ParseStmt.cpp b/Parse/ParseStmt.cpp
index fb4fe1e..d37973b 100644
--- a/Parse/ParseStmt.cpp
+++ b/Parse/ParseStmt.cpp
@@ -75,22 +75,35 @@
// Cases in this switch statement should fall through if the parser expects
// the token to end in a semicolon (in which case SemiError should be set),
// or they directly 'return;' if not.
- switch (Tok.getKind()) {
+ tok::TokenKind Kind = Tok.getKind();
+ SourceLocation AtLoc;
+ switch (Kind) {
case tok::identifier: // C99 6.8.1: labeled-statement
// identifier ':' statement
// declaration (if !OnlyStatement)
// expression[opt] ';'
return ParseIdentifierStatement(OnlyStatement);
+ case tok::at: // May be a @try or @throw statement
+ {
+ AtLoc = ConsumeToken(); // consume @
+ if (Tok.getIdentifierInfo()->getObjCKeywordID() == tok::objc_try)
+ return ParseObjCTryStmt(AtLoc);
+ else if (Tok.getIdentifierInfo()->getObjCKeywordID() == tok::objc_throw)
+ return ParseObjCThrowStmt(AtLoc);
+ }
+ // Fall thru.
+
default:
- if (!OnlyStatement && isDeclarationSpecifier()) {
+ if (Kind != tok::at && !OnlyStatement && isDeclarationSpecifier()) {
return Actions.ActOnDeclStmt(ParseDeclaration(Declarator::BlockContext));
} else if (Tok.getKind() == tok::r_brace) {
Diag(Tok, diag::err_expected_statement);
return true;
} else {
// expression[opt] ';'
- ExprResult Res = ParseExpression();
+ ExprResult Res = (Kind == tok::at) ? ParseExpressionWithLeadingAt(AtLoc)
+ : ParseExpression();
if (Res.isInvalid) {
// If the expression is invalid, skip ahead to the next semicolon. Not
// doing this opens us up to the possibility of infinite loops if
diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def
index 30253f7..059edec 100644
--- a/include/clang/Basic/DiagnosticKinds.def
+++ b/include/clang/Basic/DiagnosticKinds.def
@@ -406,6 +406,8 @@
"@required may be specified in protocols only")
DIAG(err_objc_protocol_optional, ERROR,
"@optional may be specified in protocols only")
+DIAG(err_missing_catch_finally, ERROR,
+ "@try statment without a @catch and @finally clause")
//===----------------------------------------------------------------------===//
// Semantic Analysis
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 9f57b13..243b88f 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -270,6 +270,8 @@
DeclTy *ParseObjCAtAliasDeclaration(SourceLocation atLoc);
DeclTy *ParseObjCPropertySynthesize(SourceLocation atLoc);
DeclTy *ParseObjCPropertyDynamic(SourceLocation atLoc);
+ DeclTy *ParseObjCTryStmt(SourceLocation &atLoc);
+ DeclTy *ParseObjCThrowStmt(SourceLocation &atLoc);
IdentifierInfo *ParseObjCSelector();
// Definitions for Objective-c context sensitive keywords recognition.
@@ -310,6 +312,7 @@
ExprResult ParseAssignmentExpression(); // Expr that doesn't include commas.
ExprResult ParseExpressionWithLeadingIdentifier(const Token &Tok);
+ ExprResult ParseExpressionWithLeadingAt(SourceLocation &AtLoc);
ExprResult ParseAssignmentExprWithLeadingIdentifier(const Token &Tok);
ExprResult ParseAssignmentExpressionWithLeadingStar(const Token &Tok);
@@ -355,7 +358,7 @@
//===--------------------------------------------------------------------===//
// Objective-C Expressions
- ExprResult ParseObjCExpression();
+ ExprResult ParseObjCExpression(SourceLocation &AtLocation);
ExprResult ParseObjCStringLiteral();
ExprResult ParseObjCEncodeExpression();
ExprResult ParseObjCProtocolExpression();
diff --git a/test/Parser/objc-try-catch-1.m b/test/Parser/objc-try-catch-1.m
new file mode 100644
index 0000000..f4a1201
--- /dev/null
+++ b/test/Parser/objc-try-catch-1.m
@@ -0,0 +1,36 @@
+void * proc();
+
+@interface Frob
+@end
+
+@interface Frob1
+@end
+
+void * foo()
+{
+ @try {
+ return proc();
+ }
+ @catch (Frob* ex) {
+ @throw;
+ }
+ @catch (Frob1* ex) {
+ @throw proc();
+ }
+ @finally {
+ @try {
+ return proc();
+ }
+ @catch (Frob* ex) {
+ @throw;
+ }
+ @catch(...) {
+ @throw (4,3,proc());
+ }
+ }
+
+ @try { // expected-error {{@try statment without a @catch and @finally clause}}
+ return proc();
+ }
+}
+