in src/Linters/GroupUseStatementsLinter.hack [20:333]
public function getLintErrorForNode(
Script $_context,
Script $script,
): ?ASTLintError {
$error = false;
$uses = dict[
'function' => vec[],
'namespace' => vec[],
'type' => vec[],
];
$result = dict[
'function' => dict[],
'namespace' => dict[],
'type' => dict[],
];
$use_kind = (INamespaceUseDeclaration $node): ?string ==> {
foreach ($node->getChildren() as $child) {
if ($child is FunctionToken) {
return 'function';
}
if ($child is NamespaceToken) {
return 'namespace';
}
if ($child is TypeToken) {
return 'type';
}
}
return null;
};
foreach (
$script->getDescendantsByType<INamespaceUseDeclaration>() as $use_decl
) {
if ($use_decl is NamespaceUseDeclaration) {
foreach (
$use_decl->getDescendantsByType<NamespaceUseClause>() as $use_clause
) {
$parts = vec[];
$name_leading = vec[];
$name_trailing = vec[];
$name = $use_clause->getName();
if ($name is NameToken) {
if ($name->getLeading()->isList()) {
foreach ($name->getLeading()->getChildren() as $leading) {
$name_leading[] = $leading;
}
}
if ($name->getTrailing()->isList()) {
foreach ($name->getTrailing()->getChildren() as $trailing) {
$name_trailing[] = $trailing;
}
}
$parts[] = $name->getText();
} else if ($name is QualifiedName) {
foreach ($name->getDescendantsByType<NameToken>() as $name_token) {
if ($name_token->getLeading()->isList()) {
foreach ($name_token->getLeading()->getChildren() as $leading) {
$name_leading[] = $leading;
}
}
if ($name_token->getTrailing()->isList()) {
foreach (
$name_token->getTrailing()->getChildren() as $trailing
) {
$name_trailing[] = $trailing;
}
}
$parts[] = $name_token->getText();
}
}
if (C\count($parts) > 1) {
$alias = $use_clause->getAlias();
$alias_trailing = vec[];
if ($alias is nonnull) {
if ($alias->getTrailing()->isList()) {
foreach ($alias->getTrailing()->getChildren() as $trailing) {
$alias_trailing[] = $trailing;
}
}
}
$kind = $use_kind($use_decl);
if ($kind is nonnull) {
$uses[$kind][] = tuple(
$use_decl,
$parts,
$alias?->getText(),
$name_leading,
$name_trailing,
$alias_trailing,
vec[],
);
}
}
}
}
if ($use_decl is NamespaceGroupUseDeclaration) {
$parts = vec[];
foreach (
$use_decl->getChildrenByType<QualifiedName>() as $qualified_name
) {
foreach (
$qualified_name->getDescendantsByType<NameToken>() as $name_token
) {
$parts[] = $name_token->getText();
}
}
foreach (
$use_decl->getDescendantsByType<NamespaceUseClause>() as $use_clause
) {
$parts_item = vec[];
$name_leading = vec[];
$name_trailing = vec[];
$comma_trailing = vec[];
// ->getDescendantsByType<ListItem>() can not be used, because ListItem is generic
foreach ($use_decl->getDescendantsOfType(ListItem::class) as $item) {
if ($item->isAncestorOf($use_clause)) {
$comma_tokens = $item->getChildrenByType<CommaToken>();
if (!C\is_empty($comma_tokens)) {
$first_comma_token = C\firstx($comma_tokens);
if ($first_comma_token->getTrailing()->isList()) {
foreach (
$first_comma_token->getTrailing()->getChildren() as
$trailing
) {
$comma_trailing[] = $trailing;
}
}
}
break;
}
}
$name = $use_clause->getName();
if ($name is NameToken) {
if ($name->getLeading()->isList()) {
foreach ($name->getLeading()->getChildren() as $leading) {
$name_leading[] = $leading;
}
}
if ($name->getTrailing()->isList()) {
foreach ($name->getTrailing()->getChildren() as $trailing) {
$name_trailing[] = $trailing;
}
}
$parts_item[] = $name->getText();
} else if ($name is QualifiedName) {
foreach ($name->getDescendantsByType<NameToken>() as $name_token) {
if ($name_token->getLeading()->isList()) {
foreach ($name_token->getLeading()->getChildren() as $leading) {
$name_leading[] = $leading;
}
}
if ($name_token->getTrailing()->isList()) {
foreach (
$name_token->getTrailing()->getChildren() as $trailing
) {
$name_trailing[] = $trailing;
}
}
$parts_item[] = $name_token->getText();
}
}
$parts_item = Vec\concat($parts, $parts_item);
if (C\count($parts_item) > 1) {
$alias = $use_clause->getAlias();
$alias_trailing = vec[];
if ($alias is nonnull) {
if ($alias->getTrailing()->isList()) {
foreach ($alias->getTrailing()->getChildren() as $trailing) {
$alias_trailing[] = $trailing;
}
}
}
$kind = $use_kind($use_decl);
if ($kind is nonnull) {
$uses[$kind][] = tuple(
$use_decl,
$parts_item,
$alias?->getText(),
$name_leading,
$name_trailing,
$alias_trailing,
$comma_trailing,
);
}
}
}
}
}
foreach ($uses as $kind => $use_kind_loop_var) {
foreach ($use_kind_loop_var as $use) {
list(
$node,
$parts,
$alias,
$name_leading,
$name_trailing,
$alias_trailing,
$comma_trailing,
) = $use;
$namespace = Str\join(Vec\take($parts, C\count($parts) - 1), '\\');
$name = Str\join(Vec\drop($parts, C\count($parts) - 1), '\\');
if (!C\contains_key($result[$kind], $namespace)) {
$result[$kind][$namespace] = tuple(vec[], vec[]);
} else {
if (
C\count($result[$kind][$namespace][0]) > 1 ||
$result[$kind][$namespace][0][0] !== $node
) {
$error = true;
}
}
if (!C\contains($result[$kind][$namespace][0], $node)) {
$result[$kind][$namespace][0][] = $node;
}
if (
Dict\filter($result[$kind][$namespace][1], (
(
string,
?string,
vec<Trivia>,
vec<Trivia>,
vec<Trivia>,
vec<Trivia>,
) $n,
) ==> {
return $n[0] === $name && $n[1] === $alias;
})
|> C\count($$) === 0
) {
$drop = 0;
foreach ($name_leading as $leading) {
if (!$leading is WhiteSpace && !$leading is EndOfLine) {
break;
}
$drop++;
}
$name_leading = Vec\drop($name_leading, $drop);
$take_trailing = (vec<Trivia> $trailings) ==> {
$take = C\count($trailings);
foreach (Vec\reverse($trailings) as $trailing) {
if (!$trailing is WhiteSpace && !$trailing is EndOfLine) {
break;
}
$take--;
}
return Vec\take($trailings, $take);
};
$name_trailing = $take_trailing($name_trailing);
$alias_trailing = $take_trailing($alias_trailing);
$comma_trailing = $take_trailing($comma_trailing);
$result[$kind][$namespace][1][] = tuple(
$name,
$alias,
$name_leading,
$name_trailing,
$alias_trailing,
$comma_trailing,
);
}
}
}
if ($error) {
return new ASTLintError(
$this,
'Use statements should be grouped',
$script,
() ==> $this->getFixedNode($script, Dict\map($result, (
$result_kind,
) ==> {
return Dict\filter($result_kind, ($value) ==> C\count($value[0]) > 1);
})),
);
}
return null;
}