]> granicus.if.org Git - clang/commitdiff
Part of PR10101: after a parse error in a declaration, try harder to find the
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 11 Apr 2012 20:59:20 +0000 (20:59 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 11 Apr 2012 20:59:20 +0000 (20:59 +0000)
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

include/clang/Parse/Parser.h
lib/Parse/ParseDecl.cpp
test/Parser/recovery.cpp [new file with mode: 0644]
test/SemaCXX/class.cpp

index e599207c2499f1984fb5351239dc169bce023e7c..9d784c9e9637a450906fb0d85558f14b14b78b07 100644 (file)
@@ -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.
 
index 08519410ddb0bde7452fd7b1d3be789abb58ea5e..cf3dca20d9013734ac6277cc6d10c89d343d2969 100644 (file)
@@ -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 (file)
index 0000000..ffa1bab
--- /dev/null
@@ -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 ';'}}
index ec82925fe4ed72c7e7c821a56035515e661ca410..4dffc8d9ecb80bfa08204b0476f85839df5d9d0f 100644 (file)
@@ -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;