Commit c5ff8b8e authored by Christian Kuhn's avatar Christian Kuhn
Browse files

[TASK] Remove docBlockChecker integrity test

We have the annotationChecker and the ever growing
phpstan ruleset that scan our codebase for code and
documentation code block violations.

For the additional script docBlockChecker.php it's
unclear what it actually does and when it should fail.
It never went red to my knowledge. The patch author
didn't know what was going on and I've been unable to
make the script fail with local tests. The mentioned
phpdocumentor/phpdocumentor seems to have never
materialized in our world, and tools like api.typo3.org
rely on doxygen.

The patch drops that integrity test script and removes
usages in runTests.sh and gitlab-ci.

Resolves: #94735
Related: #89023
Releases: master
Change-Id: Id7744780cd50e2009d59bd0f040799f8081924e7
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/70277


Reviewed-by: Wouter Wolters's avatarWouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Jochen's avatarJochen <rothjochen@gmail.com>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Wouter Wolters's avatarWouter Wolters <typo3@wouterwolters.nl>
Tested-by: Jochen's avatarJochen <rothjochen@gmail.com>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
parent 0cf052aa
#!/usr/bin/env php
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use phpDocumentor\Reflection\DocBlockFactory;
use PhpParser\Error;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\ParserFactory;
use Symfony\Component\Console\Output\ConsoleOutput;
require_once __DIR__ . '/../../vendor/autoload.php';
class NodeVisitor implements \PhpParser\NodeVisitor
{
/**
* @var DocBlockFactory
*/
private $docBlockFactory;
/**
* @var string|null
*/
public $namespace;
/**
* @var string|null
*/
public $className;
/**
* @var string|null
*/
public $classCommentError;
/**
* @var array
*/
public $properties = [];
/**
* @var array
*/
public $methods = [];
/**
* @var bool
*/
public $hasErrors = false;
/**
* Called once before traversal.
*
* Return value semantics:
* * null: $nodes stays as-is
* * otherwise: $nodes is set to the return value
*
* @param Node[] $nodes Array of nodes
*
* @return Node[]|null Array of nodes
*/
public function beforeTraverse(array $nodes)
{
$this->docBlockFactory = DocBlockFactory::createInstance();
return null;
}
/**
* Called when entering a node.
*
* Return value semantics:
* * null
* => $node stays as-is
* * NodeTraverser::DONT_TRAVERSE_CHILDREN
* => Children of $node are not traversed. $node stays as-is
* * NodeTraverser::STOP_TRAVERSAL
* => Traversal is aborted. $node stays as-is
* * otherwise
* => $node is set to the return value
*
* @param Node $node Node
*
* @return int|Node|null Replacement node (or special return value)
*/
public function enterNode(Node $node)
{
switch (get_class($node)) {
case Node\Stmt\Namespace_::class:
/** @var Node\Stmt\Namespace_ $node */
$this->namespace = (string)$node->name;
break;
case Node\Stmt\Class_::class:
/** @var Node\Stmt\Class_ $node */
$this->className = (string)$node->name;
try {
$docComment = $node->getDocComment();
if ($docComment instanceof \PhpParser\Comment) {
$this->docBlockFactory->create($docComment->getText());
}
} catch (\Throwable $e) {
$this->hasErrors = true;
$this->classCommentError = $e->getMessage();
}
break;
case Node\Stmt\Property::class:
/** @var Node\Stmt\Property $node */
$property = [
'name' => (string)$node->props[0]->name,
'error' => null
];
try {
$docComment = $node->getDocComment();
if ($docComment instanceof \PhpParser\Comment) {
$this->docBlockFactory->create($docComment->getText());
}
} catch (\Throwable $e) {
$this->hasErrors = true;
$property['error'] = $e->getMessage();
}
$this->properties[] = $property;
break;
case Node\Stmt\ClassMethod::class:
/** @var Node\Stmt\ClassMethod $node */
$method = [
'name' => (string)$node->name,
'error' => null
];
try {
$docComment = $node->getDocComment();
if ($docComment instanceof \PhpParser\Comment) {
$this->docBlockFactory->create($docComment->getText());
}
} catch (\Throwable $e) {
$this->hasErrors = true;
$method['error'] = $e->getMessage();
}
$this->methods[] = $method;
break;
default:
break;
}
return null;
}
/**
* Called when leaving a node.
*
* Return value semantics:
* * null
* => $node stays as-is
* * NodeTraverser::REMOVE_NODE
* => $node is removed from the parent array
* * NodeTraverser::STOP_TRAVERSAL
* => Traversal is aborted. $node stays as-is
* * array (of Nodes)
* => The return value is merged into the parent array (at the position of the $node)
* * otherwise
* => $node is set to the return value
*
* @param Node $node Node
*
* @return int|Node|Node[]|null Replacement node (or special return value)
*/
public function leaveNode(Node $node)
{
return null;
}
/**
* Called once after traversal.
*
* Return value semantics:
* * null: $nodes stays as-is
* * otherwise: $nodes is set to the return value
*
* @param Node[] $nodes Array of nodes
*
* @return Node[]|null Array of nodes
*/
public function afterTraverse(array $nodes)
{
return null;
}
}
$parser = (new ParserFactory())->create(ParserFactory::ONLY_PHP7);
$finder = new Symfony\Component\Finder\Finder();
$finder->files()
->in(__DIR__ . '/../../typo3/sysext/*/Classes/')
->in(__DIR__ . '/../../typo3/sysext/*/Tests/')
->notPath('_generated')
->name('/\.php$/')
// ->notName('ServiceProviderRegistry.php')
;
$output = new ConsoleOutput();
$errors = [];
foreach ($finder as $file) {
try {
$ast = $parser->parse($file->getContents());
} catch (Error $error) {
$output->writeln('<error>Parse error: ' . $error->getMessage() . '</error>');
exit(1);
}
$visitor = new NodeVisitor();
$traverser = new NodeTraverser();
$traverser->addVisitor($visitor);
try {
$ast = $traverser->traverse($ast);
} catch (\Throwable $e) {
$errors[$file->getRealPath()]['error'] = $e->getMessage();
$output->write('<error>F</error>');
continue;
}
if ($visitor->className === null || $visitor->namespace === null) {
// only process files that contain classes for now
continue;
}
if ($visitor->hasErrors) {
$errors[$file->getRealPath()]['fqcn'] = $visitor->namespace . '\\' . $visitor->className;
if ($visitor->classCommentError !== null) {
$errors[$file->getRealPath()]['class'] = $visitor->classCommentError;
}
foreach ($visitor->properties as $property) {
if (empty($property['error'])) {
continue;
}
$errors[$file->getRealPath()]['properties'][$property['name']] = $property['error'];
}
foreach ($visitor->methods as $method) {
if (empty($method['error'])) {
continue;
}
$errors[$file->getRealPath()]['methods'][$method['name']] = $method['error'];
}
$output->write('<error>F</error>');
} else {
$output->write('<fg=green>.</>');
}
}
$output->writeln('');
if (!empty($errors)) {
foreach ($errors as $file => $errorsInFile) {
$output->writeln('');
$output->writeln('');
$output->writeln('<error>' . $file . '</error>');
$output->writeln('</>');
if (isset($errorsInFile['class'])) {
$table = new \Symfony\Component\Console\Helper\Table($output);
$table->setHeaders(['Class', 'Errors']);
$table->addRow([$errorsInFile['fqcn'], $errorsInFile['class']]);
$table->setStyle('borderless');
$table->render();
}
$properties = $errorsInFile['properties'] ?? [];
if (count($properties)) {
$table = new \Symfony\Component\Console\Helper\Table($output);
$table->setHeaders(['Properties', 'Errors']);
foreach ($properties as $propertyName => $error) {
$table->addRow([$errorsInFile['fqcn'] . '::' . $propertyName, $error]);
}
$table->setStyle('borderless');
$table->render();
}
$methods = $errorsInFile['methods'] ?? [];
if (count($methods)) {
$table = new \Symfony\Component\Console\Helper\Table($output);
$table->setHeaders(['Methods', 'Errors']);
foreach ($methods as $methodName => $error) {
$table->addRow([$errorsInFile['fqcn'] . '::' . $methodName . '()', $error]);
}
$table->setStyle('borderless');
$table->render();
}
}
exit(1);
}
exit(0);
......@@ -108,7 +108,6 @@ Options:
- composerInstallMax: "composer update", with no platform.php config.
- composerInstallMin: "composer update --prefer-lowest", with platform.php set to PHP version x.x.0.
- composerValidate: "composer validate"
- docBlockCheck: Scan php doc blocks for validity
- fixCsvFixtures: fix broken functional test csv fixtures
- functional: functional tests
- install: installation acceptance tests, only with -d mariadb|postgres|sqlite
......@@ -569,12 +568,6 @@ case ${TEST_SUITE} in
SUITE_EXIT_CODE=$?
docker-compose down
;;
docBlockCheck)
setUpDockerComposeDotEnv
docker-compose run doc_block_check
SUITE_EXIT_CODE=$?
docker-compose down
;;
fixCsvFixtures)
setUpDockerComposeDotEnv
docker-compose run fix_csv_fixtures
......
......@@ -21,14 +21,6 @@ composer validate:
script:
- Build/Scripts/runTests.sh -s composerValidate -p 7.4
doc block check php 7.4:
stage: integrity
only:
- schedules
script:
- Build/Scripts/runTests.sh -s composerInstall -p 7.4
- Build/Scripts/runTests.sh -s docBlockCheck -p 7.4
grunt clean:
stage: integrity
only:
......
......@@ -8,16 +8,6 @@ annotations php 7.4 pre-merge:
- Build/Scripts/runTests.sh -s composerInstall -p 7.4
- Build/Scripts/runTests.sh -s checkAnnotations -p 7.4
doc block check php 7.4 pre-merge:
stage: main
except:
refs:
- schedules
- master
script:
- Build/Scripts/runTests.sh -s composerInstall -p 7.4
- Build/Scripts/runTests.sh -s docBlockCheck -p 7.4
grunt clean pre-merge:
stage: main
except:
......
......@@ -777,20 +777,6 @@ services:
composer validate;
"
doc_block_check:
image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest
user: "${HOST_UID}"
volumes:
- ${CORE_ROOT}:${CORE_ROOT}
working_dir: ${CORE_ROOT}
command: >
/bin/sh -c "
if [ ${SCRIPT_VERBOSE} -eq 1 ]; then
set -x
fi
php -dxdebug.mode=off Build/Scripts/docBlockChecker.php;
"
functional_split:
image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest
user: "${HOST_UID}"
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment