<?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); | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
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? |
This comment has been minimized.
This comment has been minimized.
@orklah yes please you may take the code, I'll update the gist to indicate a BSD-2 license. |
This comment has been minimized.
This comment has been minimized.
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
This comment has been minimized.
I also could be
'$' . $expr->expr->name . " === ''"
instead of'strlen($' . $expr->expr->name . ') === 0'
.