[clang-tools-extra] d489b38 - [pseudo] Implement a guard to determine function declarator.

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 19 00:49:30 PDT 2022


Author: Haojian Wu
Date: 2022-07-19T09:44:45+02:00
New Revision: d489b3807f096584175c321ce7f20e9dcd49b1da

URL: https://github.com/llvm/llvm-project/commit/d489b3807f096584175c321ce7f20e9dcd49b1da
DIFF: https://github.com/llvm/llvm-project/commit/d489b3807f096584175c321ce7f20e9dcd49b1da.diff

LOG: [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

Added: 
    

Modified: 
    clang-tools-extra/pseudo/lib/cxx/CXX.cpp
    clang-tools-extra/pseudo/lib/cxx/cxx.bnf
    clang-tools-extra/pseudo/test/cxx/declarator-function.cpp
    clang-tools-extra/pseudo/test/cxx/declarator-var.cpp
    clang-tools-extra/pseudo/test/cxx/recovery-func-parameters.cpp
    clang-tools-extra/pseudo/test/cxx/recovery-init-list.cpp
    clang-tools-extra/pseudo/test/glr.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/pseudo/lib/cxx/CXX.cpp b/clang-tools-extra/pseudo/lib/cxx/CXX.cpp
index 9a03422153f06..d6c056a25e365 100644
--- a/clang-tools-extra/pseudo/lib/cxx/CXX.cpp
+++ b/clang-tools-extra/pseudo/lib/cxx/CXX.cpp
@@ -46,6 +46,66 @@ bool guardExport(llvm::ArrayRef<const ForestNode *> RHS,
   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() {
   return {
       {(ExtensionID)Extension::Override, guardOverride},
@@ -53,6 +113,8 @@ llvm::DenseMap<ExtensionID, RuleGuard> buildGuards() {
       {(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
index 08384af9d9399..4e434b1e037cc 100644
--- a/clang-tools-extra/pseudo/lib/cxx/cxx.bnf
+++ b/clang-tools-extra/pseudo/lib/cxx/cxx.bnf
@@ -332,7 +332,7 @@ block-declaration := using-directive
 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 AUTO
 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 @@ designator := [ expression ]
 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
index 59dccf993af47..4d7972807c6db 100644
--- 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
index 057ec55b1c925..5aedd8037513f 100644
--- 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
index d3007c3fb4bc6..0b41f881fa3bf 100644
--- 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
index 283c53d78128d..38216ad964772 100644
--- 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 @@ auto x = { complete garbage };
 // 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
index 0eb19fba2ecc6..24b2ac05f6f1b 100644
--- 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?


        


More information about the cfe-commits mailing list