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 @@ -46,6 +46,66 @@ return Tokens.tokens()[RHS.front()->startTokenIndex()].text() == "export"; } +bool isFunctionDeclarator(const ForestNode *Declarator) { + assert(Declarator->symbol() == (SymbolID)(cxx::Symbol::declarator)); + bool IsFunction = false; + using cxx::Rule; + while (true) { + // not well-formed code, return the best guess. + if (Declarator->kind() != ForestNode::Sequence) + return IsFunction; + + switch ((cxx::Rule)Declarator->rule()) { + case Rule::noptr_declarator_0declarator_id: // reached the bottom + return IsFunction; + // *X is a nonfunction (unless X is a function). + case Rule::ptr_declarator_0ptr_operator_1ptr_declarator: + Declarator = Declarator->elements()[1]; + IsFunction = false; + continue; + // X() is a function (unless X is a pointer or similar). + case Rule:: + declarator_0noptr_declarator_1parameters_and_qualifiers_2trailing_return_type: + case Rule::noptr_declarator_0noptr_declarator_1parameters_and_qualifiers: + Declarator = Declarator->elements()[0]; + IsFunction = true; + continue; + // X[] is an array (unless X is a pointer or function). + case Rule:: + noptr_declarator_0noptr_declarator_1l_square_2constant_expression_3r_square: + case Rule::noptr_declarator_0noptr_declarator_1l_square_2r_square: + Declarator = Declarator->elements()[0]; + IsFunction = false; + continue; + // (X) is whatever X is. + case Rule::noptr_declarator_0l_paren_1ptr_declarator_2r_paren: + Declarator = Declarator->elements()[1]; + continue; + case Rule::ptr_declarator_0noptr_declarator: + case Rule::declarator_0ptr_declarator: + Declarator = Declarator->elements()[0]; + continue; + + default: + assert(false && "unhandled declarator for IsFunction"); + return IsFunction; + } + } + llvm_unreachable("unreachable"); +} +bool guardFunction(llvm::ArrayRef RHS, + const TokenStream &Tokens) { + assert(RHS.size() == 1 && + RHS.front()->symbol() == (SymbolID)(cxx::Symbol::declarator)); + return isFunctionDeclarator(RHS.front()); +} +bool guardNonFunction(llvm::ArrayRef RHS, + const TokenStream &Tokens) { + assert(RHS.size() == 1 && + RHS.front()->symbol() == (SymbolID)(cxx::Symbol::declarator)); + return !isFunctionDeclarator(RHS.front()); +} + llvm::DenseMap buildGuards() { return { {(ExtensionID)Extension::Override, guardOverride}, @@ -53,6 +113,8 @@ {(ExtensionID)Extension::Import, guardImport}, {(ExtensionID)Extension::Export, guardExport}, {(ExtensionID)Extension::Module, guardModule}, + {(ExtensionID)Extension::FunctionDeclarator, guardFunction}, + {(ExtensionID)Extension::NonFunctionDeclarator, guardNonFunction}, }; } 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 @@ -332,7 +332,7 @@ block-declaration := static_assert-declaration block-declaration := alias-declaration block-declaration := opaque-enum-declaration -nodeclspec-function-declaration := declarator ; +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 ref-qualifier_opt [ identifier-list ] initializer ; @@ -402,8 +402,19 @@ placeholder-type-specifier := type-constraint_opt DECLTYPE ( AUTO ) init-declarator-list := init-declarator init-declarator-list := init-declarator-list , init-declarator -init-declarator := declarator initializer_opt -init-declarator := declarator requires-clause +#! The standard grammar allows: +#! 1) an initializer with any declarator, including a function declarator, this +#! creates an ambiguity where a function definition is misparsed as a simple +#! declaration; +#! 2) an function-body with any declarator, includeing a non-function +#! declarator, this creates an ambiguity whwere a simple-declaration is +#! misparsed as a function-definition; +#! We extend the standard declarator to function-declarator and non-function-declarator +#! to eliminate these false parses. +init-declarator := non-function-declarator initializer_opt +init-declarator := function-declarator requires-clause_opt +function-declarator := declarator [guard=FunctionDeclarator] +non-function-declarator := declarator [guard=NonFunctionDeclarator] declarator := ptr-declarator declarator := noptr-declarator parameters-and-qualifiers trailing-return-type ptr-declarator := noptr-declarator @@ -472,8 +483,8 @@ expr-or-braced-init-list := expression expr-or-braced-init-list := braced-init-list # dcl.fct -function-definition := decl-specifier-seq_opt declarator virt-specifier-seq_opt function-body -function-definition := decl-specifier-seq_opt declarator requires-clause function-body +function-definition := decl-specifier-seq_opt function-declarator virt-specifier-seq_opt function-body +function-definition := decl-specifier-seq_opt function-declarator requires-clause function-body function-body := ctor-initializer_opt compound-statement function-body := function-try-block function-body := = DEFAULT ; diff --git a/clang-tools-extra/pseudo/test/cxx/declarator-function.cpp b/clang-tools-extra/pseudo/test/cxx/declarator-function.cpp --- a/clang-tools-extra/pseudo/test/cxx/declarator-function.cpp +++ b/clang-tools-extra/pseudo/test/cxx/declarator-function.cpp @@ -1,11 +1,9 @@ // The standard grammar allows an init-list with any declarator, including // a function declarator. This creates an ambiguity where a function-definition // is misparsed as a simple-declaration. -// FIXME: eliminate this false parse. -// XFAIL: * // RUN: clang-pseudo -grammar=cxx -source=%s --print-forest | FileCheck %s void s(){}; // CHECK-NOT: simple-declaration -// CHECK: function-definition := decl-specifier-seq declarator -// function-body CHECK-NOT: simple-declaration +// CHECK: function-definition := decl-specifier-seq function-declarator function-body +// CHECK-NOT: simple-declaration diff --git a/clang-tools-extra/pseudo/test/cxx/declarator-var.cpp b/clang-tools-extra/pseudo/test/cxx/declarator-var.cpp --- a/clang-tools-extra/pseudo/test/cxx/declarator-var.cpp +++ b/clang-tools-extra/pseudo/test/cxx/declarator-var.cpp @@ -1,11 +1,9 @@ // The standard grammar allows an function-body to use any declarator, including // a non-function declarator. This creates an ambiguity where a // simple-declaration is misparsed as a function-definition. -// FIXME: eliminate this false parse. -// XFAIL: * // RUN: clang-pseudo -grammar=cxx -source=%s --print-forest | FileCheck %s void (*s)(){}; // CHECK-NOT: function-definition -// CHECK: init-declarator := declarator initializer +// CHECK: init-declarator := non-function-declarator initializer // CHECK-NOT: function-definition diff --git a/clang-tools-extra/pseudo/test/cxx/recovery-func-parameters.cpp b/clang-tools-extra/pseudo/test/cxx/recovery-func-parameters.cpp --- a/clang-tools-extra/pseudo/test/cxx/recovery-func-parameters.cpp +++ b/clang-tools-extra/pseudo/test/cxx/recovery-func-parameters.cpp @@ -1,8 +1,8 @@ // RUN: clang-pseudo -grammar=cxx -source=%s --print-forest | FileCheck %s void foo(complete garbage???) {} -// CHECK: translation-unit~function-definition := decl-specifier-seq declarator function-body +// CHECK: translation-unit~function-definition := decl-specifier-seq function-declarator function-body // CHECK-NEXT: ├─decl-specifier-seq~VOID := tok[0] -// CHECK-NEXT: ├─declarator~noptr-declarator := noptr-declarator parameters-and-qualifiers +// CHECK-NEXT: ├─function-declarator~noptr-declarator := noptr-declarator parameters-and-qualifiers // CHECK-NEXT: │ ├─noptr-declarator~IDENTIFIER := tok[1] // CHECK-NEXT: │ └─parameters-and-qualifiers := ( parameter-declaration-clause [recover=Brackets] ) // CHECK-NEXT: │ ├─( := tok[2] diff --git a/clang-tools-extra/pseudo/test/cxx/recovery-init-list.cpp b/clang-tools-extra/pseudo/test/cxx/recovery-init-list.cpp --- a/clang-tools-extra/pseudo/test/cxx/recovery-init-list.cpp +++ b/clang-tools-extra/pseudo/test/cxx/recovery-init-list.cpp @@ -3,7 +3,7 @@ // CHECK: translation-unit~simple-declaration // CHECK-NEXT: ├─decl-specifier-seq~AUTO := tok[0] // CHECK-NEXT: ├─init-declarator-list~init-declarator -// CHECK-NEXT: │ ├─declarator~IDENTIFIER := tok[1] +// CHECK-NEXT: │ ├─non-function-declarator~IDENTIFIER := tok[1] // CHECK-NEXT: │ └─initializer~brace-or-equal-initializer // CHECK-NEXT: │ ├─= := tok[2] // CHECK-NEXT: │ └─initializer-clause~braced-init-list diff --git a/clang-tools-extra/pseudo/test/glr.cpp b/clang-tools-extra/pseudo/test/glr.cpp --- a/clang-tools-extra/pseudo/test/glr.cpp +++ b/clang-tools-extra/pseudo/test/glr.cpp @@ -1,4 +1,4 @@ -// RUN: clang-pseudo -grammar=%cxx-bnf-file -source=%s --print-forest -print-statistics | FileCheck %s +// RUN: clang-pseudo -grammar=cxx -source=%s --print-forest -print-statistics | FileCheck %s void foo() { T* a; // a multiply expression or a pointer declaration?