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 @@ -34,10 +34,72 @@ return Tokens.tokens()[RHS.front()->startTokenIndex()].text() == "final"; } +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}, {(ExtensionID)Extension::Final, guardFinal}, + {(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 @@ -402,8 +402,10 @@ 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 +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 +474,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,28 @@ // 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 +void s() {}; +// CHECK: ├─declaration-seq~function-definition := decl-specifier-seq function-declarator function-body +// CHECK-NEXT: │ ├─decl-specifier-seq~VOID := tok[0] +// CHECK-NEXT: │ ├─function-declarator~noptr-declarator := noptr-declarator parameters-and-qualifiers +// CHECK-NEXT: │ │ ├─noptr-declarator~IDENTIFIER := tok[1] +// CHECK-NEXT: │ │ └─parameters-and-qualifiers := ( ) +// CHECK-NEXT: │ │ ├─( := tok[2] +// CHECK-NEXT: │ │ └─) := tok[3] +// CHECK-NEXT: │ └─function-body~compound-statement := { } +// CHECK-NEXT: │ ├─{ := tok[4] +// CHECK-NEXT: │ └─} := tok[5] +// CHECK-NEXT: └─declaration~; := tok[6] + +// Should not parse as a function-definition. +int s2 {}; +// CHECK-NEXT: declaration~simple-declaration := decl-specifier-seq init-declarator-list ; +// CHECK-NEXT: ├─decl-specifier-seq~INT := tok[7] +// CHECK-NEXT: ├─init-declarator-list~init-declarator := non-function-declarator initializer +// CHECK-NEXT: │ ├─non-function-declarator~IDENTIFIER := tok[8] +// CHECK-NEXT: │ └─initializer~braced-init-list := { } +// CHECK-NEXT: │ ├─{ := tok[9] +// CHECK-NEXT: │ └─} := tok[10] +// CHECK-NEXT: └─; := tok[11] 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-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?