-
-
Save beberlei/7bd3809df1e8edaeb5df5e7adaec40e2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<psalm> | |
<!-- ... --> | |
<plugins> | |
<plugin filename="tools/psalm/TidewaysPsalmPlugin.php" /> | |
</plugins> | |
</psalm> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// This file is BSD-2 licenses | |
// Copyright 2020-current Tideways GmbH | |
use Psalm\Plugin\PluginEntryPointInterface; | |
use Psalm\Plugin\RegistrationInterface; | |
use Psalm\Plugin\Hook\AfterExpressionAnalysisInterface; | |
use Psalm\IssueBuffer; | |
use Psalm\Issue\PluginIssue; | |
use Psalm\CodeLocation; | |
class TidewaysPsalmPlugin implements PluginEntryPointInterface, AfterExpressionAnalysisInterface | |
{ | |
public function __invoke(RegistrationInterface $psalm, ?SimpleXMLElement $config = null) : void | |
{ | |
} | |
static public function afterExpressionAnalysis( | |
PhpParser\Node\Expr $expr, | |
Psalm\Context $context, | |
Psalm\StatementsSource $statements_source, | |
Psalm\Codebase $codebase, | |
array &$file_replacements = [] | |
) : ?bool | |
{ | |
if ($expr instanceof PhpParser\Node\Expr\Isset_ && | |
$expr->vars[0] instanceof PhpParser\Node\Expr\Variable) { | |
IssueBuffer::accepts( | |
new IssetWithNonComplexVariable( | |
new CodeLocation($statements_source, $expr->vars[0]) | |
), | |
$statements_source->getSuppressedIssues() | |
); | |
} | |
if ($expr instanceof PHPParser\Node\Expr\Empty_ && | |
$expr->expr instanceof PhpParser\Node\Expr\Variable) { | |
$type = $statements_source->getNodeTypeProvider()->getType($expr->expr); | |
if ($type !== null && $type->isInt()) { | |
IssueBuffer::accepts( | |
new ReplaceEmptyWithMoreSpecificCondition( | |
'$' . $expr->expr->name . ' === 0', | |
new CodeLocation($statements_source, $expr) | |
) | |
); | |
} elseif ($type !== null && $type->isString()) { | |
IssueBuffer::accepts( | |
new ReplaceEmptyWithMoreSpecificCondition( | |
'strlen($' . $expr->expr->name . ') === 0', | |
new CodeLocation($statements_source, $expr) | |
) | |
); | |
} elseif ($type !== null && $type->hasArray()) { | |
IssueBuffer::accepts( | |
new ReplaceEmptyWithMoreSpecificCondition( | |
'count($' . $expr->expr->name . ') === 0', | |
new CodeLocation($statements_source, $expr) | |
) | |
); | |
} else { | |
IssueBuffer::accepts( | |
new ReplaceEmptyWithMoreSpecificCondition( | |
'no suggestion possible for now', | |
new CodeLocation($statements_source, $expr) | |
) | |
); | |
} | |
} | |
return null; | |
} | |
} | |
class IssetWithNonComplexVariable extends PluginIssue | |
{ | |
public function __construct(CodeLocation $code_location) | |
{ | |
parent::__construct('Do not use isset on non-complex variables to test for (non-)emptiness.', $code_location); | |
} | |
} | |
class ReplaceEmptyWithMoreSpecificCondition extends PluginIssue | |
{ | |
public function __construct(string $replacement, CodeLocation $code_location) | |
{ | |
parent::__construct('Replace use of empty() with more specific condition: ' . $replacement, $code_location); | |
} | |
} |
Hi, I'm building a set of psalm plugins to help automatically migrate legacy projects. Do you mind if I publish a fully fleshed plugin based on this idea but with automatic code replacement when it is safe to do so?
@orklah yes please you may take the code, I'll update the gist to indicate a BSD-2 license.
Thanks! In progress at https://github.com/orklah/psalm-not-empty
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I also could be
'$' . $expr->expr->name . " === ''"
instead of'strlen($' . $expr->expr->name . ') === 0'
.