Repository: glayzzle/php-parser
Pull request #457 (2020-01-02 13:05:46)

Feat arrow functions v2
Implementation for https://github.com/glayzzle/php-parser/issues/346 based on RFC https://wiki.php.net/rfc/arrow_functions_v2



Patch:
diff --git a/src/ast.js b/src/ast.js
index 967a6698..72193183 100644
--- a/src/ast.js
+++ b/src/ast.js
@@ -35,6 +35,7 @@ const Position = require("./ast/position");
  *   - [Error](#error)
  *   - [Expression](#expression)
  *     - [Entry](#entry)
+ *     - [ArrowFunc](#arrowfunc)
  *     - [Closure](#closure)
  *     - [ByRef](#byref)
  *     - [Silent](#silent)
@@ -460,6 +461,7 @@ AST.prototype.checkNodes = function() {
 // Define all AST nodes
 [
   require("./ast/array"),
+  require("./ast/arrowfunc"),
   require("./ast/assign"),
   require("./ast/assignref"),
   require("./ast/bin"),
diff --git a/src/ast/arrowfunc.js b/src/ast/arrowfunc.js
new file mode 100644
index 00000000..edaa78f2
--- /dev/null
+++ b/src/ast/arrowfunc.js
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) 2018 Glayzzle (BSD3 License)
+ * @authors https://github.com/glayzzle/php-parser/graphs/contributors
+ * @url http://glayzzle.com
+ */
+"use strict";
+
+const Expression = require("./expression");
+const KIND = "arrowfunc";
+
+/**
+ * Defines an arrow function (it's like a closure)
+ * @constructor ArrowFunc
+ * @extends {Expression}
+ * @property {Parameter[]} arguments
+ * @property {Identifier} type
+ * @property {Expression} body
+ * @property {boolean} byref
+ * @property {boolean} nullable
+ * @property {boolean} isStatic
+ */
+module.exports = Expression.extends(KIND, function Closure(
+  args,
+  byref,
+  body,
+  type,
+  nullable,
+  isStatic,
+  docs,
+  location
+) {
+  Expression.apply(this, [KIND, docs, location]);
+  this.arguments = args;
+  this.byref = byref;
+  this.body = body;
+  this.type = type;
+  this.nullable = nullable;
+  this.isStatic = isStatic || false;
+});
diff --git a/src/ast/property.js b/src/ast/property.js
index eaee4c93..073ff436 100644
--- a/src/ast/property.js
+++ b/src/ast/property.js
@@ -14,14 +14,20 @@ const KIND = "property";
  * @extends {Statement}
  * @property {string} name
  * @property {Node|null} value
+ * @property {boolean} nullable
+ * @property {Identifier|Array<Identifier>|null} type
  */
 module.exports = Statement.extends(KIND, function Property(
   name,
   value,
+  nullable,
+  type,
   docs,
   location
 ) {
   Statement.apply(this, [KIND, docs, location]);
   this.name = name;
   this.value = value;
+  this.nullable = nullable;
+  this.type = type;
 });
diff --git a/src/index.js b/src/index.js
index a5a246b0..bfc9db8b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -83,22 +83,21 @@ const engine = function(options) {
       if (options.parser.php7 === false) {
         options.parser.php73 = false;
         options.parser.php74 = false;
-      } else {
-        options.parser.php7 = true;
-        options.parser.php73 = true;
-        options.parser.php74 = true;
       }
       if (options.parser.php73 === true) {
         options.parser.php7 = true;
       }
       if (options.parser.php74 === true) {
         options.parser.php7 = true;
+        options.parser.php73 = true;
       }
-      options.lexer.php7 = options.parser.php7;
-      options.lexer.php73 = options.parser.php73;
-      options.lexer.php74 = options.parser.php74;
     }
     combine(options, this);
+
+    // same version flags based on parser options
+    this.lexer.php7 = this.parser.php7;
+    this.lexer.php73 = this.parser.php73;
+    this.lexer.php74 = this.parser.php74;
   }
 };
 
diff --git a/src/lexer.js b/src/lexer.js
index a845550b..df95331f 100644
--- a/src/lexer.js
+++ b/src/lexer.js
@@ -146,6 +146,11 @@ lexer.prototype.setInput = function(input) {
     last_column: 0
   };
   this.tokens = [];
+  if (this.php74) {
+    this.keywords.fn = this.tok.T_FN;
+  } else {
+    delete this.keywords.fn;
+  }
   this.done = this.offset >= this.size;
   if (!this.all_tokens && this.mode_eval) {
     this.conditionStack = ["INITIAL"];
diff --git a/src/parser.js b/src/parser.js
index 4d5d64be..0ab6dbf1 100644
--- a/src/parser.js
+++ b/src/parser.js
@@ -43,6 +43,7 @@ const parser = function(lexer, ast) {
     return [item, null];
   };
   this.entries = {
+    // reserved_non_modifiers
     IDENTIFIER: new Map(
       [
         this.tok.T_ABSTRACT,
@@ -77,9 +78,10 @@ const parser = function(lexer, ast) {
         this.tok.T_FILE,
         this.tok.T_FINAL,
         this.tok.T_FINALLY,
-        this.tok.T_FUNC_C,
+        this.tok.T_FN,
         this.tok.T_FOR,
         this.tok.T_FOREACH,
+        this.tok.T_FUNC_C,
         this.tok.T_FUNCTION,
         this.tok.T_GLOBAL,
         this.tok.T_GOTO,
@@ -209,6 +211,7 @@ const parser = function(lexer, ast) {
         this.tok.T_YIELD,
         this.tok.T_STATIC,
         this.tok.T_FUNCTION,
+        this.tok.T_FN,
         // using VARIABLES :
         this.tok.T_VARIABLE,
         "$",
diff --git a/src/parser/class.js b/src/parser/class.js
index d36ff70a..cf792d35 100644
--- a/src/parser/class.js
+++ b/src/parser/class.js
@@ -97,15 +97,25 @@ module.exports = {
         flags[1] = 0; // non static var
       }
 
-      if (this.token === this.tok.T_VARIABLE) {
+      if (this.token === this.tok.T_FUNCTION) {
+        // reads a function
+        result.push(this.read_function(false, flags));
+      } else if (
+        this.token === this.tok.T_VARIABLE ||
+        // support https://wiki.php.net/rfc/typed_properties_v2
+        (this.php74 &&
+          (this.token === "?" ||
+            this.token === this.tok.T_CALLABLE ||
+            this.token === this.tok.T_ARRAY ||
+            this.token === this.tok.T_NS_SEPARATOR ||
+            this.token === this.tok.T_STRING ||
+            this.token === this.tok.T_NAMESPACE))
+      ) {
         // reads a variable
         const variables = this.read_variable_list(flags);
         this.expect(";");
         this.next();
         result = result.concat(variables);
-      } else if (this.token === this.tok.T_FUNCTION) {
-        // reads a function
-        result.push(this.read_function(false, flags));
       } else {
         // raise an error
         this.error([
@@ -140,19 +150,20 @@ module.exports = {
        */
       function read_variable_declaration() {
         const result = this.node("property");
+        const [nullable, type] = this.read_optional_type();
         this.expect(this.tok.T_VARIABLE);
         let propName = this.node("identifier");
         const name = this.text().substring(1); // ignore $
         this.next();
         propName = propName(name);
         if (this.token === ";" || this.token === ",") {
-          return result(propName, null);
+          return result(propName, null, nullable, type);
         } else if (this.token === "=") {
           // https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L815
-          return result(propName, this.next().read_expr());
+          return result(propName, this.next().read_expr(), nullable, type);
         } else {
           this.expect([",", ";", "="]);
-          return result(propName, null);
+          return result(propName, null, nullable, type);
         }
       },
       ","
@@ -268,6 +279,60 @@ module.exports = {
     if (result[2] == -1) result[2] = 0;
     return result;
   },
+
+  /**
+   * optional_type:
+   *	  /- empty -/	{ $$ = NULL; }
+   *   |	type_expr	{ $$ = $1; }
+   * ;
+   *
+   * type_expr:
+   *		type		{ $$ = $1; }
+   *	|	'?' type	{ $$ = $2; $$->attr |= ZEND_TYPE_NULLABLE; }
+   *	|	union_type	{ $$ = $1; }
+   * ;
+   *
+   * type:
+   * 		T_ARRAY		{ $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); }
+   * 	|	T_CALLABLE	{ $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); }
+   * 	|	name		{ $$ = $1; }
+   * ;
+   *
+   * union_type:
+   * 		type '|' type       { $$ = zend_ast_create_list(2, ZEND_AST_TYPE_UNION, $1, $3); }
+   * 	|	union_type '|' type { $$ = zend_ast_list_add($1, $3); }
+   * ;
+   */
+  read_optional_type: function() {
+    let nullable = false;
+    if (this.token === "?") {
+      nullable = true;
+      this.next();
+    }
+    let type = this.read_type();
+    if (nullable && !type) {
+      this.raiseError(
+        "Expecting a type definition combined with nullable operator"
+      );
+    }
+    if (!nullable && !type) {
+      return [false, null];
+    }
+    if (this.token === "|") {
+      type = [type];
+      do {
+        this.next();
+        const variant = this.read_type();
+        if (!variant) {
+          this.raiseError("Expecting a type definition");
+          break;
+        }
+        type.push(variant);
+      } while (this.token === "|");
+    }
+    return [nullable, type];
+  },
+
   /**
    * reading an interface
    * ```ebnf
diff --git a/src/parser/expr.js b/src/parser/expr.js
index eab57084..43baa9e7 100644
--- a/src/parser/expr.js
+++ b/src/parser/expr.js
@@ -377,14 +377,19 @@ module.exports = {
         expr = this.next().read_expr();
         return result(expr);
 
+      case this.tok.T_FN:
       case this.tok.T_FUNCTION:
-        return this.read_function(true);
+        return this.read_inline_function();
 
       case this.tok.T_STATIC: {
         const backup = [this.token, this.lexer.getState()];
-        if (this.next().token === this.tok.T_FUNCTION) {
+        this.next();
+        if (
+          this.token === this.tok.T_FUNCTION ||
+          (this.php74 && this.token === this.tok.T_FN)
+        ) {
           // handles static function
-          return this.read_function(true, [0, 1, 0]);
+          return this.read_inline_function([0, 1, 0]);
         } else {
           // rollback
           this.lexer.tokens.push(backup);
@@ -515,6 +520,60 @@ module.exports = {
     return expr;
   },
 
+  /**
+   *
+   * inline_function:
+   * 		function returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars return_type
+   * 		backup_fn_flags '{' inner_statement_list '}' backup_fn_flags
+   * 			{ $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2 | $13, $1, $3,
+   * 				  zend_string_init("{closure}", sizeof("{closure}") - 1, 0),
+   * 				  $5, $7, $11, $8); CG(extra_fn_flags) = $9; }
+   * 	|	fn returns_ref '(' parameter_list ')' return_type backup_doc_comment T_DOUBLE_ARROW backup_fn_flags backup_lex_pos expr backup_fn_flags
+   * 			{ $$ = zend_ast_create_decl(ZEND_AST_ARROW_FUNC, $2 | $12, $1, $7,
+   * 				  zend_string_init("{closure}", sizeof("{closure}") - 1, 0), $4, NULL,
+   * 				  zend_ast_create(ZEND_AST_RETURN, $11), $6);
+   * 				  ((zend_ast_decl *) $$)->lex_pos = $10;
+   * 				  CG(extra_fn_flags) = $9; }   *
+   */
+  read_inline_function: function(flags) {
+    if (this.token === this.tok.T_FUNCTION) {
+      return this.read_function(true, flags);
+    }
+    // introduced in PHP 7.4
+    if (!this.php74) {
+      this.raiseError("Arrow Functions are not allowed");
+    }
+    // as an arrowfunc
+    const node = this.node("arrowfunc");
+    // eat T_FN
+    if (this.expect(this.tok.T_FN)) this.next();
+    // check the &
+    const isRef = this.is_reference();
+    // ...
+    if (this.expect("(")) this.next();
+    const params = this.read_parameter_list();
+    if (this.expect(")")) this.next();
+    let nullable = false;
+    let returnType = null;
+    if (this.token === ":") {
+      if (this.next().token === "?") {
+        nullable = true;
+        this.next();
+      }
+      returnType = this.read_type();
+    }
+    if (this.expect(this.tok.T_DOUBLE_ARROW)) this.next();
+    const body = this.read_expr();
+    return node(
+      params,
+      isRef,
+      body,
+      returnType,
+      nullable,
+      flags ? true : false
+    );
+  },
+
   /**
    * ```ebnf
    *    new_expr ::= T_NEW (namespace_name function_argument_list) | (T_CLASS ... class declaration)
diff --git a/src/parser/function.js b/src/parser/function.js
index 6ba9c67c..59625fa3 100644
--- a/src/parser/function.js
+++ b/src/parser/function.js
@@ -300,14 +300,20 @@ module.exports = {
         // rollback a classic namespace
         this.lexer.tokens.push(backup);
         this.next();
+        // fix : destroy not consumed node (release comments)
+        result.destroy();
         return this.read_namespace_name();
       }
     } else if (
       this.token === this.tok.T_NAMESPACE ||
       this.token === this.tok.T_NS_SEPARATOR
     ) {
+      // fix : destroy not consumed node (release comments)
+      result.destroy();
       return this.read_namespace_name();
     }
+    // fix : destroy not consumed node (release comments)
+    result.destroy();
     return null;
   }
 };
diff --git a/src/tokens.js b/src/tokens.js
index 4471284f..7812dfdb 100644
--- a/src/tokens.js
+++ b/src/tokens.js
@@ -144,7 +144,8 @@ module.exports = {
     231: "T_POW",
     232: "T_POW_EQUAL",
     233: "T_SPACESHIP",
-    234: "T_COALESCE_EQUAL"
+    234: "T_COALESCE_EQUAL",
+    235: "T_FN"
   },
   names: {
     T_HALT_COMPILER: 101,
@@ -280,6 +281,7 @@ module.exports = {
     T_POW: 231,
     T_POW_EQUAL: 232,
     T_SPACESHIP: 233,
-    T_COALESCE_EQUAL: 234
+    T_COALESCE_EQUAL: 234,
+    T_FN: 235
   }
 };


Test patch:
diff --git a/test/debug.js b/test/debug.js
index 898fdbad..0deed9be 100644
--- a/test/debug.js
+++ b/test/debug.js
@@ -17,11 +17,16 @@
 const util = require('util');
 const parser = require("../src/index");
 const ast = parser.parseCode(`
-<?php$var = 1; ?>
+<?php
+function iter() {
+  yield 'ator' => $foo;
+  yield from iter(50);
+}
+$a = fn($n) => $n * $factor;
 `, { 
     parser: {
       debug: true,
-      php73: true
+      //extractDoc: true
     },
     ast: {
       withPositions: true,
diff --git a/test/snapshot/__snapshots__/acid.test.js.snap b/test/snapshot/__snapshots__/acid.test.js.snap
index 407970ab..88d61c55 100644
--- a/test/snapshot/__snapshots__/acid.test.js.snap
+++ b/test/snapshot/__snapshots__/acid.test.js.snap
@@ -880,6 +880,8 @@ Program {
                     },
                     "name": "dwarf",
                   },
+                  "nullable": false,
+                  "type": null,
                   "value": Array {
                     "items": Array [
                       Entry {
diff --git a/test/snapshot/__snapshots__/arrowfunc.test.js.snap b/test/snapshot/__snapshots__/arrowfunc.test.js.snap
new file mode 100644
index 00000000..b4137eeb
--- /dev/null
+++ b/test/snapshot/__snapshots__/arrowfunc.test.js.snap
@@ -0,0 +1,434 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`arrow function argument 1`] = `
+Program {
+  "children": Array [
+    ExpressionStatement {
+      "expression": Assign {
+        "kind": "assign",
+        "left": Variable {
+          "curly": false,
+          "kind": "variable",
+          "name": "var",
+        },
+        "operator": "=",
+        "right": Closure {
+          "arguments": Array [
+            Parameter {
+              "byref": false,
+              "kind": "parameter",
+              "name": Identifier {
+                "kind": "identifier",
+                "name": "arg",
+              },
+              "nullable": false,
+              "type": null,
+              "value": null,
+              "variadic": false,
+            },
+          ],
+          "body": String {
+            "isDoubleQuote": true,
+            "kind": "string",
+            "raw": "\\"something\\"",
+            "unicode": false,
+            "value": "something",
+          },
+          "byref": false,
+          "isStatic": false,
+          "kind": "arrowfunc",
+          "nullable": false,
+          "type": null,
+        },
+      },
+      "kind": "expressionstatement",
+    },
+  ],
+  "errors": Array [],
+  "kind": "program",
+}
+`;
+
+exports[`arrow function argument by ref 1`] = `
+Program {
+  "children": Array [
+    ExpressionStatement {
+      "expression": Assign {
+        "kind": "assign",
+        "left": Variable {
+          "curly": false,
+          "kind": "variable",
+          "name": "var",
+        },
+        "operator": "=",
+        "right": Closure {
+          "arguments": Array [
+            Parameter {
+              "byref": true,
+              "kind": "parameter",
+              "name": Identifier {
+                "kind": "identifier",
+                "name": "arg",
+              },
+              "nullable": false,
+              "type": null,
+              "value": null,
+              "variadic": false,
+            },
+          ],
+          "body": String {
+            "isDoubleQuote": true,
+            "kind": "string",
+            "raw": "\\"something\\"",
+            "unicode": false,
+            "value": "something",
+          },
+          "byref": false,
+          "isStatic": false,
+          "kind": "arrowfunc",
+          "nullable": false,
+          "type": null,
+        },
+      },
+      "kind": "expressionstatement",
+    },
+  ],
+  "errors": Array [],
+  "kind": "program",
+}
+`;
+
+exports[`arrow function arguments 1`] = `
+Program {
+  "children": Array [
+    ExpressionStatement {
+      "expression": Assign {
+        "kind": "assign",
+        "left": Variable {
+          "curly": false,
+          "kind": "variable",
+          "name": "var",
+        },
+        "operator": "=",
+        "right": Closure {
+          "arguments": Array [
+            Parameter {
+              "byref": false,
+              "kind": "parameter",
+              "name": Identifier {
+                "kind": "identifier",
+                "name": "arg",
+              },
+              "nullable": false,
+              "type": null,
+              "value": null,
+              "variadic": false,
+            },
+            Parameter {
+              "byref": false,
+              "kind": "parameter",
+              "name": Identifier {
+                "kind": "identifier",
+                "name": "arg",
+              },
+              "nullable": false,
+              "type": null,
+              "value": null,
+              "variadic": false,
+            },
+            Parameter {
+              "byref": false,
+              "kind": "parameter",
+              "name": Identifier {
+                "kind": "identifier",
+                "name": "arg",
+              },
+              "nullable": false,
+              "type": null,
+              "value": null,
+              "variadic": false,
+            },
+          ],
+          "body": String {
+            "isDoubleQuote": true,
+            "kind": "string",
+            "raw": "\\"something\\"",
+            "unicode": false,
+            "value": "something",
+          },
+          "byref": false,
+          "isStatic": false,
+          "kind": "arrowfunc",
+          "nullable": false,
+          "type": null,
+        },
+      },
+      "kind": "expressionstatement",
+    },
+  ],
+  "errors": Array [],
+  "kind": "program",
+}
+`;
+
+exports[`arrow function error / empty not allowed 1`] = `
+Program {
+  "children": Array [
+    ExpressionStatement {
+      "expression": Assign {
+        "kind": "assign",
+        "left": Variable {
+          "curly": false,
+          "kind": "variable",
+          "name": "var",
+        },
+        "operator": "=",
+        "right": Closure {
+          "arguments": Array [],
+          "body": undefined,
+          "byref": false,
+          "isStatic": false,
+          "kind": "arrowfunc",
+          "nullable": false,
+          "type": null,
+        },
+      },
+      "kind": "expressionstatement",
+    },
+  ],
+  "errors": Array [
+    Error {
+      "expected": "EXPR",
+      "kind": "error",
+      "line": 1,
+      "message": "Parse Error : syntax error, unexpected ';' on line 1",
+      "token": "';'",
+    },
+  ],
+  "kind": "program",
+}
+`;
+
+exports[`arrow function error / fn fails on php7.4 1`] = `
+Program {
+  "children": Array [
+    _Function {
+      "arguments": Array [
+        Parameter {
+          "byref": false,
+          "kind": "parameter",
+          "name": Identifier {
+            "kind": "identifier",
+            "name": "arg",
+          },
+          "nullable": false,
+          "type": null,
+          "value": null,
+          "variadic": false,
+        },
+      ],
+      "body": Block {
+        "children": Array [
+          Return {
+            "expr": Variable {
+              "curly": false,
+              "kind": "variable",
+              "name": "arg",
+            },
+            "kind": "return",
+          },
+        ],
+        "kind": "block",
+      },
+      "byref": false,
+      "kind": "function",
+      "name": Identifier {
+        "kind": "identifier",
+        "name": false,
+      },
+      "nullable": false,
+      "type": null,
+    },
+  ],
+  "errors": Array [
+    Error {
+      "expected": "(",
+      "kind": "error",
+      "line": 1,
+      "message": "Parse Error : syntax error, unexpected 'fn' (T_FN), expecting '(' on line 1",
+      "token": "'fn' (T_FN)",
+    },
+  ],
+  "kind": "program",
+}
+`;
+
+exports[`arrow function error / fn passes on php7.3 1`] = `
+Program {
+  "children": Array [
+    _Function {
+      "arguments": Array [
+        Parameter {
+          "byref": false,
+          "kind": "parameter",
+          "name": Identifier {
+            "kind": "identifier",
+            "name": "arg",
+          },
+          "nullable": false,
+          "type": null,
+          "value": null,
+          "variadic": false,
+        },
+      ],
+      "body": Block {
+        "children": Array [
+          Return {
+            "expr": Variable {
+              "curly": false,
+              "kind": "variable",
+              "name": "arg",
+            },
+            "kind": "return",
+          },
+        ],
+        "kind": "block",
+      },
+      "byref": false,
+      "kind": "function",
+      "name": Identifier {
+        "kind": "identifier",
+        "name": "fn",
+      },
+      "nullable": false,
+      "type": null,
+    },
+  ],
+  "errors": Array [],
+  "kind": "program",
+}
+`;
+
+exports[`arrow function inside call 1`] = `
+Program {
+  "children": Array [
+    ExpressionStatement {
+      "expression": Call {
+        "arguments": Array [
+          Closure {
+            "arguments": Array [
+              Parameter {
+                "byref": false,
+                "kind": "parameter",
+                "name": Identifier {
+                  "kind": "identifier",
+                  "name": "arg",
+                },
+                "nullable": false,
+                "type": null,
+                "value": null,
+                "variadic": false,
+              },
+            ],
+            "body": Variable {
+              "curly": false,
+              "kind": "variable",
+              "name": "arg",
+            },
+            "byref": false,
+            "isStatic": false,
+            "kind": "arrowfunc",
+            "nullable": false,
+            "type": null,
+          },
+        ],
+        "kind": "call",
+        "what": ClassReference {
+          "kind": "classreference",
+          "name": "call",
+          "resolution": "uqn",
+        },
+      },
+      "kind": "expressionstatement",
+    },
+  ],
+  "errors": Array [],
+  "kind": "program",
+}
+`;
+
+exports[`arrow function return type 1`] = `
+Program {
+  "children": Array [
+    ExpressionStatement {
+      "expression": Assign {
+        "kind": "assign",
+        "left": Variable {
+          "curly": false,
+          "kind": "variable",
+          "name": "var",
+        },
+        "operator": "=",
+        "right": Closure {
+          "arguments": Array [],
+          "body": String {
+            "isDoubleQuote": true,
+            "kind": "string",
+            "raw": "\\"something\\"",
+            "unicode": false,
+            "value": "something",
+          },
+          "byref": false,
+          "isStatic": false,
+          "kind": "arrowfunc",
+          "nullable": true,
+          "type": TypeReference {
+            "kind": "typereference",
+            "name": "string",
+            "raw": "string",
+          },
+        },
+      },
+      "kind": "expressionstatement",
+    },
+  ],
+  "errors": Array [],
+  "kind": "program",
+}
+`;
+
+exports[`arrow function simple 1`] = `
+Program {
+  "children": Array [
+    ExpressionStatement {
+      "expression": Assign {
+        "kind": "assign",
+        "left": Variable {
+          "curly": false,
+          "kind": "variable",
+          "name": "var",
+        },
+        "operator": "=",
+        "right": Closure {
+          "arguments": Array [],
+          "body": String {
+            "isDoubleQuote": true,
+            "kind": "string",
+            "raw": "\\"something\\"",
+            "unicode": false,
+            "value": "something",
+          },
+          "byref": false,
+          "isStatic": false,
+          "kind": "arrowfunc",
+          "nullable": false,
+          "type": null,
+        },
+      },
+      "kind": "expressionstatement",
+    },
+  ],
+  "errors": Array [],
+  "kind": "program",
+}
+`;
diff --git a/test/snapshot/__snapshots__/call.test.js.snap b/test/snapshot/__snapshots__/call.test.js.snap
index 3985172b..88e46cd9 100644
--- a/test/snapshot/__snapshots__/call.test.js.snap
+++ b/test/snapshot/__snapshots__/call.test.js.snap
@@ -837,7 +837,7 @@ Program {
           "kind": "method",
           "name": Identifier {
             "kind": "identifier",
-            "name": "fn",
+            "name": "foo",
           },
           "nullable": false,
           "type": null,
@@ -961,7 +961,7 @@ Program {
           "kind": "method",
           "name": Identifier {
             "kind": "identifier",
-            "name": "fn",
+            "name": "foo",
           },
           "nullable": false,
           "type": null,
@@ -1070,7 +1070,7 @@ Program {
           "kind": "method",
           "name": Identifier {
             "kind": "identifier",
-            "name": "fn",
+            "name": "foo",
           },
           "nullable": false,
           "type": null,
diff --git a/test/snapshot/__snapshots__/class.test.js.snap b/test/snapshot/__snapshots__/class.test.js.snap
index 48797f09..f84f93af 100644
--- a/test/snapshot/__snapshots__/class.test.js.snap
+++ b/test/snapshot/__snapshots__/class.test.js.snap
@@ -69,6 +69,8 @@ Program {
                 "kind": "identifier",
                 "name": "var",
               },
+              "nullable": false,
+              "type": null,
               "value": Boolean {
                 "kind": "boolean",
                 "raw": "true",
@@ -427,6 +429,81 @@ Program {
 }
 `;
 
+exports[`Test classes Implement typed_properties_v2 / php74 1`] = `
+Program {
+  "children": Array [
+    Class {
+      "body": Array [
+        PropertyStatement {
+          "isStatic": false,
+          "kind": "propertystatement",
+          "properties": Array [
+            Property {
+              "kind": "property",
+              "name": Identifier {
+                "kind": "identifier",
+                "name": "prop",
+              },
+              "nullable": true,
+              "type": TypeReference {
+                "kind": "typereference",
+                "name": "int",
+                "raw": "int",
+              },
+              "value": NullKeyword {
+                "kind": "nullkeyword",
+                "raw": "null",
+              },
+            },
+          ],
+          "visibility": "public",
+        },
+        PropertyStatement {
+          "isStatic": true,
+          "kind": "propertystatement",
+          "properties": Array [
+            Property {
+              "kind": "property",
+              "name": Identifier {
+                "kind": "identifier",
+                "name": "y",
+              },
+              "nullable": false,
+              "type": Array [
+                TypeReference {
+                  "kind": "typereference",
+                  "name": "float",
+                  "raw": "float",
+                },
+                TypeReference {
+                  "kind": "typereference",
+                  "name": "string",
+                  "raw": "string",
+                },
+              ],
+              "value": null,
+            },
+          ],
+          "visibility": "protected",
+        },
+      ],
+      "extends": null,
+      "implements": null,
+      "isAbstract": false,
+      "isAnonymous": false,
+      "isFinal": false,
+      "kind": "class",
+      "name": Identifier {
+        "kind": "identifier",
+        "name": "Test",
+      },
+    },
+  ],
+  "errors": Array [],
+  "kind": "program",
+}
+`;
+
 exports[`Test classes Test js properties 1`] = `
 Program {
   "children": Array [
@@ -442,6 +519,8 @@ Program {
                 "kind": "identifier",
                 "name": "constructor",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
@@ -694,6 +773,8 @@ Program {
                 "kind": "identifier",
                 "name": "var",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
@@ -851,6 +932,8 @@ Program {
                 "kind": "identifier",
                 "name": "foo",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
@@ -866,6 +949,8 @@ Program {
                 "kind": "identifier",
                 "name": "bar",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
diff --git a/test/snapshot/__snapshots__/classreference.test.js.snap b/test/snapshot/__snapshots__/classreference.test.js.snap
index 42e740c8..c82bb071 100644
--- a/test/snapshot/__snapshots__/classreference.test.js.snap
+++ b/test/snapshot/__snapshots__/classreference.test.js.snap
@@ -30,7 +30,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -71,7 +71,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
diff --git a/test/snapshot/__snapshots__/comment.test.js.snap b/test/snapshot/__snapshots__/comment.test.js.snap
index f1bc08a2..d9855384 100644
--- a/test/snapshot/__snapshots__/comment.test.js.snap
+++ b/test/snapshot/__snapshots__/comment.test.js.snap
@@ -1695,6 +1695,8 @@ Program {
                 "kind": "identifier",
                 "name": "test",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
             Property {
@@ -1703,6 +1705,8 @@ Program {
                 "kind": "identifier",
                 "name": "toto",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
@@ -1731,6 +1735,8 @@ Program {
                 "kind": "identifier",
                 "name": "foo",
               },
+              "nullable": false,
+              "type": null,
               "value": Number {
                 "kind": "number",
                 "value": "123",
diff --git a/test/snapshot/__snapshots__/graceful.test.js.snap b/test/snapshot/__snapshots__/graceful.test.js.snap
index d6ae2966..0973aee0 100644
--- a/test/snapshot/__snapshots__/graceful.test.js.snap
+++ b/test/snapshot/__snapshots__/graceful.test.js.snap
@@ -292,21 +292,25 @@ Program {
   "children": Array [
     Class {
       "body": Array [
-        ClassConstant {
-          "constants": Array [
-            Constant {
-              "kind": "constant",
+        PropertyStatement {
+          "isStatic": false,
+          "kind": "propertystatement",
+          "properties": Array [
+            Property {
+              "kind": "property",
               "name": Identifier {
                 "kind": "identifier",
-                "name": "A",
+                "name": "onst",
               },
-              "value": Number {
-                "kind": "number",
-                "value": "1",
+              "nullable": false,
+              "type": ClassReference {
+                "kind": "classreference",
+                "name": "foo",
+                "resolution": "uqn",
               },
+              "value": null,
             },
           ],
-          "kind": "classconstant",
           "visibility": "",
         },
       ],
@@ -323,6 +327,31 @@ Program {
     },
   ],
   "errors": Array [
+    Error {
+      "expected": 222,
+      "kind": "error",
+      "line": 1,
+      "message": "Parse Error : syntax error, unexpected 'const' (T_CONST), expecting T_VARIABLE on line 1",
+      "token": "'const' (T_CONST)",
+    },
+    Error {
+      "expected": Array [
+        ",",
+        ";",
+        "=",
+      ],
+      "kind": "error",
+      "line": 1,
+      "message": "Parse Error : syntax error, unexpected 'A' (T_STRING) on line 1",
+      "token": "'A' (T_STRING)",
+    },
+    Error {
+      "expected": ";",
+      "kind": "error",
+      "line": 1,
+      "message": "Parse Error : syntax error, unexpected 'A' (T_STRING), expecting ';' on line 1",
+      "token": "'A' (T_STRING)",
+    },
     Error {
       "expected": Array [
         198,
@@ -331,15 +360,19 @@ Program {
       ],
       "kind": "error",
       "line": 1,
-      "message": "Parse Error : syntax error, unexpected 'foo' (T_STRING) on line 1",
-      "token": "'foo' (T_STRING)",
+      "message": "Parse Error : syntax error, unexpected '=' on line 1",
+      "token": "'='",
     },
     Error {
-      "expected": ";",
+      "expected": Array [
+        198,
+        222,
+        182,
+      ],
       "kind": "error",
       "line": 1,
-      "message": "Parse Error : syntax error, expecting ';' on line 1",
-      "token": "the end of file (EOF)",
+      "message": "Parse Error : syntax error, unexpected '1' (T_LNUMBER) on line 1",
+      "token": "'1' (T_LNUMBER)",
     },
     Error {
       "expected": "}",
@@ -789,7 +822,29 @@ exports[`Test graceful mode to suppress errors trait 1`] = `
 Program {
   "children": Array [
     Trait {
-      "body": Array [],
+      "body": Array [
+        PropertyStatement {
+          "isStatic": false,
+          "kind": "propertystatement",
+          "properties": Array [
+            Property {
+              "kind": "property",
+              "name": Identifier {
+                "kind": "identifier",
+                "name": "mplement",
+              },
+              "nullable": false,
+              "type": ClassReference {
+                "kind": "classreference",
+                "name": "bar",
+                "resolution": "uqn",
+              },
+              "value": null,
+            },
+          ],
+          "visibility": "",
+        },
+      ],
       "kind": "trait",
       "name": Identifier {
         "kind": "identifier",
@@ -806,36 +861,28 @@ Program {
       "token": "'extends' (T_EXTENDS)",
     },
     Error {
-      "expected": Array [
-        198,
-        222,
-        182,
-      ],
+      "expected": 222,
       "kind": "error",
       "line": 1,
-      "message": "Parse Error : syntax error, unexpected 'bar' (T_STRING) on line 1",
-      "token": "'bar' (T_STRING)",
+      "message": "Parse Error : syntax error, unexpected 'implement' (T_STRING), expecting T_VARIABLE on line 1",
+      "token": "'implement' (T_STRING)",
     },
     Error {
       "expected": Array [
-        198,
-        222,
-        182,
+        ",",
+        ";",
+        "=",
       ],
       "kind": "error",
       "line": 1,
-      "message": "Parse Error : syntax error, unexpected 'implement' (T_STRING) on line 1",
-      "token": "'implement' (T_STRING)",
+      "message": "Parse Error : syntax error, unexpected 'baz' (T_STRING) on line 1",
+      "token": "'baz' (T_STRING)",
     },
     Error {
-      "expected": Array [
-        198,
-        222,
-        182,
-      ],
+      "expected": ";",
       "kind": "error",
       "line": 1,
-      "message": "Parse Error : syntax error, unexpected 'baz' (T_STRING) on line 1",
+      "message": "Parse Error : syntax error, unexpected 'baz' (T_STRING), expecting ';' on line 1",
       "token": "'baz' (T_STRING)",
     },
     Error {
diff --git a/test/snapshot/__snapshots__/heredoc.test.js.snap b/test/snapshot/__snapshots__/heredoc.test.js.snap
index 615c5ca5..6cf24428 100644
--- a/test/snapshot/__snapshots__/heredoc.test.js.snap
+++ b/test/snapshot/__snapshots__/heredoc.test.js.snap
@@ -467,6 +467,8 @@ FOOBAR",
                 "kind": "identifier",
                 "name": "baz",
               },
+              "nullable": false,
+              "type": null,
               "value": Encapsed {
                 "kind": "encapsed",
                 "label": "FOOBAR",
diff --git a/test/snapshot/__snapshots__/location.test.js.snap b/test/snapshot/__snapshots__/location.test.js.snap
index f0b6e4c5..579212b9 100644
--- a/test/snapshot/__snapshots__/location.test.js.snap
+++ b/test/snapshot/__snapshots__/location.test.js.snap
@@ -855,30 +855,30 @@ Program {
             "kind": "class",
             "loc": Location {
               "end": Position {
-                "column": 37,
+                "column": 38,
                 "line": 1,
-                "offset": 37,
+                "offset": 38,
               },
               "source": "abstract class Foo {}",
               "start": Position {
-                "column": 16,
+                "column": 17,
                 "line": 1,
-                "offset": 16,
+                "offset": 17,
               },
             },
             "name": Identifier {
               "kind": "identifier",
               "loc": Location {
                 "end": Position {
-                  "column": 34,
+                  "column": 35,
                   "line": 1,
-                  "offset": 34,
+                  "offset": 35,
                 },
                 "source": "Foo",
                 "start": Position {
-                  "column": 31,
+                  "column": 32,
                   "line": 1,
-                  "offset": 31,
+                  "offset": 32,
                 },
               },
               "name": "Foo",
@@ -888,15 +888,15 @@ Program {
         "kind": "block",
         "loc": Location {
           "end": Position {
-            "column": 39,
+            "column": 40,
             "line": 1,
-            "offset": 39,
+            "offset": 40,
           },
           "source": "{ abstract class Foo {} }",
           "start": Position {
-            "column": 14,
+            "column": 15,
             "line": 1,
-            "offset": 14,
+            "offset": 15,
           },
         },
       },
@@ -904,11 +904,11 @@ Program {
       "kind": "function",
       "loc": Location {
         "end": Position {
-          "column": 39,
+          "column": 40,
           "line": 1,
-          "offset": 39,
+          "offset": 40,
         },
-        "source": "function fn()",
+        "source": "function foo()",
         "start": Position {
           "column": 0,
           "line": 1,
@@ -919,18 +919,18 @@ Program {
         "kind": "identifier",
         "loc": Location {
           "end": Position {
-            "column": 11,
+            "column": 12,
             "line": 1,
-            "offset": 11,
+            "offset": 12,
           },
-          "source": "fn",
+          "source": "foo",
           "start": Position {
             "column": 9,
             "line": 1,
             "offset": 9,
           },
         },
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -940,11 +940,11 @@ Program {
   "kind": "program",
   "loc": Location {
     "end": Position {
-      "column": 39,
+      "column": 40,
       "line": 1,
-      "offset": 39,
+      "offset": 40,
     },
-    "source": "function fn() { abstract class Foo {} }",
+    "source": "function foo() { abstract class Foo {} }",
     "start": Position {
       "column": 0,
       "line": 1,
@@ -2532,30 +2532,30 @@ Program {
             "kind": "class",
             "loc": Location {
               "end": Position {
-                "column": 28,
+                "column": 29,
                 "line": 1,
-                "offset": 28,
+                "offset": 29,
               },
               "source": "class Foo {}",
               "start": Position {
-                "column": 16,
+                "column": 17,
                 "line": 1,
-                "offset": 16,
+                "offset": 17,
               },
             },
             "name": Identifier {
               "kind": "identifier",
               "loc": Location {
                 "end": Position {
-                  "column": 25,
+                  "column": 26,
                   "line": 1,
-                  "offset": 25,
+                  "offset": 26,
                 },
                 "source": "Foo",
                 "start": Position {
-                  "column": 22,
+                  "column": 23,
                   "line": 1,
-                  "offset": 22,
+                  "offset": 23,
                 },
               },
               "name": "Foo",
@@ -2565,15 +2565,15 @@ Program {
         "kind": "block",
         "loc": Location {
           "end": Position {
-            "column": 30,
+            "column": 31,
             "line": 1,
-            "offset": 30,
+            "offset": 31,
           },
           "source": "{ class Foo {} }",
           "start": Position {
-            "column": 14,
+            "column": 15,
             "line": 1,
-            "offset": 14,
+            "offset": 15,
           },
         },
       },
@@ -2581,11 +2581,11 @@ Program {
       "kind": "function",
       "loc": Location {
         "end": Position {
-          "column": 30,
+          "column": 31,
           "line": 1,
-          "offset": 30,
+          "offset": 31,
         },
-        "source": "function fn()",
+        "source": "function foo()",
         "start": Position {
           "column": 0,
           "line": 1,
@@ -2596,18 +2596,18 @@ Program {
         "kind": "identifier",
         "loc": Location {
           "end": Position {
-            "column": 11,
+            "column": 12,
             "line": 1,
-            "offset": 11,
+            "offset": 12,
           },
-          "source": "fn",
+          "source": "foo",
           "start": Position {
             "column": 9,
             "line": 1,
             "offset": 9,
           },
         },
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -2617,11 +2617,11 @@ Program {
   "kind": "program",
   "loc": Location {
     "end": Position {
-      "column": 30,
+      "column": 31,
       "line": 1,
-      "offset": 30,
+      "offset": 31,
     },
-    "source": "function fn() { class Foo {} }",
+    "source": "function foo() { class Foo {} }",
     "start": Position {
       "column": 0,
       "line": 1,
@@ -4930,30 +4930,30 @@ Program {
             "kind": "class",
             "loc": Location {
               "end": Position {
-                "column": 34,
+                "column": 35,
                 "line": 1,
-                "offset": 34,
+                "offset": 35,
               },
               "source": "final class Foo {}",
               "start": Position {
-                "column": 16,
+                "column": 17,
                 "line": 1,
-                "offset": 16,
+                "offset": 17,
               },
             },
             "name": Identifier {
               "kind": "identifier",
               "loc": Location {
                 "end": Position {
-                  "column": 31,
+                  "column": 32,
                   "line": 1,
-                  "offset": 31,
+                  "offset": 32,
                 },
                 "source": "Foo",
                 "start": Position {
-                  "column": 28,
+                  "column": 29,
                   "line": 1,
-                  "offset": 28,
+                  "offset": 29,
                 },
               },
               "name": "Foo",
@@ -4963,15 +4963,15 @@ Program {
         "kind": "block",
         "loc": Location {
           "end": Position {
-            "column": 36,
+            "column": 37,
             "line": 1,
-            "offset": 36,
+            "offset": 37,
           },
           "source": "{ final class Foo {} }",
           "start": Position {
-            "column": 14,
+            "column": 15,
             "line": 1,
-            "offset": 14,
+            "offset": 15,
           },
         },
       },
@@ -4979,11 +4979,11 @@ Program {
       "kind": "function",
       "loc": Location {
         "end": Position {
-          "column": 36,
+          "column": 37,
           "line": 1,
-          "offset": 36,
+          "offset": 37,
         },
-        "source": "function fn()",
+        "source": "function foo()",
         "start": Position {
           "column": 0,
           "line": 1,
@@ -4994,18 +4994,18 @@ Program {
         "kind": "identifier",
         "loc": Location {
           "end": Position {
-            "column": 11,
+            "column": 12,
             "line": 1,
-            "offset": 11,
+            "offset": 12,
           },
-          "source": "fn",
+          "source": "foo",
           "start": Position {
             "column": 9,
             "line": 1,
             "offset": 9,
           },
         },
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -5015,11 +5015,11 @@ Program {
   "kind": "program",
   "loc": Location {
     "end": Position {
-      "column": 36,
+      "column": 37,
       "line": 1,
-      "offset": 36,
+      "offset": 37,
     },
-    "source": "function fn() { final class Foo {} }",
+    "source": "function foo() { final class Foo {} }",
     "start": Position {
       "column": 0,
       "line": 1,
@@ -5816,15 +5816,15 @@ Program {
                 "kind": "string",
                 "loc": Location {
                   "end": Position {
-                    "column": 32,
+                    "column": 33,
                     "line": 1,
-                    "offset": 32,
+                    "offset": 33,
                   },
                   "source": "\\"something\\"",
                   "start": Position {
-                    "column": 21,
+                    "column": 22,
                     "line": 1,
-                    "offset": 21,
+                    "offset": 22,
                   },
                 },
                 "raw": "\\"something\\"",
@@ -5835,15 +5835,15 @@ Program {
             "kind": "echo",
             "loc": Location {
               "end": Position {
-                "column": 33,
+                "column": 34,
                 "line": 1,
-                "offset": 33,
+                "offset": 34,
               },
               "source": "echo \\"something\\";",
               "start": Position {
-                "column": 16,
+                "column": 17,
                 "line": 1,
-                "offset": 16,
+                "offset": 17,
               },
             },
             "shortForm": false,
@@ -5852,15 +5852,15 @@ Program {
         "kind": "block",
         "loc": Location {
           "end": Position {
-            "column": 35,
+            "column": 36,
             "line": 1,
-            "offset": 35,
+            "offset": 36,
           },
           "source": "{ echo \\"something\\"; }",
           "start": Position {
-            "column": 14,
+            "column": 15,
             "line": 1,
-            "offset": 14,
+            "offset": 15,
           },
         },
       },
@@ -5868,11 +5868,11 @@ Program {
       "kind": "function",
       "loc": Location {
         "end": Position {
-          "column": 35,
+          "column": 36,
           "line": 1,
-          "offset": 35,
+          "offset": 36,
         },
-        "source": "function fn()",
+        "source": "function foo()",
         "start": Position {
           "column": 0,
           "line": 1,
@@ -5883,18 +5883,18 @@ Program {
         "kind": "identifier",
         "loc": Location {
           "end": Position {
-            "column": 11,
+            "column": 12,
             "line": 1,
-            "offset": 11,
+            "offset": 12,
           },
-          "source": "fn",
+          "source": "foo",
           "start": Position {
             "column": 9,
             "line": 1,
             "offset": 9,
           },
         },
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -5904,11 +5904,11 @@ Program {
   "kind": "program",
   "loc": Location {
     "end": Position {
-      "column": 35,
+      "column": 36,
       "line": 1,
-      "offset": 35,
+      "offset": 36,
     },
-    "source": "function fn() { echo \\"something\\"; }",
+    "source": "function foo() { echo \\"something\\"; }",
     "start": Position {
       "column": 0,
       "line": 1,
@@ -8609,30 +8609,30 @@ Program {
           "kind": "parameter",
           "loc": Location {
             "end": Position {
-              "column": 28,
+              "column": 29,
               "line": 1,
-              "offset": 28,
+              "offset": 29,
             },
             "source": "?int $foo = 2112",
             "start": Position {
-              "column": 12,
+              "column": 13,
               "line": 1,
-              "offset": 12,
+              "offset": 13,
             },
           },
           "name": Identifier {
             "kind": "identifier",
             "loc": Location {
               "end": Position {
-                "column": 21,
+                "column": 22,
                 "line": 1,
-                "offset": 21,
+                "offset": 22,
               },
               "source": "$foo",
               "start": Position {
-                "column": 17,
+                "column": 18,
                 "line": 1,
-                "offset": 17,
+                "offset": 18,
               },
             },
             "name": "foo",
@@ -8642,15 +8642,15 @@ Program {
             "kind": "typereference",
             "loc": Location {
               "end": Position {
-                "column": 16,
+                "column": 17,
                 "line": 1,
-                "offset": 16,
+                "offset": 17,
               },
               "source": "int",
               "start": Position {
-                "column": 13,
+                "column": 14,
                 "line": 1,
-                "offset": 13,
+                "offset": 14,
               },
             },
             "name": "int",
@@ -8660,15 +8660,15 @@ Program {
             "kind": "number",
             "loc": Location {
               "end": Position {
-                "column": 28,
+                "column": 29,
                 "line": 1,
-                "offset": 28,
+                "offset": 29,
               },
               "source": "2112",
               "start": Position {
-                "column": 24,
+                "column": 25,
                 "line": 1,
-                "offset": 24,
+                "offset": 25,
               },
             },
             "value": "2112",
@@ -8681,15 +8681,15 @@ Program {
         "kind": "block",
         "loc": Location {
           "end": Position {
-            "column": 32,
+            "column": 33,
             "line": 1,
-            "offset": 32,
+            "offset": 33,
           },
           "source": "{}",
           "start": Position {
-            "column": 30,
+            "column": 31,
             "line": 1,
-            "offset": 30,
+            "offset": 31,
           },
         },
       },
@@ -8697,11 +8697,11 @@ Program {
       "kind": "function",
       "loc": Location {
         "end": Position {
-          "column": 32,
+          "column": 33,
           "line": 1,
-          "offset": 32,
+          "offset": 33,
         },
-        "source": "function fn(?int $foo = 2112)",
+        "source": "function foo(?int $foo = 2112)",
         "start": Position {
           "column": 0,
           "line": 1,
@@ -8712,18 +8712,18 @@ Program {
         "kind": "identifier",
         "loc": Location {
           "end": Position {
-            "column": 11,
+            "column": 12,
             "line": 1,
-            "offset": 11,
+            "offset": 12,
           },
-          "source": "fn",
+          "source": "foo",
           "start": Position {
             "column": 9,
             "line": 1,
             "offset": 9,
           },
         },
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -8733,11 +8733,11 @@ Program {
   "kind": "program",
   "loc": Location {
     "end": Position {
-      "column": 32,
+      "column": 33,
       "line": 1,
-      "offset": 32,
+      "offset": 33,
     },
-    "source": "function fn(?int $foo = 2112) {}",
+    "source": "function foo(?int $foo = 2112) {}",
     "start": Position {
       "column": 0,
       "line": 1,
diff --git a/test/snapshot/__snapshots__/namespace.test.js.snap b/test/snapshot/__snapshots__/namespace.test.js.snap
index 7b285893..95a51836 100644
--- a/test/snapshot/__snapshots__/namespace.test.js.snap
+++ b/test/snapshot/__snapshots__/namespace.test.js.snap
@@ -214,6 +214,8 @@ Program {
                     },
                     "name": "test",
                   },
+                  "nullable": false,
+                  "type": null,
                   "value": null,
                 },
               ],
@@ -741,6 +743,8 @@ Program {
                     },
                     "name": "test",
                   },
+                  "nullable": false,
+                  "type": null,
                   "value": null,
                 },
               ],
diff --git a/test/snapshot/__snapshots__/nowdoc.test.js.snap b/test/snapshot/__snapshots__/nowdoc.test.js.snap
index 5b5d6d2f..a9f89545 100644
--- a/test/snapshot/__snapshots__/nowdoc.test.js.snap
+++ b/test/snapshot/__snapshots__/nowdoc.test.js.snap
@@ -153,6 +153,8 @@ FOOBAR",
                 "kind": "identifier",
                 "name": "baz",
               },
+              "nullable": false,
+              "type": null,
               "value": Nowdoc {
                 "kind": "nowdoc",
                 "label": "FOOBAR",
diff --git a/test/snapshot/__snapshots__/parentreference.test.js.snap b/test/snapshot/__snapshots__/parentreference.test.js.snap
index 44c57519..8c058bed 100644
--- a/test/snapshot/__snapshots__/parentreference.test.js.snap
+++ b/test/snapshot/__snapshots__/parentreference.test.js.snap
@@ -29,7 +29,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -69,7 +69,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -156,7 +156,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": ParentReference {
@@ -196,7 +196,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": ParentReference {
diff --git a/test/snapshot/__snapshots__/property.test.js.snap b/test/snapshot/__snapshots__/property.test.js.snap
index b705de51..0bf2b517 100644
--- a/test/snapshot/__snapshots__/property.test.js.snap
+++ b/test/snapshot/__snapshots__/property.test.js.snap
@@ -15,6 +15,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
@@ -53,6 +55,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Number {
                 "kind": "number",
                 "value": "10",
@@ -94,6 +98,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
@@ -132,6 +138,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Number {
                 "kind": "number",
                 "value": "10",
@@ -173,6 +181,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
@@ -211,6 +221,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
@@ -249,6 +261,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Number {
                 "kind": "number",
                 "value": "10",
@@ -290,6 +304,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Number {
                 "kind": "number",
                 "value": "10",
@@ -331,6 +347,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
@@ -369,6 +387,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Number {
                 "kind": "number",
                 "value": "10",
@@ -410,6 +430,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Array {
                 "items": Array [
                   Boolean {
@@ -463,6 +485,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Bin {
                 "kind": "bin",
                 "left": String {
@@ -518,6 +542,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Bin {
                 "kind": "bin",
                 "left": Number {
@@ -567,6 +593,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Boolean {
                 "kind": "boolean",
                 "raw": "true",
@@ -609,6 +637,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": ClassReference {
                 "kind": "classreference",
                 "name": "CONSTANT",
@@ -651,6 +681,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": String {
                 "isDoubleQuote": true,
                 "kind": "string",
@@ -695,6 +727,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Encapsed {
                 "kind": "encapsed",
                 "label": "EOD",
@@ -756,6 +790,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Nowdoc {
                 "kind": "nowdoc",
                 "label": "EOD",
@@ -801,6 +837,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Array {
                 "items": Array [
                   Boolean {
@@ -854,6 +892,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": String {
                 "isDoubleQuote": false,
                 "kind": "string",
@@ -898,6 +938,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": Number {
                 "kind": "number",
                 "value": "10",
@@ -939,6 +981,8 @@ Program {
                 "kind": "identifier",
                 "name": "property",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
diff --git a/test/snapshot/__snapshots__/propertystatement.test.js.snap b/test/snapshot/__snapshots__/propertystatement.test.js.snap
index 81140b07..ef77f39e 100644
--- a/test/snapshot/__snapshots__/propertystatement.test.js.snap
+++ b/test/snapshot/__snapshots__/propertystatement.test.js.snap
@@ -15,6 +15,8 @@ Program {
                 "kind": "identifier",
                 "name": "dsn",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
             Property {
@@ -23,6 +25,8 @@ Program {
                 "kind": "identifier",
                 "name": "username",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
             Property {
@@ -31,6 +35,8 @@ Program {
                 "kind": "identifier",
                 "name": "password",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
@@ -69,6 +75,8 @@ Program {
                 "kind": "identifier",
                 "name": "dsn",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
             Property {
@@ -77,6 +85,8 @@ Program {
                 "kind": "identifier",
                 "name": "username",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
             Property {
@@ -85,6 +95,8 @@ Program {
                 "kind": "identifier",
                 "name": "password",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
@@ -123,6 +135,8 @@ Program {
                 "kind": "identifier",
                 "name": "dsn",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
@@ -161,6 +175,8 @@ Program {
                 "kind": "identifier",
                 "name": "dsn",
               },
+              "nullable": false,
+              "type": null,
               "value": null,
             },
           ],
diff --git a/test/snapshot/__snapshots__/selfreference.test.js.snap b/test/snapshot/__snapshots__/selfreference.test.js.snap
index 94b99854..5aad7363 100644
--- a/test/snapshot/__snapshots__/selfreference.test.js.snap
+++ b/test/snapshot/__snapshots__/selfreference.test.js.snap
@@ -29,7 +29,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -69,7 +69,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -156,7 +156,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": SelfReference {
@@ -196,7 +196,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": SelfReference {
diff --git a/test/snapshot/__snapshots__/typereference.test.js.snap b/test/snapshot/__snapshots__/typereference.test.js.snap
index 681cba6e..102450f8 100644
--- a/test/snapshot/__snapshots__/typereference.test.js.snap
+++ b/test/snapshot/__snapshots__/typereference.test.js.snap
@@ -30,7 +30,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -71,7 +71,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -108,7 +108,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -149,7 +149,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -194,7 +194,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -235,7 +235,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -272,7 +272,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -313,7 +313,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -358,7 +358,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -399,7 +399,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -436,7 +436,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -477,7 +477,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -522,7 +522,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -559,7 +559,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": ClassReference {
@@ -600,7 +600,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": ClassReference {
@@ -645,7 +645,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -686,7 +686,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -727,7 +727,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -764,7 +764,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -805,7 +805,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -850,7 +850,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -891,7 +891,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -928,7 +928,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -969,7 +969,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -1014,7 +1014,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -1055,7 +1055,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -1092,7 +1092,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -1133,7 +1133,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -1178,7 +1178,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -1219,7 +1219,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -1256,7 +1256,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -1297,7 +1297,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -1342,7 +1342,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -1383,7 +1383,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -1420,7 +1420,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -1461,7 +1461,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -1506,7 +1506,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -1547,7 +1547,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
@@ -1584,7 +1584,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
@@ -1625,7 +1625,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": TypeReference {
diff --git a/test/snapshot/__snapshots__/yield.test.js.snap b/test/snapshot/__snapshots__/yield.test.js.snap
index 8c523353..247a39f2 100644
--- a/test/snapshot/__snapshots__/yield.test.js.snap
+++ b/test/snapshot/__snapshots__/yield.test.js.snap
@@ -122,7 +122,7 @@ Program {
       "kind": "function",
       "name": Identifier {
         "kind": "identifier",
-        "name": "fn",
+        "name": "foo",
       },
       "nullable": false,
       "type": null,
diff --git a/test/snapshot/arrowfunc.test.js b/test/snapshot/arrowfunc.test.js
new file mode 100644
index 00000000..03b18dd8
--- /dev/null
+++ b/test/snapshot/arrowfunc.test.js
@@ -0,0 +1,68 @@
+const parser = require("../main");
+
+describe("arrow function", () => {
+  it("simple", () => {
+    expect(
+      parser.parseEval('$var = fn() => "something";')
+    ).toMatchSnapshot();
+  });
+  it("argument", () => {
+    expect(
+      parser.parseEval('$var = fn($arg) =>  "something";')
+    ).toMatchSnapshot();
+  });
+  it("argument by ref", () => {
+    expect(
+      parser.parseEval('$var = fn(&$arg) => "something";')
+    ).toMatchSnapshot();
+  });
+  it("arguments", () => {
+    expect(
+      parser.parseEval(
+        '$var = fn($arg, $arg, $arg) => "something";'
+      )
+    ).toMatchSnapshot();
+  });
+  it("return type", () => {
+    expect(
+      parser.parseEval(
+        '$var = fn(): ?string => "something";'
+      )
+    ).toMatchSnapshot();
+  });
+  it("inside call", () => {
+    expect(
+      parser.parseEval(`call(fn($arg) => $arg);`)
+    ).toMatchSnapshot();
+  });
+  it("error / fn passes on php7.3", () => {
+    expect(
+      parser.parseEval(`function fn($arg) { return $arg; }`, {
+        parser: {
+          php73: true,
+          php74: false // disable the php 7.4 support
+        }
+      })
+    ).toMatchSnapshot();
+  });  
+  it("error / fn fails on php7.4", () => {
+    expect(
+      parser.parseEval(`function fn($arg) { return $arg; }`, {
+        parser: {
+          php74: true,
+          suppressErrors: true
+        }
+      })
+    ).toMatchSnapshot();
+  });  
+  it("error / empty not allowed", () => {
+    expect(
+      parser.parseEval("$var = fn() => ;", {
+        parser: {
+          suppressErrors: true
+        }
+      })
+    ).toMatchSnapshot();
+  });
+
+});
diff --git a/test/snapshot/call.test.js b/test/snapshot/call.test.js
index 2e7909a5..ccbc91b4 100644
--- a/test/snapshot/call.test.js
+++ b/test/snapshot/call.test.js
@@ -102,7 +102,7 @@ describe("Test call", function() {
   });
   it("parent", function() {
     const ast = parser.parseEval(
-      'class Foo { function fn() { parent::call(); } }',
+      'class Foo { function foo() { parent::call(); } }',
       {
         parser: { debug: false }
       }
@@ -111,7 +111,7 @@ describe("Test call", function() {
   });
   it("self", function() {
     const ast = parser.parseEval(
-      'class Foo { function fn() { self::call(); } }',
+      'class Foo { function foo() { self::call(); } }',
       {
         parser: { debug: false }
       }
@@ -120,7 +120,7 @@ describe("Test call", function() {
   });
   it("static", function() {
     const ast = parser.parseEval(
-      'class Foo { function fn() { static::call(); } }',
+      'class Foo { function foo() { static::call(); } }',
       {
         parser: { debug: false }
       }
diff --git a/test/snapshot/class.test.js b/test/snapshot/class.test.js
index 38e36788..867cc02d 100644
--- a/test/snapshot/class.test.js
+++ b/test/snapshot/class.test.js
@@ -17,6 +17,17 @@ describe("Test classes", function() {
     ).toMatchSnapshot();
   });
 
+  it("Implement typed_properties_v2 / php74", function() {
+    expect(
+      parser.parseEval(`
+    class Test {
+      public ?int $prop = null;
+      protected static float|string $y;
+    }
+    `)
+    ).toMatchSnapshot();    
+  });
+
   it("Validate usual declarations", function() {
     expect(
       parser.parseEval(`
diff --git a/test/snapshot/classreference.test.js b/test/snapshot/classreference.test.js
index 03626c76..60a75433 100644
--- a/test/snapshot/classreference.test.js
+++ b/test/snapshot/classreference.test.js
@@ -11,9 +11,9 @@ describe("classreference", function() {
     expect(parser.parseEval('Foo::call();')).toMatchSnapshot();
   });
   it("argument type", function() {
-    expect(parser.parseEval('function fn(Foo $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(Foo $arg) {}')).toMatchSnapshot();
   });
   it("argument type (2)", function() {
-    expect(parser.parseEval('function fn(Foo\\Foo $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(Foo\\Foo $arg) {}')).toMatchSnapshot();
   });
 });
diff --git a/test/snapshot/location.test.js b/test/snapshot/location.test.js
index 5139bf87..9763a3ac 100644
--- a/test/snapshot/location.test.js
+++ b/test/snapshot/location.test.js
@@ -344,7 +344,7 @@ describe("Test locations", function() {
   });
   it("function", function() {
     expect(
-      parser.parseEval('function fn() { echo "something"; }', {
+      parser.parseEval('function foo() { echo "something"; }', {
         ast: {
           withPositions: true,
           withSource: true
@@ -384,7 +384,7 @@ describe("Test locations", function() {
   });
   it("class (inner statement)", function() {
     expect(
-      parser.parseEval("function fn() { class Foo {} }", {
+      parser.parseEval("function foo() { class Foo {} }", {
         ast: {
           withPositions: true,
           withSource: true
@@ -394,7 +394,7 @@ describe("Test locations", function() {
   });
   it("abstract class (inner statement)", function() {
     expect(
-      parser.parseEval("function fn() { abstract class Foo {} }", {
+      parser.parseEval("function foo() { abstract class Foo {} }", {
         ast: {
           withPositions: true,
           withSource: true
@@ -404,7 +404,7 @@ describe("Test locations", function() {
   });
   it("final class (inner statement)", function() {
     expect(
-      parser.parseEval("function fn() { final class Foo {} }", {
+      parser.parseEval("function foo() { final class Foo {} }", {
         ast: {
           withPositions: true,
           withSource: true
@@ -1012,7 +1012,7 @@ string";`,
   });
   it("parameter", function() {
     expect(
-      parser.parseEval("function fn(?int $foo = 2112) {}", {
+      parser.parseEval("function foo(?int $foo = 2112) {}", {
         ast: {
           withPositions: true,
           withSource: true
diff --git a/test/snapshot/parentreference.test.js b/test/snapshot/parentreference.test.js
index c3dbfd95..6a4702ad 100644
--- a/test/snapshot/parentreference.test.js
+++ b/test/snapshot/parentreference.test.js
@@ -14,15 +14,15 @@ describe("parentreference", function() {
     expect(parser.parseEval('PARENT::call();')).toMatchSnapshot();
   });
   it("argument", function() {
-    expect(parser.parseEval('function fn(parent $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(parent $arg) {}')).toMatchSnapshot();
   });
   it("argument (uppercase)", function() {
-    expect(parser.parseEval('function fn(PARENT $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(PARENT $arg) {}')).toMatchSnapshot();
   });
   it("return type declarations", function() {
-    expect(parser.parseEval('function fn($arg): parent {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): parent {}')).toMatchSnapshot();
   });
   it("return type declarations (uppercase)", function() {
-    expect(parser.parseEval('function fn($arg): PARENT {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): PARENT {}')).toMatchSnapshot();
   });
 });
diff --git a/test/snapshot/selfreference.test.js b/test/snapshot/selfreference.test.js
index 792b8561..901d0a3e 100644
--- a/test/snapshot/selfreference.test.js
+++ b/test/snapshot/selfreference.test.js
@@ -14,15 +14,15 @@ describe("selfreference", function() {
     expect(parser.parseEval('SELF::call();')).toMatchSnapshot();
   });
   it("argument", function() {
-    expect(parser.parseEval('function fn(self $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(self $arg) {}')).toMatchSnapshot();
   });
   it("argument (uppercase)", function() {
-    expect(parser.parseEval('function fn(SELF $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(SELF $arg) {}')).toMatchSnapshot();
   });
   it("return type declarations", function() {
-    expect(parser.parseEval('function fn($arg): self {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): self {}')).toMatchSnapshot();
   });
   it("return type declarations (uppercase)", function() {
-    expect(parser.parseEval('function fn($arg): SELF {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): SELF {}')).toMatchSnapshot();
   });
 });
diff --git a/test/snapshot/typereference.test.js b/test/snapshot/typereference.test.js
index 81a718b7..4f9d1db8 100644
--- a/test/snapshot/typereference.test.js
+++ b/test/snapshot/typereference.test.js
@@ -2,123 +2,123 @@ const parser = require('../main');
 
 describe("typereference", function() {
   it("int (argument)", function() {
-    expect(parser.parseEval('function fn(int $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(int $arg) {}')).toMatchSnapshot();
   });
   it("int (argument) (uppercase)", function() {
-    expect(parser.parseEval('function fn(INT $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(INT $arg) {}')).toMatchSnapshot();
   });
   it("int (return type declarations)", function() {
-    expect(parser.parseEval('function fn($arg): int {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): int {}')).toMatchSnapshot();
   });
   it("int (return type declarations) (uppercase)", function() {
-    expect(parser.parseEval('function fn($arg): INT {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): INT {}')).toMatchSnapshot();
   });
   it("float (argument)", function() {
-    expect(parser.parseEval('function fn(float $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(float $arg) {}')).toMatchSnapshot();
   });
   it("float (argument) (uppercase)", function() {
-    expect(parser.parseEval('function fn(FLOAT $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(FLOAT $arg) {}')).toMatchSnapshot();
   });
   it("float (return type declarations)", function() {
-    expect(parser.parseEval('function fn($arg): float {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): float {}')).toMatchSnapshot();
   });
   it("float (return type declarations) (uppercase)", function() {
-    expect(parser.parseEval('function fn($arg): FLOAT {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): FLOAT {}')).toMatchSnapshot();
   });
   it("bool (argument)", function() {
-    expect(parser.parseEval('function fn(bool $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(bool $arg) {}')).toMatchSnapshot();
   });
   it("bool (argument) (uppercase)", function() {
-    expect(parser.parseEval('function fn(BOOL $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(BOOL $arg) {}')).toMatchSnapshot();
   });
   it("bool (return type declarations)", function() {
-    expect(parser.parseEval('function fn($arg): bool {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): bool {}')).toMatchSnapshot();
   });
   it("bool (return type declarations) (uppercase)", function() {
-    expect(parser.parseEval('function fn($arg): BOOL {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): BOOL {}')).toMatchSnapshot();
   });
   it("string (argument)", function() {
-    expect(parser.parseEval('function fn(string $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(string $arg) {}')).toMatchSnapshot();
   });
   it("string (argument) (uppercase)", function() {
-    expect(parser.parseEval('function fn(STRING $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(STRING $arg) {}')).toMatchSnapshot();
   });
   it("string (return type declarations)", function() {
-    expect(parser.parseEval('function fn($arg): string {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): string {}')).toMatchSnapshot();
   });
   it("string (return type declarations) (uppercase)", function() {
-    expect(parser.parseEval('function fn($arg): STRING {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): STRING {}')).toMatchSnapshot();
   });
   it("array (argument)", function() {
-    expect(parser.parseEval('function fn(array $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(array $arg) {}')).toMatchSnapshot();
   });
   it("array (argument) (uppercase)", function() {
-    expect(parser.parseEval('function fn(ARRAY $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(ARRAY $arg) {}')).toMatchSnapshot();
   });
   it("array (return type declarations)", function() {
-    expect(parser.parseEval('function fn($arg): array {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): array {}')).toMatchSnapshot();
   });
   it("array (return type declarations) (uppercase)", function() {
-    expect(parser.parseEval('function fn($arg): ARRAY {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): ARRAY {}')).toMatchSnapshot();
   });
   it("callable (argument)", function() {
-    expect(parser.parseEval('function fn(callable $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(callable $arg) {}')).toMatchSnapshot();
   });
   it("callable (argument) (uppercase)", function() {
-    expect(parser.parseEval('function fn(CALLABLE $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(CALLABLE $arg) {}')).toMatchSnapshot();
   });
   it("callable (return type declarations)", function() {
-    expect(parser.parseEval('function fn($arg): callable {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): callable {}')).toMatchSnapshot();
   });
   it("callable (return type declarations) (uppercase)", function() {
-    expect(parser.parseEval('function fn($arg): CALLABLE  {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): CALLABLE  {}')).toMatchSnapshot();
   });
   it("object (argument)", function() {
-    expect(parser.parseEval('function fn(object $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(object $arg) {}')).toMatchSnapshot();
   });
   it("object (argument) (uppercase)", function() {
-    expect(parser.parseEval('function fn(OBJECT $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(OBJECT $arg) {}')).toMatchSnapshot();
   });
   it("object (return type declarations)", function() {
-    expect(parser.parseEval('function fn($arg): object {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): object {}')).toMatchSnapshot();
   });
   it("object (return type declarations) (uppercase)", function() {
-    expect(parser.parseEval('function fn($arg): OBJECT {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): OBJECT {}')).toMatchSnapshot();
   });
   it("iterable (argument)", function() {
-    expect(parser.parseEval('function fn(iterable $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(iterable $arg) {}')).toMatchSnapshot();
   });
   it("iterable (argument) (uppercase)", function() {
-    expect(parser.parseEval('function fn(ITERABLE $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(ITERABLE $arg) {}')).toMatchSnapshot();
   });
   it("iterable (return type declarations)", function() {
-    expect(parser.parseEval('function fn($arg): iterable {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): iterable {}')).toMatchSnapshot();
   });
   it("iterable (return type declarations) (uppercase)", function() {
-    expect(parser.parseEval('function fn($arg): ITERABLE {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): ITERABLE {}')).toMatchSnapshot();
   });
   it("void (argument)", function() {
-    expect(parser.parseEval('function fn(void $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(void $arg) {}')).toMatchSnapshot();
   });
   it("void (argument) (uppercase)", function() {
-    expect(parser.parseEval('function fn(VOID $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(VOID $arg) {}')).toMatchSnapshot();
   });
   it("void (return type declarations)", function() {
-    expect(parser.parseEval('function fn($arg): void {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): void {}')).toMatchSnapshot();
   });
   it("void (return type declarations) (uppercase)", function() {
-    expect(parser.parseEval('function fn($arg): VOID {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): VOID {}')).toMatchSnapshot();
   });
   it("class", function() {
-    expect(parser.parseEval('function fn(Foo $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(Foo $arg) {}')).toMatchSnapshot();
   });
   it("class (2)", function() {
-    expect(parser.parseEval('function fn(Foo\\Foo $arg) {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo(Foo\\Foo $arg) {}')).toMatchSnapshot();
   });
   it("class (3)", function() {
-    expect(parser.parseEval('function fn($arg): Foo {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): Foo {}')).toMatchSnapshot();
   });
   it("class (4)", function() {
-    expect(parser.parseEval('function fn($arg): Foo\\Foo {}')).toMatchSnapshot();
+    expect(parser.parseEval('function foo($arg): Foo\\Foo {}')).toMatchSnapshot();
   });
 });
diff --git a/test/snapshot/yield.test.js b/test/snapshot/yield.test.js
index f02d7a69..b80421b0 100644
--- a/test/snapshot/yield.test.js
+++ b/test/snapshot/yield.test.js
@@ -17,7 +17,7 @@ describe("yield", function() {
     expect(parser.parseEval('$data = (yield $key => $value);')).toMatchSnapshot();
   });
   it("inside function", function() {
-    expect(parser.parseEval('function fn() { yield $i; }')).toMatchSnapshot();
+    expect(parser.parseEval('function foo() { yield $i; }')).toMatchSnapshot();
   });
   it("null", function() {
     expect(parser.parseEval('yield;')).toMatchSnapshot();