4 * Return the relative path of an absolute path.
6 * Assumes that this directory is the root directory.
8 function relative_path(string $path): string {
9 if (strlen($path) === 0 ||
$path[0] !== '/') {
13 $offset = strpos($path, $root);
15 print "FAILURE: $path does not begin with $root\n";
17 return substr($path, $offset +
strlen($root) +
1);
20 function jsonify_arr(Container
<arraykey
> $arr): string {
23 return json_encode($arr);
26 function jsonify_filters(HH\Facts\DeriveFilters
$filters): string {
27 $filters_for_json = dict
[];
28 $kinds = Shapes
::idx($filters, 'kind');
29 if ($kinds is Container
<_
>) {
32 $filters_for_json['kind'] = $kinds;
34 $derive_kinds = Shapes
::idx($filters, 'derive_kind');
35 if ($derive_kinds is Container
<_
>) {
36 $derive_kinds = vec($derive_kinds);
37 \
sort(inout
$derive_kinds);
38 $filters_for_json['derive_kind'] = $derive_kinds;
40 $attributes = Shapes
::idx($filters, 'attributes');
41 if ($attributes is Container
<_
>) {
42 $attributes = vec($attributes);
43 \
sort(inout
$attributes);
44 $filters_for_json['attributes'] = $attributes;
47 return json_encode($filters_for_json);
50 function print_kind(classname
<mixed> $type): void
{
51 $kind = HH\Facts\
kind($type);
53 print "$type has no kind\n";
55 print "$type is $kind\n";
59 function print_subtypes(
60 classname
<mixed> $type,
61 ?HH\Facts\DeriveFilters
$filters = null,
63 if ($filters is nonnull
) {
64 $subtypes_json = jsonify_arr(HH\Facts\
subtypes($type, $filters));
65 $filters_json = jsonify_filters($filters);
66 print "Subtypes of $type with filters $filters_json: $subtypes_json\n";
68 $subtypes_json = jsonify_arr(HH\Facts\
subtypes($type));
69 print "Subtypes of $type: $subtypes_json\n";
73 function print_transitive_subtypes(
74 classname
<mixed> $type,
75 ?HH\Facts\DeriveFilters
$filters = null,
77 if ($filters is nonnull
) {
78 $subtypes_json = jsonify_arr(HH\Facts\transitive_subtypes
(
82 $filters_json = jsonify_filters($filters);
83 print "Transitive subtypes of $type with filters $filters_json: ".
86 $subtypes_json = jsonify_arr(HH\Facts\transitive_subtypes
($type));
87 print "Transitive subtypes of $type: $subtypes_json\n";
91 function print_supertypes(
92 classname
<mixed> $type,
93 ?HH\Facts\DeriveFilters
$filters = null,
95 if ($filters is nonnull
) {
96 $supertypes_json = jsonify_arr(HH\Facts\
supertypes($type, $filters));
97 $filters_json = jsonify_filters($filters);
98 print "Supertypes of $type with filters $filters_json: $supertypes_json\n";
100 $supertypes_json = jsonify_arr(HH\Facts\
supertypes($type));
101 print "Supertypes of $type: $supertypes_json\n";
105 function print_type_attrs(
106 classname
<mixed> $type,
109 foreach (HH\Facts\type_attributes
($type) as $attr) {
110 $attrs[$attr] = HH\Facts\type_attribute_parameters
($type, $attr);
112 \
ksort(inout
$attrs);
113 $attrs_json = \
json_encode($attrs);
114 print "Attributes of $type: $attrs_json\n";
117 function print_type_alias_attrs(
121 foreach (HH\Facts\type_alias_attributes
($type_alias) as $attr) {
122 $attrs[$attr] = HH\Facts\type_alias_attribute_parameters
(
127 \
ksort(inout
$attrs);
128 $attrs_json = \
json_encode($attrs);
129 print "Attributes of $type_alias: $attrs_json\n";
132 function print_method_attrs(
133 classname
<nonnull
> $type,
137 foreach (HH\Facts\
method_attributes($type, $method) as $attr) {
138 $attrs[$attr] = HH\Facts\
method_attribute_parameters($type, $method, $attr);
140 \
ksort(inout
$attrs);
141 $attrs_json = \
json_encode($attrs);
142 print "Attributes of $type::$method: $attrs_json\n";
145 function print_file_attrs(
149 foreach (HH\Facts\file_attributes
($file) as $attr) {
150 $attrs[$attr] = HH\Facts\file_attribute_parameters
($file, $attr);
152 \
ksort(inout
$attrs);
153 $attrs_json = \
json_encode($attrs);
154 print "Attributes of $file: $attrs_json\n";
157 function print_attr_types(
158 classname
<\HH\ClassAttribute
> $attr,
160 $types = HH\Facts\types_with_attribute
($attr);
162 $types_json = \
json_encode($types);
163 print "Types decorated with $attr: $types_json\n";
166 function print_attr_type_aliases(
167 classname
<\HH\ClassAttribute
> $attr,
169 $type_aliases = HH\Facts\type_aliases_with_attribute
($attr);
170 \
sort(inout
$type_aliases);
171 $type_aliases_json = \
json_encode($type_aliases);
172 print "Type aliases decorated with $attr: $type_aliases_json\n";
175 function print_attr_methods(
176 classname
<\HH\MethodAttribute
> $attr,
178 $method_tuples = HH\Facts\
methods_with_attribute($attr);
180 foreach ($method_tuples as list($type, $method)) {
181 $methods[] = "$type::$method";
183 \
sort(inout
$methods);
184 $methods_json = \
json_encode($methods);
185 print "Methods decorated with $attr: $methods_json\n";
188 function print_attr_files(
189 classname
<\HH\FileAttribute
> $attr,
191 $files = HH\Facts\files_with_attribute
($attr);
193 $files_json = \
json_encode($files);
194 print "Files decorated with $attr: $files_json\n";
197 function print_num_symbols(
200 $types = HH\Facts\
path_to_types($path);
201 $num_types = \
count($types);
202 print "$path has $num_types types\n";
204 $functions = HH\Facts\
path_to_functions($path);
205 $num_functions = \
count($functions);
206 print "$path has $num_functions functions\n";
208 $constants = HH\Facts\
path_to_constants($path);
209 $num_constants = \
count($constants);
210 print "$path has $num_constants constants\n";
212 $type_aliases = HH\Facts\
path_to_type_aliases($path);
213 $num_type_aliases = \
count($type_aliases);
214 print "$path has $num_type_aliases type aliases\n";
217 function print_extracted_facts(vec
<string> $files): void
{
218 $files_with_null_hashes = vec
[];
219 foreach ($files as $file) {
220 $files_with_null_hashes[] = tuple($file, null);
223 HH\Facts\
extract($files_with_null_hashes) as $file => $facts
225 print "Facts in $file:\n";
228 foreach ($facts['types'] as $type) {
229 $name = $type['name'];
230 print " name: $name\n";
232 $kind = $type['kind'];
233 print " kind: $kind\n";
235 $flags = $type['flags'];
236 print " flags: $flags\n";
238 $base_types = $type['baseTypes'];
239 \
sort(inout
$base_types);
240 $base_types_json = \
json_encode($base_types);
241 print " baseTypes: $base_types_json\n";
243 $require_extends = $type['requireExtends'];
244 \
sort(inout
$require_extends);
245 $require_extends_json = \
json_encode($require_extends);
246 print " requireExtends: $require_extends_json\n";
248 $require_implements = $type['requireImplements'];
249 \
sort(inout
$require_implements);
250 $require_implements_json = \
json_encode($require_implements);
251 print " requireImplements: $require_implements_json\n";
253 $attributes = $type['attributes'];
256 ($attr1, $attr2) ==> $attr1['name'] <=> $attr2['name'],
258 $attributes_json = \
json_encode($attributes);
259 print " attributes: $attributes_json\n";
261 $methods = $type['methods'];
264 ($m1, $m2) ==> $m1['name'] <=> $m2['name'],
266 $methods_json = \
json_encode($methods);
267 print " methods: $methods_json\n";
270 $functions = $facts['functions'];
271 \
sort(inout
$functions);
272 $functions_json = \
json_encode($functions);
273 print " functions: $functions_json\n";
275 $constants = $facts['constants'];
276 \
sort(inout
$constants);
277 $constants_json = \
json_encode($constants);
278 print " constants: $constants_json\n";
280 $attributes = $facts['attributes'];
283 ($attr1, $attr2) ==> $attr1['name'] <=> $attr2['name'],
285 $attributes_json = \
json_encode($attributes);
286 print " attributes: $attributes_json\n";
288 $sha1 = $facts['sha1sum'];
289 print " sha1sum: $sha1\n";
294 function facts(): void
{
295 var_dump(HH\Facts\
enabled());
297 var_dump(HH\Facts\TypeKind
::K_CLASS
);
298 var_dump(HH\Facts\TypeKind
::K_ENUM
);
299 var_dump(HH\Facts\TypeKind
::K_INTERFACE
);
300 var_dump(HH\Facts\TypeKind
::K_TRAIT
);
302 print_kind(BaseClass
::class);
303 print_kind(DerivedClass
::class);
305 print_kind(C1
::class);
306 print_kind(E1
::class);
307 print_kind(I1
::class);
308 print_kind(T1
::class);
309 print_kind(Nonexistent
::class);
311 print_subtypes(BaseClass
::class);
312 print_supertypes(BaseClass
::class);
316 shape('kind' => keyset
[HH\Facts\TypeKind
::K_CLASS
]),
318 print_transitive_subtypes(
320 shape('kind' => keyset
[HH\Facts\TypeKind
::K_CLASS
]),
324 shape('kind' => keyset
[HH\Facts\TypeKind
::K_TRAIT
]),
326 print_transitive_subtypes(
328 shape('kind' => keyset
[HH\Facts\TypeKind
::K_TRAIT
]),
334 HH\Facts\TypeKind
::K_CLASS
,
335 HH\Facts\TypeKind
::K_TRAIT
,
339 print_transitive_subtypes(
343 HH\Facts\TypeKind
::K_CLASS
,
344 HH\Facts\TypeKind
::K_TRAIT
,
349 print_supertypes(DerivedClass
::class);
352 shape('kind' => keyset
[HH\Facts\TypeKind
::K_CLASS
]),
356 shape('kind' => keyset
[HH\Facts\TypeKind
::K_TRAIT
]),
360 shape('kind' => keyset
[HH\Facts\TypeKind
::K_INTERFACE
]),
366 HH\Facts\TypeKind
::K_CLASS
,
367 HH\Facts\TypeKind
::K_TRAIT
,
375 HH\Facts\TypeKind
::K_INTERFACE
,
376 HH\Facts\TypeKind
::K_TRAIT
,
381 print "\nExcluding `require extends` relations\n";
385 shape('derive_kind' => keyset
[HH\Facts\DeriveKind
::K_EXTENDS
]),
387 print_transitive_subtypes(
389 shape('derive_kind' => keyset
[HH\Facts\DeriveKind
::K_EXTENDS
]),
393 shape('derive_kind' => keyset
[HH\Facts\DeriveKind
::K_EXTENDS
]),
395 print_transitive_subtypes(
397 shape('derive_kind' => keyset
[HH\Facts\DeriveKind
::K_EXTENDS
]),
400 TRequireExtendsBaseClassAndRequireImplementsIBase
::class,
401 shape('derive_kind' => keyset
[HH\Facts\DeriveKind
::K_EXTENDS
]),
404 TRequireImplementsAndImplementsIBase
::class,
405 shape('derive_kind' => keyset
[HH\Facts\DeriveKind
::K_EXTENDS
]),
410 'kind' => keyset
[HH\Facts\TypeKind
::K_INTERFACE
],
411 'derive_kind' => keyset
[HH\Facts\DeriveKind
::K_EXTENDS
],
414 print_transitive_subtypes(
417 'kind' => keyset
[HH\Facts\TypeKind
::K_INTERFACE
],
418 'derive_kind' => keyset
[HH\Facts\DeriveKind
::K_EXTENDS
],
422 print "\nExcluding `extends` relations\n";
426 shape('derive_kind' => keyset
[HH\Facts\DeriveKind
::K_REQUIRE_EXTENDS
]),
430 shape('derive_kind' => keyset
[HH\Facts\DeriveKind
::K_REQUIRE_EXTENDS
]),
432 print_transitive_subtypes(
434 shape('derive_kind' => keyset
[HH\Facts\DeriveKind
::K_REQUIRE_EXTENDS
]),
437 TRequireExtendsBaseClassAndRequireImplementsIBase
::class,
438 shape('derive_kind' => keyset
[HH\Facts\DeriveKind
::K_REQUIRE_EXTENDS
]),
440 print_transitive_subtypes(
442 shape('derive_kind' => keyset
[HH\Facts\DeriveKind
::K_REQUIRE_EXTENDS
]),
445 TRequireImplementsAndImplementsIBase
::class,
446 shape('derive_kind' => keyset
[HH\Facts\DeriveKind
::K_REQUIRE_EXTENDS
]),
450 shape('kind' => keyset
[HH\Facts\TypeKind
::K_CLASS
]),
455 'kind' => keyset
[HH\Facts\TypeKind
::K_INTERFACE
],
456 'derive_kind' => keyset
[HH\Facts\DeriveKind
::K_REQUIRE_EXTENDS
],
459 print_transitive_subtypes(
462 'kind' => keyset
[HH\Facts\TypeKind
::K_INTERFACE
],
463 'derive_kind' => keyset
[HH\Facts\DeriveKind
::K_REQUIRE_EXTENDS
],
469 'kind' => keyset
[HH\Facts\TypeKind
::K_TRAIT
],
470 'derive_kind' => keyset
[HH\Facts\DeriveKind
::K_REQUIRE_EXTENDS
],
473 print_transitive_subtypes(
476 'kind' => keyset
[HH\Facts\TypeKind
::K_TRAIT
],
477 'derive_kind' => keyset
[HH\Facts\DeriveKind
::K_REQUIRE_EXTENDS
],
481 print "\nFiltering by attribute\n";
484 BaseClassForAttributeFiltering
::class,
485 shape('attributes' => vec
[shape(
486 'name' => TwoArgAttr
::class,
487 'parameters' => dict
[0 => "banana"],
490 print_transitive_subtypes(
491 IBaseForAttributeFiltering
::class,
492 shape('attributes' => vec
[shape(
493 'name' => TwoArgAttr
::class,
494 'parameters' => dict
[0 => "banana"],
498 BaseClassForAttributeFiltering
::class,
499 shape('attributes' => vec
[shape(
500 'name' => TwoArgAttr
::class,
501 'parameters' => dict
[0 => 'apple'],
504 print_transitive_subtypes(
505 IBaseForAttributeFiltering
::class,
506 shape('attributes' => vec
[shape(
507 'name' => TwoArgAttr
::class,
508 'parameters' => dict
[0 => 'apple'],
512 BaseClassForAttributeFiltering
::class,
513 shape('attributes' => vec
[shape(
514 'name' => TwoArgAttr
::class,
515 'parameters' => dict
[1 => 'carrot'],
518 print_transitive_subtypes(
519 IBaseForAttributeFiltering
::class,
520 shape('attributes' => vec
[shape(
521 'name' => TwoArgAttr
::class,
522 'parameters' => dict
[1 => 'carrot'],
526 print "\nGetting attributes\n";
528 print_type_attrs(AppleThenBanana
::class);
529 print_type_attrs(BananaThenApple
::class);
530 print_type_attrs(AppleThenCarrot
::class);
531 print_type_attrs(ClassWithTwoAttrs
::class);
532 print_type_alias_attrs(TypeAliasWithAttr
::class);
533 print_method_attrs(ClassWithMethodAttrs
::class, 'methodWithNoArgAttr');
534 print_method_attrs(ClassWithMethodAttrs
::class, 'methodWithTwoArgAttr');
537 "\nThese should be empty, otherwise we're mixing types and type aliases ".
539 print_type_alias_attrs(ClassWithTwoAttrs
::class);
540 print_type_attrs(TypeAliasWithAttr
::class);
542 print "\nGetting types with attribute\n";
543 print_attr_types(NoArgAttr
::class);
544 print_attr_types(TwoArgAttr
::class);
545 print_attr_methods(NoArgMethodAttr
::class);
546 print_attr_methods(TwoArgMethodAttr
::class);
548 print "\nGetting type aliases with attribute\n";
549 print_attr_type_aliases(TypeAliasAttr
::class);
551 print "\nGetting file attributes\n";
552 print_attr_files(NoArgFileAttr
::class);
553 print_attr_files(TwoArgFileAttr
::class);
554 print_file_attrs('attribute-classes.inc');
556 print "\nChecking nonexistent paths\n";
557 print_num_symbols('this/path/does/not/exist.php');
558 print_num_symbols('/this/path/does/not/exist.php');
560 print "\nGetting all types\n";
561 $all_types = HH\Facts\all_types
();
562 \
ksort(inout
$all_types);
563 foreach ($all_types as $type => $path) {
564 $is_abstract = HH\Facts\
is_abstract($type) ?
'true' : 'false';
565 $is_final = HH\Facts\
is_final($type) ?
'true' : 'false';
566 print "$type => $path\n";
567 print " Abstract? $is_abstract\n";
568 print " Final? $is_final\n";
569 $facts_path = relative_path(HH\Facts\type_to_path
($type) as nonnull
);
570 if ($facts_path !== $path) {
571 print "FAILURE: $facts_path !== $path\n";
574 print "\nGetting all functions\n";
575 $all_functions = HH\Facts\all_functions
();
576 \
ksort(inout
$all_functions);
577 foreach ($all_functions as $function => $path) {
578 print "$function => $path\n";
579 $facts_path = relative_path(
580 HH\Facts\function_to_path
($function) as nonnull
,
582 if ($facts_path !== $path) {
583 print "FAILURE: $facts_path !== $path\n";
586 print "\nGetting all constants\n";
587 $all_constants = HH\Facts\all_constants
();
588 \
ksort(inout
$all_constants);
589 foreach ($all_constants as $constant => $path) {
590 print "$constant => $path\n";
591 $facts_path = relative_path(
592 HH\Facts\
constant_to_path($constant) as nonnull
,
594 if ($facts_path !== $path) {
595 print "FAILURE: $facts_path !== $path\n";
598 print "\nGetting all type aliases\n";
599 $all_type_aliases = HH\Facts\all_type_aliases
();
600 \
ksort(inout
$all_type_aliases);
601 foreach ($all_type_aliases as $type_alias => $path) {
602 print "$type_alias => $path\n";
603 $facts_path = relative_path(
604 HH\Facts\type_alias_to_path
($type_alias) as nonnull
,
606 if ($facts_path !== $path) {
607 print "FAILURE: $facts_path !== $path\n";
611 print_extracted_facts(
613 'attribute-classes.inc',
614 'attribute-namespace.inc',
619 'types-with-kinds.inc',