From 01f7fed70d675643b200e8661e07eaed781592af Mon Sep 17 00:00:00 2001 From: Scott Sandler Date: Wed, 25 Jan 2023 11:12:29 -0700 Subject: [PATCH] use ffp_parse_string instead of shelling out to hh_parse (#537) * use ffp_parse_string instead of shelling out to hh_parse * remove user_args from entrypoints.hack * testing * fix lack of hhi * remove support for xhp children declarations and the migration, since they break native parsing --- .devcontainer/devcontainer.json | 2 +- ...XHPChildrenDeclarationMethodMigration.hack | 430 ------------------ src/__Private/MigrationCLI.hack | 9 +- src/__Private/ParserCache.hack | 2 +- src/__Private/from_decoded_json.hack | 2 +- src/entrypoints.hack | 32 +- ...hildrenDeclarationMethodMigrationTest.hack | 31 -- tests/NodeTypesTest.hack | 18 +- .../basic_behavior.hack.expect | 37 -- .../basic_behavior.hack.in | 17 - .../leading_trivia.hack.expect | 13 - .../leading_trivia.hack.in | 5 - .../namespace_blocks.hack.expect | 26 -- .../namespace_blocks.hack.in | 18 - .../no_children.hack.expect | 5 - .../no_children.hack.in | 5 - 16 files changed, 14 insertions(+), 638 deletions(-) delete mode 100644 src/Migrations/AddXHPChildrenDeclarationMethodMigration.hack delete mode 100644 tests/AddXHPChildrenDeclarationMethodMigrationTest.hack delete mode 100644 tests/examples/migrations/AddXHPChildrenDeclarationMethod/basic_behavior.hack.expect delete mode 100644 tests/examples/migrations/AddXHPChildrenDeclarationMethod/basic_behavior.hack.in delete mode 100644 tests/examples/migrations/AddXHPChildrenDeclarationMethod/leading_trivia.hack.expect delete mode 100644 tests/examples/migrations/AddXHPChildrenDeclarationMethod/leading_trivia.hack.in delete mode 100644 tests/examples/migrations/AddXHPChildrenDeclarationMethod/namespace_blocks.hack.expect delete mode 100644 tests/examples/migrations/AddXHPChildrenDeclarationMethod/namespace_blocks.hack.in delete mode 100644 tests/examples/migrations/AddXHPChildrenDeclarationMethod/no_children.hack.expect delete mode 100644 tests/examples/migrations/AddXHPChildrenDeclarationMethod/no_children.hack.in diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 250b9b360..3725efbf9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,7 +4,7 @@ "runArgs": [ "--init" ], - "image": "hhvm/hhvm:latest", + "image": "hhvm/hhvm:4.168-latest", // Set *default* container specific settings.json values on container create. "settings": { diff --git a/src/Migrations/AddXHPChildrenDeclarationMethodMigration.hack b/src/Migrations/AddXHPChildrenDeclarationMethodMigration.hack deleted file mode 100644 index 895c09db2..000000000 --- a/src/Migrations/AddXHPChildrenDeclarationMethodMigration.hack +++ /dev/null @@ -1,430 +0,0 @@ -/* - * 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}; - -final class AddXHPChildrenDeclarationMethodMigration - extends StepBasedMigration { - private static function scopeNeedsUseNamespace(NodeList $in): bool { - if ( - Str\contains( - $in->getCode(), - 'use namespace Facebook\\XHP\\ChildValidation as XHPChild;', - ) - ) { - return false; - } - $classes = $in->getChildrenByType(); - return C\any( - $classes, - $class ==> !C\is_empty( - $class->getBody() - ->getElements() - ?->getChildrenByType() ?? - vec[], - ), - ); - } - - private static function addUseNamespaceToScript(Script $in): Script { - $decls = $in->getDeclarations(); - if (!self::scopeNeedsUseNamespace($decls)) { - return $in; - } - - $before = C\firstx($decls->getChildrenByType()); - $t = $before->getFirstTokenx(); - $use = self::getUseNamespace(); - $ut = $use->getFirstTokenx(); - return $in->withDeclarations( - $in->getDeclarations()->insertBefore( - $before, - $use->replace($ut, $ut->withLeading($t->getLeading())), - ), - ) - ->replace($t, $t->withLeading(null)); - } - - private static function addUseNamespaceToNamespaceBlock( - NamespaceDeclaration $in, - ): NamespaceDeclaration { - // Can't use XHP in namespaces, so if we're not in the root namespace, - // we have nothing to do. - if ($in->hasName()) { - return $in; - } - - // Don't have to worry about empty bodies as `namespace;` is invalid, - // and `namespace foo;` is caught above. - - $body = $in->getBody() as NamespaceBody; - $decls = $body->getDeclarationsx(); - if (!self::scopeNeedsUseNamespace($decls)) { - return $in; - } - - $use = self::getUseNamespace(); - $ws = $decls->getFirstTokenx()->getLeadingWhitespace(); - if ($ws !== null) { - $t = $use->getFirstTokenx(); - $use = $use->replace($t, $t->withLeading(new NodeList(vec[$ws]))); - } - - return $in->withBody( - $body->withDeclarations( - $decls->insertBefore( - C\firstx($decls->getChildrenByType()), - $use, - ), - ), - ); - } - - <<__Memoize>> - private static function getUseNamespace(): NamespaceUseDeclaration { - $s = new NodeList(vec[new WhiteSpace(' ')]); - return new NamespaceUseDeclaration( - new UseToken(null, $s), - new NamespaceToken(null, $s), - new NodeList(vec[new ListItem( - new NamespaceUseClause( - null, - new QualifiedName( - new NodeList(vec[ - new ListItem( - new NameToken(null, null, 'Facebook'), - new BackslashToken(null, null), - ), - new ListItem( - new NameToken(null, null, 'XHP'), - new BackslashToken(null, null), - ), - new ListItem(new NameToken(null, $s, 'ChildValidation'), null), - ]), - ), - new AsToken(null, $s, 'as'), - new NameToken(null, null, 'XHPChild'), - ), - null, - )]), - new SemicolonToken( - null, - new NodeList(vec[new EndOfLine("\n"), new EndOfLine("\n")]), - ), - ); - } - - private static function alreadyUsesTrait(ClassishBody $in): bool { - $uses = $in->getElementsx()->getChildrenByType() - |> Vec\map($$, $use ==> $use->getNames()->getChildrenOfItems()) - |> Vec\flatten($$); - foreach ($uses as $use) { - $name = $use ?as SimpleTypeSpecifier - |> $$?->getSpecifier() ?as NameToken - |> $$?->getText(); - if ( - $name === 'XHPChildValidation' || - $name === 'XHPChildDeclarationConsistencyValidation' - ) { - return true; - } - } - return false; - } - - private static function addTrait(ClassishBody $in): ClassishBody { - $decl = $in->getElements() - ?->getChildrenByType() ?? - vec[]; - if (C\is_empty($decl)) { - return $in; - } - if (self::alreadyUsesTrait($in)) { - return $in; - } - - $decl = C\firstx($decl); - - $indent = $decl->getFirstTokenx()->getLeadingWhitespace()?->getText() ?? ''; - $use = new TraitUse( - new UseToken( - new NodeList(vec[new WhiteSpace($indent)]), - new NodeList(vec[new WhiteSpace(' ')]), - ), - new NodeList(vec[ - new ListItem( - new SimpleTypeSpecifier( - new NameToken( - null, - null, - 'XHPChildDeclarationConsistencyValidation', - ), - ), - null, - ), - ]), - new SemicolonToken(null, new NodeList(vec[new EndOfLine("\n")])), - ); - - return $in->withElements( - $in->getElementsx() - ->insertBefore(C\firstx($in->getElementsx()->toVec()), $use), - ); - } - - private static function addChildrenDeclarationMethod( - ClassishBody $in, - ): ClassishBody { - $decl = $in->getElements() - ?->getChildrenByType() ?? - vec[]; - if (C\is_empty($decl)) { - return $in; - } - foreach ( - $in->getElementsx()->getChildrenByType() as $method - ) { - if ( - $method->getFunctionDeclHeader()->getName()->getText() === - 'getChildrenDeclaration' - ) { - return $in; - } - } - $decl = C\firstx($decl); - - $indent = $decl->getFirstTokenx()->getLeadingWhitespace()?->getText() ?? ''; - $s = new NodeList(vec[new WhiteSpace(' ')]); - $meth = new MethodishDeclaration( - null, - self::getFunctionDeclarationHeader($indent), - new CompoundStatement( - new LeftBraceToken(null, new NodeList(vec[new EndOfLine("\n")])), - new NodeList(vec[ - new ReturnStatement( - new ReturnToken( - new NodeList(vec[new WhiteSpace($indent.$indent)]), - $s, - ), - self::convertChildrenExpression($decl->getExpression()), - new SemicolonToken(null, new NodeList(vec[new EndOfLine("\n")])), - ), - ]), - new RightBraceToken( - new NodeList(vec[new WhiteSpace($indent)]), - new NodeList(vec[ - new EndOfLine("\n"), - new EndOfLine("\n"), - ]), - ), - ), - /* semicolon = */ null, - ); - return $in->withElements($in->getElementsx()->insertAfter($decl, $meth)); - } - - <<__Memoize>> - private static function getFunctionDeclarationHeader( - string $leading_whitespace, - ): FunctionDeclarationHeader { - $s = new NodeList(vec[new WhiteSpace(' ')]); - return new FunctionDeclarationHeader( - new NodeList(vec[ - new ProtectedToken( - new NodeList( - vec[new EndOfLine("\n"), new WhiteSpace($leading_whitespace)], - ), - $s, - ), - new StaticToken(null, $s), - ]), - new FunctionToken(null, $s), - new NameToken(null, null, 'getChildrenDeclaration'), - null, - new LeftParenToken(null, null), - null, - new RightParenToken(null, null), - null, - new ColonToken(null, $s), - null, - new SimpleTypeSpecifier( - new QualifiedName( - new NodeList(vec[ - new ListItem( - new NameToken(null, null, 'XHPChild'), - new BackslashToken(null, null), - ), - new ListItem(new NameToken(null, $s, 'Constraint'), null), - ]), - ), - ), - null, - ); - } - - private static function convertChildrenExpression( - Node $in, - ): FunctionCallExpression { - if ($in is XHPChildrenParenthesizedList) { - $count = $in->getXhpChildren()->getCount(); - invariant($count >= 1, 'Got empty XHP children parenthesized list'); - if ($count === 1) { - return self::convertChildrenExpression( - C\onlyx($in->getXhpChildren()->getChildrenOfItems()), - ); - } - $children = Vec\map( - $in->getXhpChildren()->getChildrenOfItems(), - $node ==> self::convertChildrenExpression($node), - ); - return self::makeCall('sequence', null, $children); - } - - if ($in is PostfixUnaryExpression) { - $op = $in->getOperator(); - if ($op is PlusToken) { - $fun = 'atLeastOneOf'; - } else if ($op is QuestionToken) { - $fun = 'optional'; - } else if ($op is StarToken) { - $fun = 'anyNumberOf'; - } else { - invariant_violation( - "Got an XHP postfix unary expression with unexpected operator '%s'", - $in->getOperator()->getText(), - ); - } - return self::makeCall( - $fun, - null, - vec[self::convertChildrenExpression($in->getOperand())], - ); - } - - if ($in is BinaryExpression) { - // foo | bar | baz - invariant( - $in->getOperator() is BarToken, - "Got an XHP child binary expression with unexpected operator '%s'", - $in->getOperator()->getText(), - ); - // `foo | bar | baz` is `((foo | bar) | baz)` in the AST; flatten them out - // for readability - $next = $in; - $parts = vec[]; - while ($next is BinaryExpression && $next->getOperator() is BarToken) { - $parts[] = $next->getRightOperand(); - $next = $next->getLeftOperand(); - } - $parts[] = $next; - $parts = Vec\reverse($parts) - |> Vec\map($$, $part ==> self::convertChildrenExpression($part)); - return self::makeCall('anyOf', null, $parts); - } - - if ($in is NameExpression || $in is EmptyToken || $in is NameToken) { - $name = $in is NameExpression ? $in->getWrappedNode() : $in; - if ( - $name is NameToken || $name is EmptyToken || $name is XHPClassNameToken - ) { - $name = $name->getText(); - if ($name === 'pcdata' || $name === 'empty' || $name === 'any') { - return self::makeCall($name); - } - - // Class name - return self::makeCall( - 'ofType', - new TypeArguments( - new LessThanToken(null, null), - new NodeList(vec[ - new ListItem( - new SimpleTypeSpecifier(new NameToken(null, null, $name)), - null, - ), - ]), - new GreaterThanToken(null, null), - ), - ); - } - - if ($name is XHPCategoryNameToken) { - return self::makeCall('category', null, vec[new LiteralExpression( - new SingleQuotedStringLiteralToken( - null, - null, - "'".$name->getText()."'", - ), - )]); - } - - invariant_violation( - 'Unhandled XHP child name type: %s', - \get_class($name), - ); - } - - invariant_violation( - 'Unhandled XHP children expression: %s (%s)', - \get_class($in), - $in->getCode(), - ); - } - - private static function makeCall( - string $name, - ?TypeArguments $generics = null, - vec $arguments = vec[], - ): FunctionCallExpression { - $sep = C\count($arguments) > 1 - ? new CommaToken(null, new NodeList(vec[new WhiteSpace(' ')])) - : null; - return new FunctionCallExpression( - new QualifiedName( - new NodeList(vec[ - new ListItem( - new NameToken(null, null, 'XHPChild'), - new BackslashToken(null, null), - ), - new ListItem(new NameToken(null, null, $name), null), - ]), - ), - $generics, - new LeftParenToken(null, null), - C\is_empty($arguments) - ? null - : new NodeList(Vec\map($arguments, $arg ==> new ListItem($arg, $sep))), - new RightParenToken(null, null), - ); - } - - <<__Override>> - public function getSteps(): Traversable { - return vec[ - new NodeTypeMigrationStep( - 'Add `use` statement to top level of file if needed', - $node ==> self::addUseNamespaceToScript($node), - ), - new NodeTypeMigrationStep( - 'Add `use` statement to namespace blocks as needed', - $node ==> self::addUseNamespaceToNamespaceBlock($node), - ), - new NodeTypeMigrationStep( - 'Add `getChildrenDeclaration()` method', - $node ==> self::addChildrenDeclarationMethod($node), - ), - new NodeTypeMigrationStep( - 'Add `XHPChildDeclarationConsistencyValidation` trait', - $node ==> self::addTrait($node), - ), - ]; - } -} diff --git a/src/__Private/MigrationCLI.hack b/src/__Private/MigrationCLI.hack index 9d5c17462..ad3b4a9da 100644 --- a/src/__Private/MigrationCLI.hack +++ b/src/__Private/MigrationCLI.hack @@ -13,7 +13,6 @@ use namespace Facebook\{HHAST, TypeAssert}; use namespace HH\Lib\{C, Dict, Str, Vec}; use type Facebook\HHAST\{ AddFixmesMigration, - AddXHPChildrenDeclarationMethodMigration, BaseMigration, DemangleXHPMigration, DollarBraceEmbeddedVariableMigration, @@ -170,13 +169,7 @@ final class MigrationCLI extends CLIWithRequiredArguments { '--harden-varray-or-darray-typehints', ), self::removed('--php-array-typehints-best-guess', '4.64.4 to 4.64.6'), - CLIOptions\flag( - () ==> { - $this->migrations[] = AddXHPChildrenDeclarationMethodMigration::class; - }, - 'Add getChildrenDeclaration() method to XHP classes with a children declaration', - '--add-xhp-children-declaration-method', - ), + self::removed('--add-xhp-children-declaration-method', '4.73 to 4.153'), CLIOptions\flag( () ==> { $this->migrations[] = DemangleXHPMigration::class; diff --git a/src/__Private/ParserCache.hack b/src/__Private/ParserCache.hack index 69ebe9684..f4d2c19ff 100644 --- a/src/__Private/ParserCache.hack +++ b/src/__Private/ParserCache.hack @@ -54,7 +54,7 @@ final class ParserCache { return $data; } - public function store(File $file, dict $ast): void { + public function store(File $file, dict $ast): void { $path = $this->getCacheFileName($file); if ($path === null) { return; diff --git a/src/__Private/from_decoded_json.hack b/src/__Private/from_decoded_json.hack index d711274a0..a1a849d66 100644 --- a/src/__Private/from_decoded_json.hack +++ b/src/__Private/from_decoded_json.hack @@ -12,7 +12,7 @@ namespace Facebook\HHAST\__Private; use type Facebook\HHAST\{SchemaVersionError, Script}; function from_decoded_json( - dict $json, + dict $json, ?string $file = null, ): Script { $version = $json['version'] ?? null; diff --git a/src/entrypoints.hack b/src/entrypoints.hack index 2cdbfbfbc..87352fa1c 100644 --- a/src/entrypoints.hack +++ b/src/entrypoints.hack @@ -9,45 +9,31 @@ namespace Facebook\HHAST; -use namespace HH\Lib\{Str, Vec}; +use namespace HH\Lib\Str; +use function HH\ffp_parse_string_native; async function from_file_async( File $file, - vec $user_args = vec[], ): Awaitable