Add support for validation of missing method synopses (#9491)

This commit is contained in:
Máté Kocsis 2022-09-07 17:40:36 +02:00 committed by GitHub
parent 05aa3b3e0a
commit ce058273ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 72 additions and 13 deletions

View file

@ -1222,6 +1222,8 @@ class FuncInfo {
public $numRequiredArgs; public $numRequiredArgs;
/** @var string|null */ /** @var string|null */
public $cond; public $cond;
/** @var bool */
public $isUndocumentable;
/** /**
* @param ArgInfo[] $args * @param ArgInfo[] $args
@ -1238,7 +1240,8 @@ class FuncInfo {
array $args, array $args,
ReturnInfo $return, ReturnInfo $return,
int $numRequiredArgs, int $numRequiredArgs,
?string $cond ?string $cond,
bool $isUndocumentable
) { ) {
$this->name = $name; $this->name = $name;
$this->classFlags = $classFlags; $this->classFlags = $classFlags;
@ -1252,6 +1255,7 @@ class FuncInfo {
$this->return = $return; $this->return = $return;
$this->numRequiredArgs = $numRequiredArgs; $this->numRequiredArgs = $numRequiredArgs;
$this->cond = $cond; $this->cond = $cond;
$this->isUndocumentable = $isUndocumentable;
} }
public function isMethod(): bool public function isMethod(): bool
@ -3218,7 +3222,8 @@ function parseFunctionLike(
int $classFlags, int $classFlags,
int $flags, int $flags,
Node\FunctionLike $func, Node\FunctionLike $func,
?string $cond ?string $cond,
bool $isUndocumentable
): FuncInfo { ): FuncInfo {
try { try {
$comment = $func->getDocComment(); $comment = $func->getDocComment();
@ -3283,6 +3288,10 @@ function parseFunctionLike(
} }
$paramMeta[$varName][$tag->name] = true; $paramMeta[$varName][$tag->name] = true;
break; break;
case 'undocumentable':
$isUndocumentable = true;
break;
} }
} }
} }
@ -3383,7 +3392,8 @@ function parseFunctionLike(
$args, $args,
$return, $return,
$numRequiredArgs, $numRequiredArgs,
$cond $cond,
$isUndocumentable
); );
} catch (Exception $e) { } catch (Exception $e) {
throw new Exception($name . "(): " .$e->getMessage()); throw new Exception($name . "(): " .$e->getMessage());
@ -3570,6 +3580,12 @@ function parseClass(
throw new Exception("Unknown class kind " . get_class($class)); throw new Exception("Unknown class kind " . get_class($class));
} }
if ($isUndocumentable) {
foreach ($methods as $method) {
$method->isUndocumentable = true;
}
}
return new ClassInfo( return new ClassInfo(
$name, $name,
$flags, $flags,
@ -3674,7 +3690,8 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
0, 0,
0, 0,
$stmt, $stmt,
$cond $cond,
$fileInfo->isUndocumentable
); );
continue; continue;
} }
@ -3731,7 +3748,8 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
$classFlags, $classFlags,
$classStmt->flags | $abstractFlag, $classStmt->flags | $abstractFlag,
$classStmt, $classStmt,
$cond $cond,
$fileInfo->isUndocumentable
); );
} else if ($classStmt instanceof Stmt\EnumCase) { } else if ($classStmt instanceof Stmt\EnumCase) {
$enumCaseInfos[] = new EnumCaseInfo( $enumCaseInfos[] = new EnumCaseInfo(
@ -4342,7 +4360,7 @@ function replaceClassSynopses(string $targetDirectory, array $classMap, iterable
foreach ($missingClassSynopses as $className => $info) { foreach ($missingClassSynopses as $className => $info) {
/** @var ClassInfo $info */ /** @var ClassInfo $info */
if (!$info->isUndocumentable) { if (!$info->isUndocumentable) {
echo "Warning: Missing class synopsis page for $className\n"; echo "Warning: Missing class synopsis for $className\n";
} }
} }
} }
@ -4388,7 +4406,8 @@ function generateMethodSynopses(array $funcMap, array $aliasMap): array {
* @param array<string, FuncInfo> $aliasMap * @param array<string, FuncInfo> $aliasMap
* @return array<string, string> * @return array<string, string>
*/ */
function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $aliasMap): array { function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $aliasMap, bool $isVerify): array {
$existingMethodSynopses = [];
$methodSynopses = []; $methodSynopses = [];
$it = new RecursiveIteratorIterator( $it = new RecursiveIteratorIterator(
@ -4407,6 +4426,27 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a
continue; continue;
} }
if ($isVerify) {
$matches = [];
preg_match("/<refname>\s*([\w:]+)\s*<\/refname>\s*<refpurpose>\s*&Alias;\s*<(?:function|methodname)>\s*([\w:]+)\s*<\/(?:function|methodname)>\s*<\/refpurpose>/i", $xml, $matches);
$aliasName = $matches[1] ?? null;
$alias = $funcMap[$aliasName] ?? null;
$funcName = $matches[2] ?? null;
$func = $funcMap[$funcName] ?? null;
if ($alias &&
!$alias->isUndocumentable &&
($func === null || $func->alias === null || $func->alias->__toString() !== $aliasName) &&
($alias->alias === null || $alias->alias->__toString() !== $funcName)
) {
echo "Warning: $aliasName()" . ($alias->alias ? " is an alias of " . $alias->alias->__toString() . "(), but it" : "") . " is incorrectly documented as an alias for $funcName()\n";
}
if ($aliasName) {
$existingMethodSynopses[$aliasName] = $aliasName;
}
}
if (stripos($xml, "<methodsynopsis") === false && stripos($xml, "<constructorsynopsis") === false && stripos($xml, "<destructorsynopsis") === false) { if (stripos($xml, "<methodsynopsis") === false && stripos($xml, "<constructorsynopsis") === false && stripos($xml, "<destructorsynopsis") === false) {
continue; continue;
} }
@ -4448,7 +4488,9 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a
if (!isset($funcMap[$funcName])) { if (!isset($funcMap[$funcName])) {
continue; continue;
} }
$funcInfo = $funcMap[$funcName]; $funcInfo = $funcMap[$funcName];
$existingMethodSynopses[$funcInfo->name->__toString()] = $funcInfo->name->__toString();
$newMethodSynopsis = $funcInfo->getMethodSynopsisElement($funcMap, $aliasMap, $doc); $newMethodSynopsis = $funcInfo->getMethodSynopsisElement($funcMap, $aliasMap, $doc);
if ($newMethodSynopsis === null) { if ($newMethodSynopsis === null) {
@ -4534,6 +4576,16 @@ function replaceMethodSynopses(string $targetDirectory, array $funcMap, array $a
} }
} }
if ($isVerify) {
$missingMethodSynopses = array_diff_key($funcMap, $existingMethodSynopses);
foreach ($missingMethodSynopses as $functionName => $info) {
/** @var FuncInfo $info */
if (!$info->isUndocumentable) {
echo "Warning: Missing method synopsis for $functionName()\n";
}
}
}
return $methodSynopses; return $methodSynopses;
} }
@ -4845,7 +4897,7 @@ if ($generateMethodSynopses) {
} }
if ($replaceMethodSynopses) { if ($replaceMethodSynopses) {
$methodSynopses = replaceMethodSynopses($targetSynopses, $funcMap, $aliasMap); $methodSynopses = replaceMethodSynopses($targetSynopses, $funcMap, $aliasMap, $verify);
foreach ($methodSynopses as $filename => $content) { foreach ($methodSynopses as $filename => $content) {
if (file_put_contents($filename, $content)) { if (file_put_contents($filename, $content)) {

View file

@ -1,6 +1,9 @@
<?php <?php
/** @generate-class-entries */ /**
* @generate-class-entries
* @undocumentable
*/
function dl_test_test1(): void {} function dl_test_test1(): void {}

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead. /* This is a generated file, edit the .stub.php file instead.
* Stub hash: 39ccf8141b0eb785cafd490b420f2e99d6a7a66d */ * Stub hash: 547ddbc21e9aa853b491cb17e902bbbb9cc2df00 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dl_test_test1, 0, 0, IS_VOID, 0) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dl_test_test1, 0, 0, IS_VOID, 0)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()

View file

@ -1,6 +1,9 @@
<?php <?php
/** @generate-class-entries */ /**
* @generate-class-entries
* @undocumentable
*/
function test1(): void {} function test1(): void {}

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead. /* This is a generated file, edit the .stub.php file instead.
* Stub hash: efdd79c2c8ccff694699c86fdd6248a13839c744 */ * Stub hash: 54b0ffc3af871b189435266df516f7575c1b9675 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_test1, 0, 0, IS_VOID, 0) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_test1, 0, 0, IS_VOID, 0)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()

View file

@ -212,6 +212,7 @@ function gzwrite($stream, string $data, ?int $length = null): int|false {}
/** /**
* @param resource $stream * @param resource $stream
* @alias fwrite * @alias fwrite
* @undocumentable gzputs is an alias of gzwrite, but transitive aliases are not yet supported
*/ */
function gzputs($stream, string $data, ?int $length = null): int|false {} function gzputs($stream, string $data, ?int $length = null): int|false {}

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead. /* This is a generated file, edit the .stub.php file instead.
* Stub hash: 338e64b821dc3f27b6d1d262a90cb7c144ed78b6 */ * Stub hash: 3660ad3239f93c84b6909c36ddfcc92dd0773c70 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ob_gzhandler, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ob_gzhandler, 0, 2, MAY_BE_STRING|MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)