forked from hhvm/hhast
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTopLevelRequiresMigration.hack
More file actions
112 lines (100 loc) · 3.01 KB
/
Copy pathTopLevelRequiresMigration.hack
File metadata and controls
112 lines (100 loc) · 3.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/*
* Copyright (c) 2017-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\HHAST;
use namespace HH\Lib\{C, Str, Vec};
/** Move requires to top-level `<<__EntryPoint>>` functions */
final class TopLevelRequiresMigration extends BaseMigration {
<<__Override>>
public function migrateFile(string $_path, Script $script): Script {
/*HHAST_FIXME[DontUseAsioJoin]*/
return \HH\Asio\join($this->migrateFileAsync($script));
}
private async function migrateFileAsync(Script $script): Awaitable<Script> {
$decls = $script->getDeclarations();
$includes = $decls->getChildrenByType<InclusionDirective>();
if (C\is_empty($includes)) {
return $script;
}
$entrypoint = $decls->getChildrenByType<FunctionDeclaration>()
|> Vec\filter(
$$,
$f ==> C\any(
$f->getAttributeSpec()?->getAttributes()?->getChildrenOfItems() ??
vec[],
$attr ==>
($attr->getType() as NameToken)->getText() === '__EntryPoint',
),
)
|> C\first($$);
if (!$entrypoint) {
return $script;
}
$classes = $script->getDescendantsByType<ClassishDeclaration>();
if (
C\any(
$classes,
$c ==> $c->hasExtendsKeyword() || $c->hasImplementsKeyword(),
)
) {
/* This kind of file needs to be manually refactored:
*
* require_once('Bar.php');
* class Foo extends Bar {}
*/
return $script;
}
// Figure out leading whitespace
$body = $entrypoint->getBody();
if (!$body is CompoundStatement) {
// Invalid, but e.g. `<<__EntryPoint>> function foo(): void;`
return $script;
}
$leading = $body->getStatements()?->toVec() ?? vec[]
|> C\first($$)
|> $$?->getFirstTokenx()?->getLeadingWhitespace()
|> $$?->getText() ?? ' ';
// Generate a lambda so that if the require()'d file sets variables in
// the psuedomain, they don't affect the entrypoint
$includes_text = Vec\map(
$includes,
$incl ==> {
$t = $incl->getFirstTokenx();
return $incl->replace(
$t,
$t->withLeading(new NodeList(vec[new WhiteSpace($leading.$leading)])),
)
->getCode();
},
)
|> Str\join($$, '');
$lambda = await self::statementFromCodeAsync(
$leading.
"(() ==> {\n".
$leading.
$leading.
"// HHAST-generated to avoid pseudomain local leaks\n".
$includes_text.
$leading.
"})();\n\n",
);
$body = $body->withStatements(
new NodeList(
Vec\concat(
vec[$lambda],
$body->getStatements()?->getChildren() ?? varray[],
),
),
);
return $script->withDeclarations(
$script->getDeclarations()
->filterChildren($decl ==> !C\contains($includes, $decl))
->replaceChild($entrypoint, $entrypoint->withBody($body)),
);
}
}