in src/get_unresolved_referenced_names.hack [19:104]
function get_unresolved_referenced_names(Node $root): shape(
'namespaces' => keyset<string>,
'types' => keyset<string>,
'functions' => keyset<string>,
) {
$ret = shape(
'namespaces' => keyset[],
'types' => keyset[],
'functions' => keyset[],
);
foreach ($root->traverse() as $node) {
if ($node is QualifiedName) {
$name = C\firstx($node->getParts()->getChildrenOfItems());
if ($name is NameToken) {
$ret['namespaces'][] = $name->getText();
}
continue;
}
if ($node is SimpleTypeSpecifier) {
$name = $node->getSpecifierx();
if ($name is NameToken) {
$ret['types'][] = $name->getText();
}
continue;
}
if ($node is GenericTypeSpecifier) {
$name = $node->getClassType();
if ($name is NameToken) {
$ret['types'][] = $name->getText();
}
}
if ($node is ScopeResolutionExpression) {
$name = $node->getQualifier();
if ($name is NameToken) {
$ret['types'][] = $name->getText();
}
continue;
}
if ($node is FunctionCallExpression) {
$name = $node->getReceiver();
if ($name is NameToken) {
$ret['functions'][] = $name->getText();
}
continue;
}
if ($node is FunctionPointerExpression) {
$name = $node->getReceiver();
if ($name is NameExpression) {
$name = $name->getWrappedNode();
if ($name is NameToken) {
$ret['functions'][] = $name->getText();
}
}
continue;
}
// `new Foo()` gets us a SimpleTypeSpecifier, but
// <<Foo>> gets us a NameToken directly
if ($node is ConstructorCall) {
$name = $node->getType() ?as NameToken;
if ($name !== null) {
$ret['types'][] = $name->getText();
}
}
if (
$node is XHPElementNameToken &&
\ini_get('hhvm.hack.lang.disable_xhp_element_mangling')
) {
$parts = Str\split($node->getText(), ':');
if (C\count($parts) === 1) {
$ret['types'][] = C\onlyx($parts);
} else {
$ret['namespaces'][] = C\firstx($parts);
}
}
}
return $ret;
}