diff --git a/clang-tools-extra/pseudo/lib/cxx/CXX.cpp b/clang-tools-extra/pseudo/lib/cxx/CXX.cpp --- a/clang-tools-extra/pseudo/lib/cxx/CXX.cpp +++ b/clang-tools-extra/pseudo/lib/cxx/CXX.cpp @@ -161,6 +161,59 @@ return symbolToToken(P.Lookahead) != tok::kw_else; } +bool canOmitDeclarator(const ForestNode* N) { + // Returns true if a decl-specifier is + // a class-specifier + // or an elaborated-type-specifier with class-key, or an enum_specifier. + auto hasTargetSpecifier = [](const ForestNode *N, auto &hasTargetSpecifier) { + if (N->kind() == ForestNode::Opaque) + return false; + if (N->kind() == ForestNode::Ambiguous) + return llvm::all_of(N->alternatives(), + std::bind(hasTargetSpecifier, std::placeholders::_1, + hasTargetSpecifier)); + + switch (N->rule()) { + case rule::defining_type_specifier::type_specifier: + case rule::defining_type_specifier::enum_specifier: + case rule::type_specifier::elaborated_type_specifier: + case rule::decl_specifier::defining_type_specifier: + return hasTargetSpecifier(N->elements()[0], hasTargetSpecifier); + + case rule::enum_specifier::enum_head__L_BRACE__R_BRACE: + case rule::enum_specifier:: + enum_head__L_BRACE__enumerator_list__COMMA__R_BRACE: + case rule::enum_specifier::enum_head__L_BRACE__enumerator_list__R_BRACE: + case rule::elaborated_type_specifier:: + class_key__nested_name_specifier__IDENTIFIER: + case rule::elaborated_type_specifier:: + class_key__nested_name_specifier__simple_template_id: + case rule::elaborated_type_specifier:: + class_key__nested_name_specifier__TEMPLATE__simple_template_id: + case rule::elaborated_type_specifier::class_key__simple_template_id: + case rule::elaborated_type_specifier::class_key__IDENTIFIER: + case rule::defining_type_specifier::class_specifier: + return true; + default: + return false; + } + }; + while (true) { + switch (N->rule()) { + case cxx::rule::decl_specifier_seq::decl_specifier: + return hasTargetSpecifier(N->elements()[0], hasTargetSpecifier); + case cxx::rule::decl_specifier_seq::decl_specifier__decl_specifier_seq: + if (hasTargetSpecifier(N->elements()[0], hasTargetSpecifier)) + return true; + N = N->elements()[1]; + break; + default: + LLVM_DEBUG(llvm::errs() << "Unhandled rule " << N->rule() << "\n"); + llvm_unreachable("decl_specifier_seq be exhaustive!"); + } + } + return false; +} // Whether this e.g. decl-specifier contains an "exclusive" type such as a class // name, and thus can't combine with a second exclusive type. // @@ -320,6 +373,15 @@ {rule::nested_name_specifier::COLONCOLON, TOKEN_GUARD(coloncolon, Tok.prev().Kind != tok::identifier)}, + // Implement C++ [dcl.pre#5]: + // In a simple-declaration, the optional init-declarator-list can be + // omitted only when declaring a class ([class.pre]) or enumeration + // ([dcl.enum]), that is, when the decl-specifier-seq contains either a + // class-specifier, an elaborated-type-specifier with a class-key + // ([class.name]), or an enum-specifier. + {rule::simple_declaration::decl_specifier_seq__SEMI, + GUARD(canOmitDeclarator(P.RHS[0]))}, + // The grammar distinguishes (only) user-defined vs plain string literals, // where the clang lexer distinguishes (only) encoding types. {rule::user_defined_string_literal_chunk::STRING_LITERAL, diff --git a/clang-tools-extra/pseudo/lib/cxx/cxx.bnf b/clang-tools-extra/pseudo/lib/cxx/cxx.bnf --- a/clang-tools-extra/pseudo/lib/cxx/cxx.bnf +++ b/clang-tools-extra/pseudo/lib/cxx/cxx.bnf @@ -334,7 +334,7 @@ block-declaration := opaque-enum-declaration nodeclspec-function-declaration := function-declarator ; alias-declaration := USING IDENTIFIER = defining-type-id ; -simple-declaration := decl-specifier-seq init-declarator-list_opt ; +simple-declaration := decl-specifier-seq init-declarator-list_opt ; [guard] simple-declaration := decl-specifier-seq ref-qualifier_opt [ identifier-list ] initializer ; static_assert-declaration := STATIC_ASSERT ( constant-expression ) ; static_assert-declaration := STATIC_ASSERT ( constant-expression , string-literal ) ;