From b8caac8e321d28ec5aa45eccf199e40659c09493 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 11 Apr 2012 20:59:20 +0000 Subject: [PATCH] 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. llvm-svn: 154530 --- clang/include/clang/Parse/Parser.h | 4 ++ clang/lib/Parse/ParseDecl.cpp | 66 ++++++++++++++++++++++++++++-- clang/test/Parser/recovery.cpp | 42 +++++++++++++++++++ clang/test/SemaCXX/class.cpp | 4 +- 4 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 clang/test/Parser/recovery.cpp diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index e599207c2499..9d784c9e9637 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -742,6 +742,10 @@ private: bool SkipUntil(ArrayRef 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/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 08519410ddb0..cf3dca20d901 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/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/clang/test/Parser/recovery.cpp b/clang/test/Parser/recovery.cpp new file mode 100644 index 000000000000..ffa1bab55a44 --- /dev/null +++ b/clang/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/clang/test/SemaCXX/class.cpp b/clang/test/SemaCXX/class.cpp index ec82925fe4ed..4dffc8d9ecb8 100644 --- a/clang/test/SemaCXX/class.cpp +++ b/clang/test/SemaCXX/class.cpp @@ -131,10 +131,10 @@ namespace pr6629 { bogus > // 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 { // why isn't there an error here? + template<> struct foo { // expected-error {{undeclared identifier 'unknown'}} template struct bar { typedef bar type; static const int value = 0;