From 258c919b3c2a5211b4c792c41e37d0bad8d451ea Mon Sep 17 00:00:00 2001 From: Daniel Kesselberg Date: Tue, 17 Jan 2023 10:47:22 +0100 Subject: [PATCH] ci: migrate ocp since checker to psalm Signed-off-by: Daniel Kesselberg --- autotest-checkers.sh | 2 - build/OCPSinceChecker.php | 124 ------------------- build/psalm/OcpSinceChecker.php | 115 +++++++++++++++++ lib/public/Dashboard/RegisterWidgetEvent.php | 4 + lib/public/Files/Storage.php | 4 + lib/public/Group/Backend/ABackend.php | 1 + lib/public/User/Backend/ABackend.php | 1 + psalm-ocp.xml | 1 + 8 files changed, 126 insertions(+), 126 deletions(-) delete mode 100644 build/OCPSinceChecker.php create mode 100644 build/psalm/OcpSinceChecker.php diff --git a/autotest-checkers.sh b/autotest-checkers.sh index 6abce428bc5..ccad1608559 100755 --- a/autotest-checkers.sh +++ b/autotest-checkers.sh @@ -10,8 +10,6 @@ php ./build/triple-dot-checker.php RESULT=$(($RESULT+$?)) php ./build/htaccess-checker.php RESULT=$(($RESULT+$?)) -php ./build/OCPSinceChecker.php -RESULT=$(($RESULT+$?)) php ./build/files-checker.php RESULT=$(($RESULT+$?)) diff --git a/build/OCPSinceChecker.php b/build/OCPSinceChecker.php deleted file mode 100644 index f882d1b7724..00000000000 --- a/build/OCPSinceChecker.php +++ /dev/null @@ -1,124 +0,0 @@ - - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - - -require_once(dirname(__DIR__) . '/3rdparty/autoload.php'); - -/** - * Class SinceTagCheckVisitor - * - * this class checks all methods for the presence of the @since tag - */ -class SinceTagCheckVisitor extends \PhpParser\NodeVisitorAbstract { - /** @var string */ - protected $namespace = ''; - /** @var string */ - protected $className = ''; - /** @var bool */ - protected $deprecatedClass = false; - - /** @var array */ - protected $errors = []; - - public function enterNode(\PhpParser\Node $node) { - if ($this->deprecatedClass) { - return; - } - - if ($node instanceof \PhpParser\Node\Stmt\Namespace_) { - $this->namespace = $node->name; - } - - if ($node instanceof \PhpParser\Node\Stmt\Interface_ or - $node instanceof \PhpParser\Node\Stmt\Class_) { - $this->className = $node->name; - - /** @var \PhpParser\Comment\Doc[] $comments */ - $comments = $node->getAttribute('comments'); - - if (empty($comments)) { - $this->errors[] = 'PHPDoc is needed for ' . $this->namespace . '\\' . $this->className . '::' . $node->name; - return; - } - - $comment = $comments[count($comments) - 1]; - $text = $comment->getText(); - if (strpos($text, '@deprecated') !== false) { - $this->deprecatedClass = true; - } - - if ($this->deprecatedClass === false && strpos($text, '@since') === false && strpos($text, '@deprecated') === false) { - $type = $node instanceof \PhpParser\Node\Stmt\Interface_ ? 'interface' : 'class'; - $this->errors[] = '@since or @deprecated tag is needed in PHPDoc for ' . $type . ' ' . $this->namespace . '\\' . $this->className; - return; - } - } - - if ($node instanceof \PhpParser\Node\Stmt\ClassMethod) { - /** @var \PhpParser\Node\Stmt\ClassMethod $node */ - /** @var \PhpParser\Comment\Doc[] $comments */ - $comments = $node->getAttribute('comments'); - - if (empty($comments)) { - $this->errors[] = 'PHPDoc is needed for ' . $this->namespace . '\\' . $this->className . '::' . $node->name; - return; - } - $comment = $comments[count($comments) - 1]; - $text = $comment->getText(); - if (strpos($text, '@since') === false && strpos($text, '@deprecated') === false) { - $this->errors[] = '@since or @deprecated tag is needed in PHPDoc for ' . $this->namespace . '\\' . $this->className . '::' . $node->name; - return; - } - } - } - - public function getErrors() { - return $this->errors; - } -} - -echo 'Parsing all files in lib/public for the presence of @since or @deprecated on each method...' . PHP_EOL . PHP_EOL; - - -$parser = (new PhpParser\ParserFactory)->create(PhpParser\ParserFactory::PREFER_PHP7); - -/* iterate over all .php files in lib/public */ -$Directory = new RecursiveDirectoryIterator(dirname(__DIR__) . '/lib/public'); -$Iterator = new RecursiveIteratorIterator($Directory); -$Regex = new RegexIterator($Iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH); - -$errors = []; - -foreach ($Regex as $file) { - $stmts = $parser->parse(file_get_contents($file[0])); - - $visitor = new SinceTagCheckVisitor(); - $traverser = new \PhpParser\NodeTraverser(); - $traverser->addVisitor($visitor); - $traverser->traverse($stmts); - - $errors = array_merge($errors, $visitor->getErrors()); -} - -if (count($errors)) { - echo join(PHP_EOL, $errors) . PHP_EOL . PHP_EOL; - exit(1); -} diff --git a/build/psalm/OcpSinceChecker.php b/build/psalm/OcpSinceChecker.php new file mode 100644 index 00000000000..62b555284ac --- /dev/null +++ b/build/psalm/OcpSinceChecker.php @@ -0,0 +1,115 @@ + + * + * @author 2023 Daniel Kesselberg + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\ClassLike; +use Psalm\CodeLocation; +use Psalm\DocComment; +use Psalm\Exception\DocblockParseException; +use Psalm\FileSource; +use Psalm\Issue\InvalidDocblock; +use Psalm\IssueBuffer; +use Psalm\Plugin\EventHandler\Event\AfterClassLikeVisitEvent; + +class OcpSinceChecker implements Psalm\Plugin\EventHandler\AfterClassLikeVisitInterface { + public static function afterClassLikeVisit(AfterClassLikeVisitEvent $event): void { + $stmt = $event->getStmt(); + $statementsSource = $event->getStatementsSource(); + + self::checkClassComment($stmt, $statementsSource); + + foreach ($stmt->getMethods() as $method) { + self::checkMethodComment($method, $statementsSource); + } + } + + private static function checkClassComment(ClassLike $stmt, FileSource $statementsSource): void { + $docblock = $stmt->getDocComment(); + + if ($docblock === null) { + IssueBuffer::maybeAdd( + new InvalidDocblock( + 'PHPDoc is required for classes/interfaces in OCP.', + new CodeLocation($statementsSource, $stmt) + ) + ); + return; + } + + try { + $parsedDocblock = DocComment::parsePreservingLength($docblock); + } catch (DocblockParseException $e) { + IssueBuffer::maybeAdd( + new InvalidDocblock( + $e->getMessage(), + new CodeLocation($statementsSource, $stmt) + ) + ); + return; + } + + if (!isset($parsedDocblock->tags['since'])) { + IssueBuffer::maybeAdd( + new InvalidDocblock( + '@since is required for classes/interfaces in OCP.', + new CodeLocation($statementsSource, $stmt) + ) + ); + } + } + + private static function checkMethodComment(Stmt $stmt, FileSource $statementsSource): void { + $docblock = $stmt->getDocComment(); + + if ($docblock === null) { + IssueBuffer::maybeAdd( + new InvalidDocblock( + 'PHPDoc is required for methods in OCP.', + new CodeLocation($statementsSource, $stmt) + ), + ); + return; + } + + try { + $parsedDocblock = DocComment::parsePreservingLength($docblock); + } catch (DocblockParseException $e) { + IssueBuffer::maybeAdd( + new InvalidDocblock( + $e->getMessage(), + new CodeLocation($statementsSource, $stmt) + ) + ); + return; + } + + if (!isset($parsedDocblock->tags['since'])) { + IssueBuffer::maybeAdd( + new InvalidDocblock( + '@since is required for methods in OCP.', + new CodeLocation($statementsSource, $stmt) + ) + ); + } + } +} diff --git a/lib/public/Dashboard/RegisterWidgetEvent.php b/lib/public/Dashboard/RegisterWidgetEvent.php index 289de8e965d..f0bf049571a 100644 --- a/lib/public/Dashboard/RegisterWidgetEvent.php +++ b/lib/public/Dashboard/RegisterWidgetEvent.php @@ -41,6 +41,10 @@ use OCP\EventDispatcher\Event; class RegisterWidgetEvent extends Event { private $manager; + /** + * @param IManager $manager + * @since 20.0.0 + */ public function __construct(IManager $manager) { parent::__construct(); diff --git a/lib/public/Files/Storage.php b/lib/public/Files/Storage.php index f3812396c5b..a0acb4508d2 100644 --- a/lib/public/Files/Storage.php +++ b/lib/public/Files/Storage.php @@ -462,5 +462,9 @@ interface Storage extends IStorage { */ public function setAvailability($isAvailable); + /** + * @since 12.0.0 + * @return mixed + */ public function needsPartFile(); } diff --git a/lib/public/Group/Backend/ABackend.php b/lib/public/Group/Backend/ABackend.php index e5b7f78ac6a..7f5cf732335 100644 --- a/lib/public/Group/Backend/ABackend.php +++ b/lib/public/Group/Backend/ABackend.php @@ -33,6 +33,7 @@ use OCP\GroupInterface; abstract class ABackend implements GroupInterface { /** * @deprecated 14.0.0 + * @since 14.0.0 * * @param int $actions The action to check for * @return bool diff --git a/lib/public/User/Backend/ABackend.php b/lib/public/User/Backend/ABackend.php index 2b246dc0acf..417b14cfb36 100644 --- a/lib/public/User/Backend/ABackend.php +++ b/lib/public/User/Backend/ABackend.php @@ -36,6 +36,7 @@ use OCP\UserInterface; abstract class ABackend implements IUserBackend, UserInterface { /** * @deprecated 14.0.0 + * @since 14.0.0 * * @param int $actions The action to check for * @return bool diff --git a/psalm-ocp.xml b/psalm-ocp.xml index 03706430c5f..f117afc550f 100644 --- a/psalm-ocp.xml +++ b/psalm-ocp.xml @@ -9,6 +9,7 @@ > +