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 {}
}
Additional rules:
xhp class :foo
is an error)extends :foo:bar
is fine, but means \foo\bar, not namespace foo:bar):
MUST NOT be used to refer to non-XHP types\
MUST NOT be used for XHP class decl names (e.gxhp class foo\bar
is an error)\
MAY be used for XHP class extends lists (e.g.xhp class foo extends \x\element
)xhp class foo:bar extend \x\element
\
MUST NOT be used for XHP constructor calls\
MAY be used for XHP children declarations