diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index bb2178bfa5f96..00edb842ae715 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -449,6 +449,8 @@ Bug Fixes to C++ Support Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ +- Preserve ``namespace`` definitions that follow malformed declarations. + Miscellaneous Bug Fixes ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 2bbe2ba82139d..92fa7d8a8a759 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2176,13 +2176,16 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, return Actions.ConvertDeclToDeclGroup(TheDecl); } - if (isDeclarationSpecifier(ImplicitTypenameContext::No)) { - // If there is an invalid declaration specifier right after the - // function prototype, then we must be in a missing semicolon case - // where this isn't actually a body. Just fall through into the code - // that handles it as a prototype, and let the top-level code handle - // the erroneous declspec where it would otherwise expect a comma or - // semicolon. + if (isDeclarationSpecifier(ImplicitTypenameContext::No) || + Tok.is(tok::kw_namespace)) { + // If there is an invalid declaration specifier or a namespace + // definition right after the function prototype, then we must be in a + // missing semicolon case where this isn't actually a body. Just fall + // through into the code that handles it as a prototype, and let the + // top-level code handle the erroneous declspec where it would + // otherwise expect a comma or semicolon. Note that + // isDeclarationSpecifier already covers 'inline namespace', since + // 'inline' can be a declaration specifier. } else { Diag(Tok, diag::err_expected_fn_body); SkipUntil(tok::semi); @@ -2303,10 +2306,8 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // Okay, there was no semicolon and one was expected. If we see a // declaration specifier, just assume it was missing and continue parsing. // Otherwise things are very confused and we skip to recover. - if (!isDeclarationSpecifier(ImplicitTypenameContext::No)) { - SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); - TryConsumeToken(tok::semi); - } + if (!isDeclarationSpecifier(ImplicitTypenameContext::No)) + SkipMalformedDecl(); } return Actions.FinalizeDeclaratorGroup(getCurScope(), DS, DeclsInGroup); diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 72df117c123e6..964d985b6b6e6 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -275,10 +275,7 @@ Decl *Parser::ParseSingleDeclarationAfterTemplate( // Error parsing the declarator? if (!DeclaratorInfo.hasName()) { - // If so, skip until the semi-colon or a }. - SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); - if (Tok.is(tok::semi)) - ConsumeToken(); + SkipMalformedDecl(); return nullptr; } diff --git a/clang/test/Parser/cxx-namespace-after-missing-semicolon.cpp b/clang/test/Parser/cxx-namespace-after-missing-semicolon.cpp new file mode 100644 index 0000000000000..6d2dc606ef642 --- /dev/null +++ b/clang/test/Parser/cxx-namespace-after-missing-semicolon.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +UNKNOWN_MACRO_1("z", 1) // expected-error {{a type specifier is required for all declarations}} +// expected-error@-1 {{expected ';' after top level declarator}} + +namespace foo { + class bar {}; +} + +int variable = 0; // ok +foo::bar something; // ok + +UNKNOWN_MACRO_2(void) // expected-error {{a type specifier is required for all declarations}} +// expected-error@-1 {{expected ';' after top level declarator}} + +namespace baz { + using Bool = bool; +} + +int variable2 = 2; // ok +const baz::Bool flag = false; // ok diff --git a/clang/test/Parser/cxx-template-recovery.cpp b/clang/test/Parser/cxx-template-recovery.cpp new file mode 100644 index 0000000000000..e22f6c55e3f0c --- /dev/null +++ b/clang/test/Parser/cxx-template-recovery.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +template +void Foo::Bar(void* aRawPtr) { // expected-error {{no template named 'Foo'}} + (void)(aRawPtr); +} + +namespace baz { + class klass {}; +} + +int *variable = 0; // ok +const baz::klass object; // ok