mirror of
https://github.com/danog/psalm-plugin-symfony.git
synced 2024-11-30 04:29:10 +01:00
Support Psalm v4.0 (#98)
* Support Psalm v4.0 * no message * no message * no message
This commit is contained in:
parent
720e554ff8
commit
6c6b4f53fb
5
.github/workflows/integrate.yaml
vendored
5
.github/workflows/integrate.yaml
vendored
@ -61,7 +61,7 @@ jobs:
|
||||
restore-keys: php-${{ matrix.php-version }}-psalm-
|
||||
|
||||
- name: "Run vimeo/psalm"
|
||||
run: vendor/bin/psalm --find-dead-code --find-unused-psalm-suppress --diff --diff-methods --shepherd --show-info=false --stats --output-format=github
|
||||
run: vendor/bin/psalm --find-dead-code --find-unused-psalm-suppress --shepherd --show-info=false --stats --output-format=github
|
||||
|
||||
tests:
|
||||
name: "Tests"
|
||||
@ -71,10 +71,9 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
- 7.4
|
||||
- nightly
|
||||
|
||||
symfony-version:
|
||||
- 3
|
||||
|
@ -10,10 +10,10 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"php": "^7.3 || ^8.0",
|
||||
"ext-simplexml": "*",
|
||||
"symfony/framework-bundle": "^3.0 || ^4.0 || ^5.0",
|
||||
"vimeo/psalm": "^3.17"
|
||||
"vimeo/psalm": "^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/orm": "^2.7",
|
||||
|
@ -50,7 +50,7 @@ class ConsoleHandler implements AfterMethodCallAnalysisInterface
|
||||
Codebase $codebase,
|
||||
array &$file_replacements = [],
|
||||
Union &$return_type_candidate = null
|
||||
) {
|
||||
): void {
|
||||
switch ($declaring_method_id) {
|
||||
case 'Symfony\Component\Console\Command\Command::addargument':
|
||||
self::analyseArgument($expr->args, $statements_source);
|
||||
|
@ -23,7 +23,7 @@ class ContainerDependencyHandler implements AfterFunctionLikeAnalysisInterface
|
||||
StatementsSource $statements_source,
|
||||
Codebase $codebase,
|
||||
array &$file_replacements = []
|
||||
) {
|
||||
): ?bool {
|
||||
if ($stmt instanceof Node\Stmt\ClassMethod && '__construct' === $stmt->name->name) {
|
||||
foreach ($stmt->params as $param) {
|
||||
if ($param->type instanceof Node\Name && ContainerInterface::class === $param->type->getAttribute('resolvedName')) {
|
||||
@ -34,5 +34,7 @@ class ContainerDependencyHandler implements AfterFunctionLikeAnalysisInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ class ContainerHandler implements AfterMethodCallAnalysisInterface, AfterClassLi
|
||||
Codebase $codebase,
|
||||
array &$file_replacements = [],
|
||||
Union &$return_type_candidate = null
|
||||
) {
|
||||
): void {
|
||||
if (!self::isContainerMethod($declaring_method_id, 'get')) {
|
||||
if (self::isContainerMethod($declaring_method_id, 'getparameter')) {
|
||||
$argument = $expr->args[0]->value;
|
||||
|
@ -37,7 +37,7 @@ class DoctrineRepositoryHandler implements AfterMethodCallAnalysisInterface, Aft
|
||||
Codebase $codebase,
|
||||
array &$file_replacements = [],
|
||||
Union &$return_type_candidate = null
|
||||
) {
|
||||
): void {
|
||||
if (in_array($declaring_method_id, ['Doctrine\ORM\EntityManagerInterface::getrepository', 'Doctrine\Persistence\ObjectManager::getrepository'])) {
|
||||
$entityName = $expr->args[0]->value;
|
||||
if ($entityName instanceof String_) {
|
||||
|
@ -8,6 +8,7 @@ use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
use Psalm\Plugin\Hook\MethodReturnTypeProviderInterface;
|
||||
use Psalm\StatementsSource;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TArray;
|
||||
use Psalm\Type\Atomic\TInt;
|
||||
use Psalm\Type\Atomic\TNull;
|
||||
@ -25,8 +26,17 @@ class HeaderBagHandler implements MethodReturnTypeProviderInterface
|
||||
];
|
||||
}
|
||||
|
||||
public static function getMethodReturnType(StatementsSource $source, string $fq_classlike_name, string $method_name_lowercase, array $call_args, Context $context, CodeLocation $code_location, array $template_type_parameters = null, string $called_fq_classlike_name = null, string $called_method_name_lowercase = null)
|
||||
{
|
||||
public static function getMethodReturnType(
|
||||
StatementsSource $source,
|
||||
string $fq_classlike_name,
|
||||
string $method_name_lowercase,
|
||||
array $call_args,
|
||||
Context $context,
|
||||
CodeLocation $code_location,
|
||||
?array $template_type_parameters = null,
|
||||
?string $called_fq_classlike_name = null,
|
||||
?string $called_method_name_lowercase = null
|
||||
): ?Type\Union {
|
||||
if (HeaderBag::class !== $fq_classlike_name) {
|
||||
return null;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ class Plugin implements PluginEntryPointInterface
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __invoke(RegistrationInterface $api, SimpleXMLElement $config = null)
|
||||
public function __invoke(RegistrationInterface $api, SimpleXMLElement $config = null): void
|
||||
{
|
||||
require_once __DIR__.'/Handler/HeaderBagHandler.php';
|
||||
require_once __DIR__.'/Handler/ContainerHandler.php';
|
||||
|
@ -30,14 +30,23 @@ class CachedTemplatesTainter implements MethodReturnTypeProviderInterface
|
||||
return [Environment::class];
|
||||
}
|
||||
|
||||
public static function getMethodReturnType(StatementsSource $source, string $fq_classlike_name, string $method_name_lowercase, array $call_args, Context $context, CodeLocation $code_location, array $template_type_parameters = null, string $called_fq_classlike_name = null, string $called_method_name_lowercase = null): void
|
||||
{
|
||||
public static function getMethodReturnType(
|
||||
StatementsSource $source,
|
||||
string $fq_classlike_name,
|
||||
string $method_name_lowercase,
|
||||
array $call_args,
|
||||
Context $context,
|
||||
CodeLocation $code_location,
|
||||
?array $template_type_parameters = null,
|
||||
?string $called_fq_classlike_name = null,
|
||||
?string $called_method_name_lowercase = null
|
||||
): ?Union {
|
||||
if (!$source instanceof StatementsAnalyzer) {
|
||||
throw new RuntimeException(sprintf('The %s::%s hook can only be called using a %s.', __CLASS__, __METHOD__, StatementsAnalyzer::class));
|
||||
}
|
||||
|
||||
if ('render' !== $method_name_lowercase) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$fake_method_call = new MethodCall(
|
||||
@ -52,7 +61,7 @@ class CachedTemplatesTainter implements MethodReturnTypeProviderInterface
|
||||
|
||||
$firstArgument = $call_args[0]->value;
|
||||
if (!$firstArgument instanceof String_) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$cacheClassName = CachedTemplatesMapping::getCacheClassName($firstArgument->value);
|
||||
@ -66,5 +75,7 @@ class CachedTemplatesTainter implements MethodReturnTypeProviderInterface
|
||||
$fake_method_call,
|
||||
$context
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,9 @@ namespace Psalm\SymfonyPsalmPlugin\Twig;
|
||||
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Internal\Codebase\TaintFlowGraph;
|
||||
use Psalm\Internal\ControlFlow\ControlFlowNode;
|
||||
use Psalm\Internal\ControlFlow\TaintSink;
|
||||
use Psalm\Internal\ControlFlow\TaintSource;
|
||||
use Psalm\Internal\DataFlow\DataFlowNode;
|
||||
use Psalm\Internal\DataFlow\TaintSink;
|
||||
use Psalm\Internal\DataFlow\TaintSource;
|
||||
use Psalm\Type\TaintKind;
|
||||
use Twig\Node\Expression\FilterExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
@ -18,10 +18,10 @@ use Twig\Source;
|
||||
|
||||
class Context
|
||||
{
|
||||
/** @var array<string, ControlFlowNode> */
|
||||
/** @var array<string, DataFlowNode> */
|
||||
private $unassignedVariables = [];
|
||||
|
||||
/** @var array<string, ControlFlowNode> */
|
||||
/** @var array<string, DataFlowNode> */
|
||||
private $localVariables = [];
|
||||
|
||||
/** @var Source */
|
||||
@ -36,7 +36,7 @@ class Context
|
||||
$this->taint = $taint;
|
||||
}
|
||||
|
||||
public function addSink(Node $node, ControlFlowNode $source): void
|
||||
public function addSink(Node $node, DataFlowNode $source): void
|
||||
{
|
||||
$codeLocation = $this->getNodeLocation($node);
|
||||
|
||||
@ -59,7 +59,7 @@ class Context
|
||||
$this->taint->addPath($source, $sink, 'arg');
|
||||
}
|
||||
|
||||
public function taintVariable(NameExpression $expression): ControlFlowNode
|
||||
public function taintVariable(NameExpression $expression): DataFlowNode
|
||||
{
|
||||
/** @var string $variableName */
|
||||
$variableName = $expression->getAttribute('name');
|
||||
@ -72,7 +72,7 @@ class Context
|
||||
return $this->addVariableUsage($variableName, $sinkNode);
|
||||
}
|
||||
|
||||
public function getTaintDestination(ControlFlowNode $taintSource, FilterExpression $expression): ControlFlowNode
|
||||
public function getTaintDestination(DataFlowNode $taintSource, FilterExpression $expression): DataFlowNode
|
||||
{
|
||||
/** @var string $filterName */
|
||||
$filterName = $expression->getNode('filter')->getAttribute('value');
|
||||
@ -117,7 +117,7 @@ class Context
|
||||
}
|
||||
}
|
||||
|
||||
private function addVariableTaintNode(NameExpression $variableNode): ControlFlowNode
|
||||
private function addVariableTaintNode(NameExpression $variableNode): DataFlowNode
|
||||
{
|
||||
/** @var string $variableName */
|
||||
$variableName = $variableNode->getAttribute('name');
|
||||
@ -128,7 +128,7 @@ class Context
|
||||
return $taintNode;
|
||||
}
|
||||
|
||||
private function addVariableUsage(string $variableName, ControlFlowNode $variableTaint): ControlFlowNode
|
||||
private function addVariableUsage(string $variableName, DataFlowNode $variableTaint): DataFlowNode
|
||||
{
|
||||
if (!isset($this->localVariables[$variableName])) {
|
||||
return $this->unassignedVariables[$variableName] = $variableTaint;
|
||||
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Psalm\SymfonyPsalmPlugin\Twig;
|
||||
|
||||
use Psalm\Internal\ControlFlow\ControlFlowNode;
|
||||
use Psalm\Internal\DataFlow\DataFlowNode;
|
||||
use RuntimeException;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\FilterExpression;
|
||||
@ -47,7 +47,7 @@ class PrintNodeAnalyzer
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getTaintSource(AbstractExpression $expression): ?ControlFlowNode
|
||||
private function getTaintSource(AbstractExpression $expression): ?DataFlowNode
|
||||
{
|
||||
if ($expression instanceof FilterExpression) {
|
||||
/** @var AbstractExpression $filteredExpression */
|
||||
|
@ -6,7 +6,7 @@ namespace Psalm\SymfonyPsalmPlugin\Twig;
|
||||
|
||||
use Psalm\Context as PsalmContext;
|
||||
use Psalm\Internal\Analyzer\FileAnalyzer;
|
||||
use Psalm\Internal\ControlFlow\TaintSource;
|
||||
use Psalm\Internal\DataFlow\DataFlowNode;
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
use Twig\NodeTraverser;
|
||||
@ -17,7 +17,7 @@ class TemplateFileAnalyzer extends FileAnalyzer
|
||||
PsalmContext $file_context = null,
|
||||
bool $preserve_analyzers = false,
|
||||
PsalmContext $global_context = null
|
||||
) {
|
||||
): void {
|
||||
$codebase = $this->project_analyzer->getCodebase();
|
||||
$taint = $codebase->taint_flow_graph;
|
||||
|
||||
@ -52,9 +52,9 @@ class TemplateFileAnalyzer extends FileAnalyzer
|
||||
public static function getTaintNodeForTwigNamedVariable(
|
||||
string $template_id,
|
||||
string $variable_name
|
||||
): TaintSource {
|
||||
): DataFlowNode {
|
||||
$label = $arg_id = strtolower($template_id).'#'.strtolower($variable_name);
|
||||
|
||||
return new TaintSource($arg_id, $label, null, null);
|
||||
return new DataFlowNode($arg_id, $label, null, null);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user