Except for the 'changes/decisions' section, this document aims to be standalone - though without going into rationale of things that have been agreed on.
For detailed rationale and alternatives, see https://gist.github.com/fredemmott/9db26f3d14652a510d56327c956f4a57 and comments
- Defining a class in a sub-namespace (
class :foo:bar
/xhp class foo:bar
) is no longer intended to be temporary- avoid proliferation of fully-qualified names and explicit
use namespace
etc - this will not be permitted for non-XHP classes, e.g.:
xhp class foo:bar {}
is permittedclass foo:bar {}
is notclass foo\bar {}
is not
- avoid proliferation of fully-qualified names and explicit
- Defining a class in a sub-namespace is permitted in any namespace, not just the root namespace
- allows new use cases of XHP to be largely defined in a consistent way with existing uses of (1) in the same codebase
xhp class foo:bar
instead ofclass :foo:bar
: make it so that:
is only a namespace separator, not a sigil- avoids most cases of problems like 'does
:foo:bar
mean\foo\bar
ornamespace\foo\bar
?', especially in declarations
- avoids most cases of problems like 'does
- ban
-
in XHP class names: use explicit_
instead (which it currently mangles to).- in combination with
:
just being a namespace separator, this entirely removes mangling from XHP - consistent with non-XHP classes
- consistent with JSX/React
- in combination with
- When exactly should
:
be permitted? Goal is "XHP contexts"- definitely: XHP constructor calls (
$_ = <HERE />
) - definitely: XHP class names at declarations (
xhp class HERE {}
) - undecided: XHP parents:
xhp class foo extends HERE {}
- relates to (3) -
namespace MyFeature { class :foo:bar extends :x:element {} }
: needs to be\x\element
- relates to (3) -
- undecided: XHP children statements:
xhp class foo { children (HERE *); }
- undecided: other uses of XHP as a type -
foo(:bar:baz $_)
,foo ? :bar:baz : qux
- definitely: XHP constructor calls (
- Is
:foo:bar
->foo\bar
a safe codemod excluding those situations?->:foo
would need to stop being parsed as->{XHP_Class_Name}
- chilren cases (
children(:foo:bar*)
includes a postfix unary expression) are complicated
- If we need to keep
:foo:bar
supported in places other than XHP class declarations, how do we disambiguate between\foo\bar
andnamespace\foo\bar
?:foo:bar
vs::foo:bar
?- do we have real problems along the lines of
foo ? bar:baz : qux
- Should
xhp class :foo:bar
be permitted?- for: more familiar for existing users
- against:
namespace myns { xhp class :foo:bar {} }
- for consistency with<:foo:bar>
, this should define\foo\bar
; but a namespace should not contain a definition of something that is not a member of that namespace - alt: only in root NS. for as above, consistency against
- Dynamic cases are covered by later enforcement of
<<__DynamicallyConstructible>>
; we will keep existing mangling until that logs/throws. This may requireclass_exists()
and similar to log/throw if<<__DynamicallyConstructible>>
is not declared on the class - If a context already permits both qualified names and XHP class names, a straightforward replacement is safe
- No code using XHP is in a namespace at present, as it is effectively unusable
- impossible to reference
\:x:element
as a superclass - impossible to reference
\:foo
from an XHP constructor call
- impossible to reference
Seen by hhast at:
- classish_declaration.classish_name
- member_selection_expression.member_name (attributes:
$this->:attr
- RHS is an XHP class name) - postfix_unary_expression.postfix_unary_operand (children:
children (:foo*)
-:foo*
has operand:foo
and oeprator*
)) xhp_children_* - places that also accept a qualified name
namespace {
// old: defines '\xhp_foo__bar'
class :foo:bar extends :x:element {}
// new: defines '\foo\bar'
xhp class foo:bar extends x:element {}
// optional: equivalent in the root namespace
// allow: slightly smaller codemod/more familiar
// parse error: consistent with banning in sub-namespaces
xhp class :foo:bar extends :x:element {}
// old: instantiate \xhp_foo__bar
// new: instantiate namespace\foo\bar, which is \foo\bar
$_ = <foo:bar />;
// old: parse error
// new: instantiate \foo\bar
$_ = <:foo:bar />;
// old: defines `\xhp_foo__bar_baz'
class :foo:bar-baz {}
// new: parse error
xhp class foo:bar-baz {}
// new: defines \foo\bar_baz
xhp class foo:bar_baz {}
// old: takes a \xhp_foo
// new: open question 3: parse error or takes a namespace\foo or \foo (equivalent here)
function dostuff(:foo $_): void {}
// new: takes a namespace\foo, which is a \foo
function dostuff(foo $_): void {}
}
namespace myns {
// old: no usable equivalent
// new: defines '\myns\foo\bar` extending `\x\element`
xhp class foo:bar extends :x:element {}
// new: defines '\myns\foo\bar` extending `\myns\foo\bar`
// probably a mistake
xhp class foo:bar extends x:element {}
// old: no usable equivalent
// new: parse error or other kind of forbidden
// for consistency, would mean \foo\bar, which we don't want to allow
xhp class :foo:bar {}
// old: uninstantiable but parsable
// new: instantiate namespace\foo\bar, which is myns\foo\bar
$_ = <foo:bar />;
// old: parse error
// new: instantiate \foo\bar
$_ = <:foo:bar />;
// \foo\bar [
// \myns\herp:derp [
// \p [
// "hello"
// ]
// ]
// ]
$_ = <:foo:bar><herp:derp><:p>hello</:p></herp:derp></:foo:bar>;
// old: no usable equivalent
// new: open question 3: options are:
// - parse error
// - takes a namespace\foo (which is a myns\foo)
// - takes a \foo
function dostuff(:foo $_): void {}
// old: parse error
// new: open question 3: parse error or \foo
function dostuff(::foo $_): void {}
}
With that path, the safe codemod (2) and disambuation (3) questions don't apply.