slackhq / tree-sitter-hack

Hack grammar for tree-sitter
MIT License
33 stars 16 forks source link

Namespace declaration improperly handles whitespace #28

Closed frankeld closed 2 years ago

frankeld commented 3 years ago

Case:

namespace\test_call();

Expected Output:

(expression_statement
  (call_expression
    function: (qualified_identifier
      (identifier)
      (identifier))
    (arguments))))

Actual Output:

(script
  (namespace_declaration)
  (ERROR
    (qualified_identifier
      (identifier))
    (parameters))
  (empty_statement))
aosq commented 3 years ago

Wow. Wonder if this is a PHP-ism.

// The keyword namespace before a name should be parsed as
// "the current namespace we are in", essentially a no op.
// example:
// namespace\f1(); should be parsed as a call to the function f1 in
// the current namespace.

https://github.com/facebook/hhvm/blob/9c9d1ff64/hphp/hack/src/parser/core/declaration_parser.rs#L2544-L2548

a\namespace\b();
namespace\a\b();
{
  "kind": "script",
  "script_declarations": [
    {
      "kind": "expression_statement",
      "expression_statement_expression": {
        "kind": "function_call_expression",
        "function_call_receiver": {
          "kind": "qualified_name",
          "qualified_name_parts": [
            {
              "name": "a"
            },
            {
              "name": "namespace"
            },
            {
              "name": "b"
            }
          ]
        }
      }
    },
    {
      "kind": "expression_statement",
      "expression_statement_expression": {
        "kind": "function_call_expression",
        "function_call_receiver": {
          "kind": "qualified_name",
          "qualified_name_parts": [
            "namespace",
            {
              "name": "a"
            },
            {
              "name": "b"
            }
          ]
        }
      }
    }
  ]
}
cfroystad commented 3 years ago

It's indeed a PHP-ism 🙁

Relative names always resolve to the name with namespace replaced by the current namespace. If the name occurs in the global namespace, the namespace\ prefix is stripped. For example namespace\A inside namespace X\Y resolves to X\Y\A. The same name inside the global namespace resolves to A.

https://www.php.net/manual/en/language.namespaces.rules.php