diff options
author | Alejandro Álvarez Ayllón <alejandro.alvarez@sonarsource.com> | 2023-05-15 07:39:58 -0400 |
---|---|---|
committer | Aaron Ballman <aaron@aaronballman.com> | 2023-05-15 07:39:58 -0400 |
commit | b321738f71259d138c9b2002944eb65f099ec2a6 (patch) | |
tree | 3fb967d72b11641b7a3473edf92ed09f31a37007 | |
parent | 45b899b92f5762c15d435e58666f5eee18e73b40 (diff) | |
download | llvm-b321738f71259d138c9b2002944eb65f099ec2a6.zip llvm-b321738f71259d138c9b2002944eb65f099ec2a6.tar.gz llvm-b321738f71259d138c9b2002944eb65f099ec2a6.tar.bz2 |
[clang][parser] Fix namespace dropping after malformed declarations
* 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"
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.
```
MACRO_FROM_MISSING_INCLUDE("X")
namespace my_namespace {
//...
}
```
Before this patch, my_namespace is not visible for auto-completion.
Differential Revision: https://reviews.llvm.org/D150258
-rw-r--r-- | clang/docs/ReleaseNotes.rst | 2 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 23 | ||||
-rw-r--r-- | clang/lib/Parse/ParseTemplate.cpp | 5 | ||||
-rw-r--r-- | clang/test/Parser/cxx-namespace-after-missing-semicolon.cpp | 21 | ||||
-rw-r--r-- | clang/test/Parser/cxx-template-recovery.cpp | 13 |
5 files changed, 49 insertions, 15 deletions
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index bb2178b..00edb84 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 2bbe2ba..92fa7d8 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 72df117..964d985 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 0000000..6d2dc60 --- /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 0000000..e22f6c5 --- /dev/null +++ b/clang/test/Parser/cxx-template-recovery.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fsyntax-only -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 |