first commit

This commit is contained in:
orklah 2021-02-21 09:25:52 +01:00
parent 84d4f2d352
commit 6013ab1fda
4 changed files with 137 additions and 0 deletions

13
.editorconfig Normal file
View File

@ -0,0 +1,13 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.{yaml,yml}]
indent_size = 2

32
composer.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "orklah/psalm-no-empty",
"description": "Automatically change empty() into a more explicit expression",
"type": "psalm-plugin",
"minimum-stability": "dev",
"prefer-stable": true,
"license": "MIT",
"authors": [
{
"name": "orklah"
}
],
"extra": {
"psalm" : {
"pluginClass": "Orklah\\NotEmpty\\Plugin"
}
},
"require": {
"php": "^7.3",
"vimeo/psalm": "master"
},
"autoload": {
"psr-4": {
"Orklah\\NotEmpty\\": ["./src"]
}
},
"require-dev": {
"nikic/php-parser": "^4.0",
"phpunit/phpunit": "^9.5",
"psalm/plugin-phpunit": "^0.15.0"
}
}

View File

@ -0,0 +1,75 @@
<?php declare(strict_types=1);
namespace Orklah\NotEmpty\Hooks;
use PhpParser\Node\Expr\Empty_;
use PhpParser\Node\Expr\Variable;
use Psalm\FileManipulation;
use Psalm\Plugin\EventHandler\AfterExpressionAnalysisInterface;
use Psalm\Plugin\EventHandler\Event\AfterExpressionAnalysisEvent;
use Psalm\Type\Atomic;
use function get_class;
class NotEmptyHooks implements AfterExpressionAnalysisInterface
{
public static function afterExpressionAnalysis(AfterExpressionAnalysisEvent $event): ?bool
{
if (!$event->getCodebase()->alter_code) {
return true;
}
$expr = $event->getExpr();
$node_provider = $event->getStatementsSource()->getNodeTypeProvider();
if (!$expr instanceof Empty_) {
return true;
}
if ($expr->expr instanceof Variable) {
return true;
}
$type = $node_provider->getType($expr->expr);
if ($type === null) {
return true;
}
if ($type->from_docblock) {
//TODO: maybe add an issue in non alter mode
return true;
}
if (!$type->isSingle()) {
// TODO: add functionnality with isSingleAndMaybeNullable and add || EXPR !== null
return true;
}
$startPos = $expr->getStartFilePos() + 1;
$endPos = $expr->getEndFilePos();
$atomic_types = $type->getAtomicTypes();
$atomic_type = array_shift($atomic_types);
$display_expr = (string)$expr->expr;
if ($atomic_type instanceof Atomic\TInt) {
$file_manipulation = new FileManipulation($startPos, $endPos, $display_expr . " === 0");
$event->setFileReplacements([$file_manipulation]);
} elseif ($atomic_type instanceof Atomic\TFloat) {
$file_manipulation = new FileManipulation($startPos, $endPos, $display_expr . " === 0.0");
$event->setFileReplacements([$file_manipulation]);
} elseif ($atomic_type instanceof Atomic\TString) {
$file_manipulation = new FileManipulation($startPos, $endPos, "(" . $display_expr . " === '' || " . $display_expr . " === '0')");
$event->setFileReplacements([$file_manipulation]);
} elseif ($atomic_type instanceof Atomic\TArray) {
$file_manipulation = new FileManipulation($startPos, $endPos, $display_expr . " === []");
$event->setFileReplacements([$file_manipulation]);
} elseif ($atomic_type instanceof Atomic\TBool) {
$file_manipulation = new FileManipulation($startPos, $endPos, $display_expr . " === false");
$event->setFileReplacements([$file_manipulation]);
} else {
var_dump(get_class($atomic_type));
}
return true;
}
}

17
src/Plugin.php Normal file
View File

@ -0,0 +1,17 @@
<?php declare(strict_types=1);
namespace Orklah\NotEmpty;
use Orklah\NotEmpty\Hooks\NotEmptyHooks;
use SimpleXMLElement;
use Psalm\Plugin\PluginEntryPointInterface;
use Psalm\Plugin\RegistrationInterface;
class Plugin implements PluginEntryPointInterface
{
public function __invoke(RegistrationInterface $registration, ?SimpleXMLElement $config = null): void
{
if(class_exists(NotEmptyHooks::class)){
$registration->registerHooksFromClass(NotEmptyHooks::class);
}
}
}