aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2012-04-11 20:59:20 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2012-04-11 20:59:20 +0000
commit994d73f8473cb2cd3ce2f69c9575c95015be788a (patch)
treed6f95d3f1b8ed751e3d6c5485455411b26e81b48
parent47f1165c92bc4104e314223ed9ad251e914687c1 (diff)
Part of PR10101: after a parse error in a declaration, try harder to find the
right place to pick up parsing. In C++, this had a tendency to skip everything declared within headers if the TU starts with garbage. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@154530 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Parse/Parser.h4
-rw-r--r--lib/Parse/ParseDecl.cpp66
-rw-r--r--test/Parser/recovery.cpp42
-rw-r--r--test/SemaCXX/class.cpp4
4 files changed, 110 insertions, 6 deletions
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index e599207c24..9d784c9e96 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -742,6 +742,10 @@ private:
bool SkipUntil(ArrayRef<tok::TokenKind> Toks, bool StopAtSemi = true,
bool DontConsume = false, bool StopAtCodeCompletion = false);
+ /// SkipMalformedDecl - Read tokens until we get to some likely good stopping
+ /// point for skipping past a simple-declaration.
+ void SkipMalformedDecl();
+
//===--------------------------------------------------------------------===//
// Lexing and parsing of C++ inline methods.
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 08519410dd..cf3dca20d9 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -1117,6 +1117,67 @@ bool Parser::MightBeDeclarator(unsigned Context) {
}
}
+/// Skip until we reach something which seems like a sensible place to pick
+/// up parsing after a malformed declaration. This will sometimes stop sooner
+/// than SkipUntil(tok::r_brace) would, but will never stop later.
+void Parser::SkipMalformedDecl() {
+ while (true) {
+ switch (Tok.getKind()) {
+ case tok::l_brace:
+ // Skip until matching }, then stop. We've probably skipped over
+ // a malformed class or function definition or similar.
+ ConsumeBrace();
+ SkipUntil(tok::r_brace, /*StopAtSemi*/false);
+ if (Tok.is(tok::comma) || Tok.is(tok::l_brace) || Tok.is(tok::kw_try)) {
+ // This declaration isn't over yet. Keep skipping.
+ continue;
+ }
+ if (Tok.is(tok::semi))
+ ConsumeToken();
+ return;
+
+ case tok::l_square:
+ ConsumeBracket();
+ SkipUntil(tok::r_square, /*StopAtSemi*/false);
+ continue;
+
+ case tok::l_paren:
+ ConsumeParen();
+ SkipUntil(tok::r_paren, /*StopAtSemi*/false);
+ continue;
+
+ case tok::r_brace:
+ return;
+
+ case tok::semi:
+ ConsumeToken();
+ return;
+
+ case tok::kw_inline:
+ // 'inline namespace' at the start of a line is almost certainly
+ // a good place to pick back up parsing.
+ if (Tok.isAtStartOfLine() && NextToken().is(tok::kw_namespace))
+ return;
+ break;
+
+ case tok::kw_namespace:
+ // 'namespace' at the start of a line is almost certainly a good
+ // place to pick back up parsing.
+ if (Tok.isAtStartOfLine())
+ return;
+ break;
+
+ case tok::eof:
+ return;
+
+ default:
+ break;
+ }
+
+ ConsumeAnyToken();
+ }
+}
+
/// ParseDeclGroup - Having concluded that this is either a function
/// definition or a group of object declarations, actually parse the
/// result.
@@ -1131,10 +1192,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
// Bail out if the first declarator didn't seem well-formed.
if (!D.hasName() && !D.mayOmitIdentifier()) {
- // Skip until ; or }.
- SkipUntil(tok::r_brace, true, true);
- if (Tok.is(tok::semi))
- ConsumeToken();
+ SkipMalformedDecl();
return DeclGroupPtrTy();
}
diff --git a/test/Parser/recovery.cpp b/test/Parser/recovery.cpp
new file mode 100644
index 0000000000..ffa1bab55a
--- /dev/null
+++ b/test/Parser/recovery.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang -cc1 -verify -std=c++11 %s
+
+8gi///===--- recovery.cpp ---===// // expected-error {{unqualified-id}}
+namespace Std { // expected-note {{here}}
+ typedef int Important;
+}
+
+/ redeclare as an inline namespace // expected-error {{unqualified-id}}
+inline namespace Std { // expected-error {{cannot be reopened as inline}}
+ Important n;
+} / end namespace Std // expected-error {{unqualified-id}}
+int x;
+Std::Important y;
+
+// FIXME: Recover as if the typo correction were applied.
+extenr "C" { // expected-error {{did you mean 'extern'}} expected-error {{unqualified-id}}
+ void f();
+}
+void g() {
+ z = 1; // expected-error {{undeclared}}
+ f(); // expected-error {{undeclared}}
+}
+
+struct S {
+ int a, b, c;
+ S();
+};
+8S::S() : a{ 5 }, b{ 6 }, c{ 2 } { // expected-error {{unqualified-id}}
+ return;
+}
+int k;
+int l = k;
+
+5int m = { l }, n = m; // expected-error {{unqualified-id}}
+
+namespace N {
+ int
+} // expected-error {{unqualified-id}}
+
+// FIXME: Recover as if the typo correction were applied.
+strcut U { // expected-error {{did you mean 'struct'}}
+} *u[3]; // expected-error {{expected ';'}}
diff --git a/test/SemaCXX/class.cpp b/test/SemaCXX/class.cpp
index ec82925fe4..4dffc8d9ec 100644
--- a/test/SemaCXX/class.cpp
+++ b/test/SemaCXX/class.cpp
@@ -131,10 +131,10 @@ namespace pr6629 {
bogus<foo<T1,T2> > // expected-error {{unknown template name 'bogus'}} \
// BOGUS expected-error {{expected '{' after base class list}} \
// BOGUS expected-error {{expected ';' after struct}} \
- // BOGUS expected-error {{expected unqualified-id}} \
+ // BOGUS expected-error {{expected unqualified-id}}
{ };
- template<> struct foo<unknown,unknown> { // why isn't there an error here?
+ template<> struct foo<unknown,unknown> { // expected-error {{undeclared identifier 'unknown'}}
template <typename U1, typename U2> struct bar {
typedef bar type;
static const int value = 0;