src/consumers/classish_from_ast.hack (113 lines of code) (raw):
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
namespace Facebook\DefinitionFinder;
use namespace Facebook\HHAST;
use namespace HH\Lib\{C, Vec};
function classish_from_ast<T as ScannedClassish>(
ConsumerContext $context,
classname<T> $def_class,
HHAST\ClassishDeclaration $node,
): ?T {
$context = context_with_node_position($context, $node);
switch ($def_class) {
case ScannedClass::class:
if (!$node->getKeyword() is HHAST\ClassToken) {
return null;
}
break;
case ScannedInterface::class:
if (!$node->getKeyword() is HHAST\InterfaceToken) {
return null;
}
break;
case ScannedTrait::class:
if (!$node->getKeyword() is HHAST\TraitToken) {
return null;
}
break;
default:
invariant_violation('new classish kind: %s', $def_class);
}
$name = $node->getName();
if ($name === null) {
return null;
} else if ($name is HHAST\XHPClassNameToken) {
$name = decl_name_in_context($context, mangle_xhp_name_token($name));
} else {
$name = decl_name_in_context($context, $name->getText());
}
$modifiers = $node->getModifiers() ?? new HHAST\NodeList(vec[]);
$has_modifier = (
classname<HHAST\Node> $m
) ==> !C\is_empty($modifiers->getChildrenOfType($m));
$generics = generics_from_ast($context, $node->getTypeParameters());
$extends = $node->getExtendsList();
$parent = null;
$interfaces = vec[];
if ($extends) {
$extends = $extends->getChildrenOfItemsOfType(HHAST\Node::class)
|> Vec\map($$, $super ==> typehint_from_ast($context, $super))
|> Vec\filter_nulls($$);
if ($def_class === ScannedClass::class) {
if (C\count($extends) === 1) {
$parent = C\onlyx($extends);
}
} else {
invariant(
$def_class === ScannedInterface::class,
"Shouldnt see `extends` unless we're dealing with a class or interface",
);
$interfaces = $extends;
}
}
$implements = $node->getImplementsList();
if ($implements) {
$interfaces = $implements->getChildrenOfItemsOfType(HHAST\Node::class)
|> Vec\map($$, $super ==> typehint_from_ast($context, $super))
|> Vec\filter_nulls($$);
}
$contents_context = $context;
$contents_context['scopeType'] = ScopeType::CLASSISH_SCOPE;
$contents =
scope_from_ast($contents_context, $node->getBody()->getElements());
$promoted_constructor_args = vec[];
$constructor =
C\find($contents->getMethods(), $m ==> $m->getName() === '__construct');
if ($constructor) {
$promoted_constructor_args =
Vec\filter($constructor->getParameters(), $p ==> $p->__isPromoted())
|> Vec\map(
$$,
$p ==> new ScannedProperty(
$p->getASTx(),
$p->getName(),
$p->getContext(),
$p->getAttributes(),
$p->getDocComment(),
$p->getTypehint(),
$p->__getVisibility(),
StaticityToken::NOT_STATIC,
$p->getDefault(),
),
);
}
return new $def_class(
$node,
$name,
$context['definitionContext'],
attributes_from_ast($node->getAttribute()),
/* docblock = */ null,
$contents->getMethods(),
Vec\concat($contents->getProperties(), $promoted_constructor_args),
$contents->getConstants(),
$contents->getTypeConstants(),
$generics,
$parent,
$interfaces,
$contents->getUsedTraits(),
$has_modifier(HHAST\AbstractToken::class)
? AbstractnessToken::IS_ABSTRACT
: AbstractnessToken::NOT_ABSTRACT,
$has_modifier(HHAST\FinalToken::class)
? FinalityToken::IS_FINAL
: FinalityToken::NOT_FINAL,
);
}