[pseudo] Implement a guard to determine function declarator.

This eliminates some simple-declaration/function-definition false
parses.

- implement a function to determine whether a declarator ForestNode is a
  function declarator;
- extend the standard declarator to two guarded function-declarator and
  non-function-declarator nonterminals;

Differential Revision: https://reviews.llvm.org/D129222
This commit is contained in:
Haojian Wu 2022-07-15 16:15:31 +02:00
parent 05d424d165
commit d489b3807f
7 changed files with 85 additions and 16 deletions

View File

@ -46,6 +46,66 @@ bool guardExport(llvm::ArrayRef<const ForestNode *> RHS,
return Tokens.tokens()[RHS.front()->startTokenIndex()].text() == "export"; 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<const ForestNode *> RHS,
const TokenStream &Tokens) {
assert(RHS.size() == 1 &&
RHS.front()->symbol() == (SymbolID)(cxx::Symbol::declarator));
return isFunctionDeclarator(RHS.front());
}
bool guardNonFunction(llvm::ArrayRef<const ForestNode *> RHS,
const TokenStream &Tokens) {
assert(RHS.size() == 1 &&
RHS.front()->symbol() == (SymbolID)(cxx::Symbol::declarator));
return !isFunctionDeclarator(RHS.front());
}
llvm::DenseMap<ExtensionID, RuleGuard> buildGuards() { llvm::DenseMap<ExtensionID, RuleGuard> buildGuards() {
return { return {
{(ExtensionID)Extension::Override, guardOverride}, {(ExtensionID)Extension::Override, guardOverride},
@ -53,6 +113,8 @@ llvm::DenseMap<ExtensionID, RuleGuard> buildGuards() {
{(ExtensionID)Extension::Import, guardImport}, {(ExtensionID)Extension::Import, guardImport},
{(ExtensionID)Extension::Export, guardExport}, {(ExtensionID)Extension::Export, guardExport},
{(ExtensionID)Extension::Module, guardModule}, {(ExtensionID)Extension::Module, guardModule},
{(ExtensionID)Extension::FunctionDeclarator, guardFunction},
{(ExtensionID)Extension::NonFunctionDeclarator, guardNonFunction},
}; };
} }

View File

@ -332,7 +332,7 @@ block-declaration := using-directive
block-declaration := static_assert-declaration block-declaration := static_assert-declaration
block-declaration := alias-declaration block-declaration := alias-declaration
block-declaration := opaque-enum-declaration block-declaration := opaque-enum-declaration
nodeclspec-function-declaration := declarator ; nodeclspec-function-declaration := function-declarator ;
alias-declaration := USING IDENTIFIER = defining-type-id ; 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 ;
simple-declaration := decl-specifier-seq ref-qualifier_opt [ identifier-list ] initializer ; simple-declaration := decl-specifier-seq ref-qualifier_opt [ identifier-list ] initializer ;
@ -402,8 +402,19 @@ placeholder-type-specifier := type-constraint_opt AUTO
placeholder-type-specifier := type-constraint_opt DECLTYPE ( AUTO ) placeholder-type-specifier := type-constraint_opt DECLTYPE ( AUTO )
init-declarator-list := init-declarator init-declarator-list := init-declarator
init-declarator-list := init-declarator-list , init-declarator init-declarator-list := init-declarator-list , init-declarator
init-declarator := declarator initializer_opt #! The standard grammar allows:
init-declarator := declarator requires-clause #! 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 := ptr-declarator
declarator := noptr-declarator parameters-and-qualifiers trailing-return-type declarator := noptr-declarator parameters-and-qualifiers trailing-return-type
ptr-declarator := noptr-declarator ptr-declarator := noptr-declarator
@ -472,8 +483,8 @@ designator := [ expression ]
expr-or-braced-init-list := expression expr-or-braced-init-list := expression
expr-or-braced-init-list := braced-init-list expr-or-braced-init-list := braced-init-list
# dcl.fct # dcl.fct
function-definition := decl-specifier-seq_opt declarator virt-specifier-seq_opt function-body function-definition := decl-specifier-seq_opt function-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 requires-clause function-body
function-body := ctor-initializer_opt compound-statement function-body := ctor-initializer_opt compound-statement
function-body := function-try-block function-body := function-try-block
function-body := = DEFAULT ; function-body := = DEFAULT ;

View File

@ -1,11 +1,9 @@
// The standard grammar allows an init-list with any declarator, including // The standard grammar allows an init-list with any declarator, including
// a function declarator. This creates an ambiguity where a function-definition // a function declarator. This creates an ambiguity where a function-definition
// is misparsed as a simple-declaration. // is misparsed as a simple-declaration.
// FIXME: eliminate this false parse.
// XFAIL: *
// RUN: clang-pseudo -grammar=cxx -source=%s --print-forest | FileCheck %s // RUN: clang-pseudo -grammar=cxx -source=%s --print-forest | FileCheck %s
void s(){}; void s(){};
// CHECK-NOT: simple-declaration // CHECK-NOT: simple-declaration
// CHECK: function-definition := decl-specifier-seq declarator // CHECK: function-definition := decl-specifier-seq function-declarator function-body
// function-body CHECK-NOT: simple-declaration // CHECK-NOT: simple-declaration

View File

@ -1,11 +1,9 @@
// The standard grammar allows an function-body to use any declarator, including // The standard grammar allows an function-body to use any declarator, including
// a non-function declarator. This creates an ambiguity where a // a non-function declarator. This creates an ambiguity where a
// simple-declaration is misparsed as a function-definition. // 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 // RUN: clang-pseudo -grammar=cxx -source=%s --print-forest | FileCheck %s
void (*s)(){}; void (*s)(){};
// CHECK-NOT: function-definition // CHECK-NOT: function-definition
// CHECK: init-declarator := declarator initializer // CHECK: init-declarator := non-function-declarator initializer
// CHECK-NOT: function-definition // CHECK-NOT: function-definition

View File

@ -1,8 +1,8 @@
// RUN: clang-pseudo -grammar=cxx -source=%s --print-forest | FileCheck %s // RUN: clang-pseudo -grammar=cxx -source=%s --print-forest | FileCheck %s
void foo(complete garbage???) {} 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: ├─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: │ ├─noptr-declarator~IDENTIFIER := tok[1]
// CHECK-NEXT: │ └─parameters-and-qualifiers := ( parameter-declaration-clause [recover=Brackets] ) // CHECK-NEXT: │ └─parameters-and-qualifiers := ( parameter-declaration-clause [recover=Brackets] )
// CHECK-NEXT: │ ├─( := tok[2] // CHECK-NEXT: │ ├─( := tok[2]

View File

@ -3,7 +3,7 @@ auto x = { complete garbage };
// CHECK: translation-unit~simple-declaration // CHECK: translation-unit~simple-declaration
// CHECK-NEXT: ├─decl-specifier-seq~AUTO := tok[0] // CHECK-NEXT: ├─decl-specifier-seq~AUTO := tok[0]
// CHECK-NEXT: ├─init-declarator-list~init-declarator // 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: │ └─initializer~brace-or-equal-initializer
// CHECK-NEXT: │ ├─= := tok[2] // CHECK-NEXT: │ ├─= := tok[2]
// CHECK-NEXT: │ └─initializer-clause~braced-init-list // CHECK-NEXT: │ └─initializer-clause~braced-init-list

View File

@ -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() { void foo() {
T* a; // a multiply expression or a pointer declaration? T* a; // a multiply expression or a pointer declaration?