alejandro-alvarez-sonarsource created this revision. alejandro-alvarez-sonarsource added a reviewer: aaron.ballman. alejandro-alvarez-sonarsource added a project: clang. Herald added a subscriber: kadircet. Herald added a project: All. alejandro-alvarez-sonarsource requested review of this revision. Herald added subscribers: cfe-commits, ilya-biryukov.
- After a malformed top-level declaration - After a malformed templated class method declaration In both cases, when there is a malformed declaration, any following namespace is dropped from the AST. This can trigger a cascade of confusing diagnostics that may hide the original error. An example: // Start #include "SomeFile.h" template <class T> void Foo<T>::Bar(void* aRawPtr) { (void)(aRawPtr); } // End #include "SomeFile.h" #include <iostream> int main() {} We get the original error, plus 19 others from the standard library. With this patch, we only get the original error. clangd ``` can also benefit from this patch, as namespaces following the malformed declaration is now preserved. i.e. #pragma once MACRO_FROM_MISSING_INCLUDE("X") namespace my_namespace { //... } Before this patch, `my_namespace` is not visible for auto-completion. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D150258 Files: clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/ParseTemplate.cpp clang/test/Parser/cxx-namespace-after-missing-semicolon.cpp clang/test/Parser/cxx-template-recovery.cpp Index: clang/test/Parser/cxx-template-recovery.cpp =================================================================== --- /dev/null +++ clang/test/Parser/cxx-template-recovery.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -verify %s + +template <class T> +void Foo<T>::Bar(void* aRawPtr) { // expected-error {{no template named 'Foo'}} + (void)(aRawPtr); +} + +namespace baz { + class klass {}; +} + +int *variable = 0; // ok +const baz::klass object; // ok Index: clang/test/Parser/cxx-namespace-after-missing-semicolon.cpp =================================================================== --- /dev/null +++ clang/test/Parser/cxx-namespace-after-missing-semicolon.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -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 Index: clang/lib/Parse/ParseTemplate.cpp =================================================================== --- clang/lib/Parse/ParseTemplate.cpp +++ clang/lib/Parse/ParseTemplate.cpp @@ -275,10 +275,7 @@ // 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; } Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -2176,7 +2176,8 @@ return Actions.ConvertDeclToDeclGroup(TheDecl); } - if (isDeclarationSpecifier(ImplicitTypenameContext::No)) { + if (isDeclarationSpecifier(ImplicitTypenameContext::No) || + Tok.is(tok::kw_namespace)) { // 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 @@ -2304,8 +2305,7 @@ // 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); + SkipMalformedDecl(); } }
Index: clang/test/Parser/cxx-template-recovery.cpp =================================================================== --- /dev/null +++ clang/test/Parser/cxx-template-recovery.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -verify %s + +template <class T> +void Foo<T>::Bar(void* aRawPtr) { // expected-error {{no template named 'Foo'}} + (void)(aRawPtr); +} + +namespace baz { + class klass {}; +} + +int *variable = 0; // ok +const baz::klass object; // ok Index: clang/test/Parser/cxx-namespace-after-missing-semicolon.cpp =================================================================== --- /dev/null +++ clang/test/Parser/cxx-namespace-after-missing-semicolon.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -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 Index: clang/lib/Parse/ParseTemplate.cpp =================================================================== --- clang/lib/Parse/ParseTemplate.cpp +++ clang/lib/Parse/ParseTemplate.cpp @@ -275,10 +275,7 @@ // 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; } Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -2176,7 +2176,8 @@ return Actions.ConvertDeclToDeclGroup(TheDecl); } - if (isDeclarationSpecifier(ImplicitTypenameContext::No)) { + if (isDeclarationSpecifier(ImplicitTypenameContext::No) || + Tok.is(tok::kw_namespace)) { // 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 @@ -2304,8 +2305,7 @@ // 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); + SkipMalformedDecl(); } }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits