Skip to content

Commit

Permalink
Improve recovery when there is a stray ']' or ')' before the ';' at
Browse files Browse the repository at this point in the history
the end of a statement. Fixes <rdar://problem/6896493>.

llvm-svn: 113202
  • Loading branch information
DougGregor committed Sep 7, 2010
1 parent ce66d02 commit 45d6bdf
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 20 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ def err_expected_while : Error<"expected 'while' in do/while loop">;
def err_expected_semi_after : Error<"expected ';' after %0">;
def err_expected_semi_after_stmt : Error<"expected ';' after %0 statement">;
def err_expected_semi_after_expr : Error<"expected ';' after expression">;
def err_extraneous_token_before_semi : Error<"extraneous '%0' before ';'">;

def err_expected_semi_after_method_proto : Error<
"expected ';' after method prototype">;
def err_expected_semi_after_namespace_name : Error<
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,13 @@ class Parser : public CodeCompletionHandler {
const char *DiagMsg = "",
tok::TokenKind SkipToTok = tok::unknown);

/// \brief The parser expects a semicolon and, if present, will consume it.
///
/// If the next token is not a semicolon, this emits the specified diagnostic,
/// or, if there's just some closing-delimiter noise (e.g., ')' or ']') prior
/// to the semicolon, consumes that extra token.
bool ExpectAndConsumeSemi(unsigned DiagID);

//===--------------------------------------------------------------------===//
// Scope manipulation

Expand Down
7 changes: 4 additions & 3 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,9 @@ Decl *Parser::ParseUsingDirective(unsigned Context,
// Eat ';'.
DeclEnd = Tok.getLocation();
ExpectAndConsume(tok::semi,
GNUAttr ? diag::err_expected_semi_after_attribute_list :
diag::err_expected_semi_after_namespace_name, "", tok::semi);
GNUAttr ? diag::err_expected_semi_after_attribute_list
: diag::err_expected_semi_after_namespace_name,
"", tok::semi);

return Actions.ActOnUsingDirective(getCurScope(), UsingLoc, NamespcLoc, SS,
IdentLoc, NamespcName, Attr);
Expand Down Expand Up @@ -422,7 +423,7 @@ Decl *Parser::ParseStaticAssertDeclaration(SourceLocation &DeclEnd){
MatchRHSPunctuation(tok::r_paren, LParenLoc);

DeclEnd = Tok.getLocation();
ExpectAndConsume(tok::semi, diag::err_expected_semi_after_static_assert);
ExpectAndConsumeSemi(diag::err_expected_semi_after_static_assert);

return Actions.ActOnStaticAssertDeclaration(StaticAssertLoc,
AssertExpr.take(),
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Parse/ParseObjc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1731,7 +1731,7 @@ StmtResult Parser::ParseObjCAtStatement(SourceLocation AtLoc) {
}

// Otherwise, eat the semicolon.
ExpectAndConsume(tok::semi, diag::err_expected_semi_after_expr);
ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
return Actions.ActOnExprStmt(Actions.MakeFullExpr(Res.take()));
}

Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ Parser::ParseStatementOrDeclaration(bool OnlyStatement) {
return StmtError();
}
// Otherwise, eat the semicolon.
ExpectAndConsume(tok::semi, diag::err_expected_semi_after_expr);
ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
return Actions.ActOnExprStmt(Actions.MakeFullExpr(Expr.get()));
}

Expand Down Expand Up @@ -507,7 +507,7 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
// FIXME: Use attributes?
// Eat the semicolon at the end of stmt and convert the expr into a
// statement.
ExpectAndConsume(tok::semi, diag::err_expected_semi_after_expr);
ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
R = Actions.ActOnExprStmt(Actions.MakeFullExpr(Res.get()));
}
}
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/Parse/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,25 @@ bool Parser::ExpectAndConsume(tok::TokenKind ExpectedTok, unsigned DiagID,
return true;
}

bool Parser::ExpectAndConsumeSemi(unsigned DiagID) {
if (Tok.is(tok::semi) || Tok.is(tok::code_completion)) {
ConsumeAnyToken();
return false;
}

if ((Tok.is(tok::r_paren) || Tok.is(tok::r_square)) &&
NextToken().is(tok::semi)) {
Diag(Tok, diag::err_extraneous_token_before_semi)
<< PP.getSpelling(Tok)
<< FixItHint::CreateRemoval(Tok.getLocation());
ConsumeAnyToken(); // The ')' or ']'.
ConsumeToken(); // The ';'.
return false;
}

return ExpectAndConsume(tok::semi, DiagID);
}

//===----------------------------------------------------------------------===//
// Error recovery.
//===----------------------------------------------------------------------===//
Expand Down
32 changes: 18 additions & 14 deletions clang/test/FixIt/fixit-objc.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 -pedantic -verify %s
// RUN: cp %s %t
// RUN: %clang_cc1 -pedantic -fixit -x objective-c %t
// RUN: %clang_cc1 -pedantic -verify -x objective-c %t
// RUN: not %clang_cc1 -pedantic -fixit -x objective-c %t
// RUN: %clang_cc1 -pedantic -Werror -x objective-c %t

/* This is a test of the various code modification hints that are
provided as part of warning or extension diagnostics. All of the
Expand All @@ -10,28 +11,31 @@
@protocol X;

void foo() {
<X> *P; // should be fixed to 'id<X>'.
<X> *P; // expected-warning{{protocol qualifiers without 'id' is archaic}}
}

@class A;
@class NSString;

@interface Test
- (void)test:(NSString *)string;
- (void)test:(NSString *)string; // expected-note{{passing argument to parameter 'string' here}}

@property (copy) NSString *property;
@end

void g(NSString *a);
void h(id a);
void g(NSString *a); // expected-note{{passing argument to parameter 'a' here}}
void h(id a); // expected-note 2{{passing argument to parameter 'a' here}}

void f(Test *t) {
NSString *a = "Foo";
id b = "Foo";
A* c = "Foo"; // expected-warning {{incompatible pointer types initializing 'A *' with an expression of type 'char [4]'}}
g("Foo");
h("Foo");
h(("Foo"));
[t test:"Foo"];
t.property = "Foo";
NSString *a = "Foo"; // expected-warning {{incompatible pointer types initializing 'NSString *' with an expression of type 'char [4]'}}
id b = "Foo"; // expected-warning {{incompatible pointer types initializing 'id' with an expression of type 'char [4]'}}
g("Foo"); // expected-warning{{incompatible pointer types passing 'char [4]' to parameter of type 'NSString *'}}
h("Foo"); // expected-warning{{incompatible pointer types passing 'char [4]' to parameter of type 'id'}}
h(("Foo")); // expected-warning{{incompatible pointer types passing 'char [4]' to parameter of type 'id'}}
[t test:"Foo"]; // expected-warning{{incompatible pointer types sending 'char [4]' to parameter of type 'NSString *'}}
t.property = "Foo"; // expected-warning{{incompatible pointer types assigning to 'NSString *' from 'char [4]'}}

// <rdar://problem/6896493>
[t test:@"Foo"]]; // expected-error{{extraneous ']' before ';'}}
g(@"Foo")); // expected-error{{extraneous ')' before ';'}}
}

0 comments on commit 45d6bdf

Please sign in to comment.