mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Add special type for SQL select strings for plugins to consume
This commit is contained in:
parent
0b74c6a6e7
commit
5beb26659e
@ -23,7 +23,8 @@
|
|||||||
"webmozart/path-util": "^2.3",
|
"webmozart/path-util": "^2.3",
|
||||||
"symfony/console": "^3.0||^4.0",
|
"symfony/console": "^3.0||^4.0",
|
||||||
"amphp/amp": "^2.1",
|
"amphp/amp": "^2.1",
|
||||||
"amphp/byte-stream": "^1.5"
|
"amphp/byte-stream": "^1.5",
|
||||||
|
"phpmyadmin/sql-parser": "^4.0"
|
||||||
},
|
},
|
||||||
"bin": ["psalm", "psalter", "psalm-language-server", "psalm-plugin"],
|
"bin": ["psalm", "psalter", "psalm-language-server", "psalm-plugin"],
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -876,7 +876,8 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
|||||||
$stmt->name->name,
|
$stmt->name->name,
|
||||||
$stmt->args,
|
$stmt->args,
|
||||||
$context,
|
$context,
|
||||||
new CodeLocation($statements_analyzer->getSource(), $stmt->name)
|
new CodeLocation($statements_analyzer->getSource(), $stmt->name),
|
||||||
|
$lhs_type_part instanceof TGenericObject ? $lhs_type_part->type_params : null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ class ExpressionAnalyzer
|
|||||||
} elseif ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
|
} elseif ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
|
||||||
ConstFetchAnalyzer::analyze($statements_analyzer, $stmt, $context);
|
ConstFetchAnalyzer::analyze($statements_analyzer, $stmt, $context);
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Scalar\String_) {
|
} elseif ($stmt instanceof PhpParser\Node\Scalar\String_) {
|
||||||
$stmt->inferredType = Type::getString(strlen($stmt->value) < 50 ? $stmt->value : null);
|
$stmt->inferredType = Type::getString($stmt->value);
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Scalar\EncapsedStringPart) {
|
} elseif ($stmt instanceof PhpParser\Node\Scalar\EncapsedStringPart) {
|
||||||
// do nothing
|
// do nothing
|
||||||
} elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst) {
|
} elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst) {
|
||||||
|
@ -20,7 +20,8 @@ class MethodReturnTypeProvider
|
|||||||
* string,
|
* string,
|
||||||
* array<PhpParser\Node\Arg>,
|
* array<PhpParser\Node\Arg>,
|
||||||
* Context,
|
* Context,
|
||||||
* CodeLocation
|
* CodeLocation,
|
||||||
|
* ?array<Type\Union>=
|
||||||
* ) : ?Type\Union>
|
* ) : ?Type\Union>
|
||||||
* >
|
* >
|
||||||
*/
|
*/
|
||||||
@ -68,7 +69,8 @@ class MethodReturnTypeProvider
|
|||||||
* string,
|
* string,
|
||||||
* array<PhpParser\Node\Arg>,
|
* array<PhpParser\Node\Arg>,
|
||||||
* Context,
|
* Context,
|
||||||
* CodeLocation
|
* CodeLocation,
|
||||||
|
* ?array<Type\Union>=
|
||||||
* ) : ?Type\Union $c
|
* ) : ?Type\Union $c
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
@ -85,6 +87,7 @@ class MethodReturnTypeProvider
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<PhpParser\Node\Arg> $call_args
|
* @param array<PhpParser\Node\Arg> $call_args
|
||||||
|
* @param ?array<Type\Union> $template_type_parameters
|
||||||
* @return ?Type\Union
|
* @return ?Type\Union
|
||||||
*/
|
*/
|
||||||
public function getReturnType(
|
public function getReturnType(
|
||||||
@ -93,7 +96,8 @@ class MethodReturnTypeProvider
|
|||||||
string $method_name,
|
string $method_name,
|
||||||
array $call_args,
|
array $call_args,
|
||||||
Context $context,
|
Context $context,
|
||||||
CodeLocation $code_location
|
CodeLocation $code_location,
|
||||||
|
array $template_type_parameters = null
|
||||||
) {
|
) {
|
||||||
foreach (self::$handlers[strtolower($fq_classlike_name)] as $class_handler) {
|
foreach (self::$handlers[strtolower($fq_classlike_name)] as $class_handler) {
|
||||||
$result = $class_handler(
|
$result = $class_handler(
|
||||||
@ -102,7 +106,8 @@ class MethodReturnTypeProvider
|
|||||||
strtolower($method_name),
|
strtolower($method_name),
|
||||||
$call_args,
|
$call_args,
|
||||||
$context,
|
$context,
|
||||||
$code_location
|
$code_location,
|
||||||
|
$template_type_parameters
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
|
@ -25,7 +25,8 @@ class DomNodeAppendChild implements \Psalm\Plugin\Hook\MethodReturnTypeProviderI
|
|||||||
string $method_name_lowercase,
|
string $method_name_lowercase,
|
||||||
array $call_args,
|
array $call_args,
|
||||||
Context $context,
|
Context $context,
|
||||||
CodeLocation $code_location
|
CodeLocation $code_location,
|
||||||
|
array $template_type_parameters = null
|
||||||
) {
|
) {
|
||||||
if ($method_name_lowercase === 'appendchild'
|
if ($method_name_lowercase === 'appendchild'
|
||||||
&& isset($call_args[0]->value->inferredType)
|
&& isset($call_args[0]->value->inferredType)
|
||||||
|
@ -25,7 +25,8 @@ class SimpleXmlElementAsXml implements \Psalm\Plugin\Hook\MethodReturnTypeProvid
|
|||||||
string $method_name_lowercase,
|
string $method_name_lowercase,
|
||||||
array $call_args,
|
array $call_args,
|
||||||
Context $context,
|
Context $context,
|
||||||
CodeLocation $code_location
|
CodeLocation $code_location,
|
||||||
|
array $template_type_parameters = null
|
||||||
) {
|
) {
|
||||||
if ($method_name_lowercase === 'asxml'
|
if ($method_name_lowercase === 'asxml'
|
||||||
&& !count($call_args)
|
&& !count($call_args)
|
||||||
|
@ -17,6 +17,7 @@ interface MethodReturnTypeProviderInterface
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<PhpParser\Node\Arg> $call_args
|
* @param array<PhpParser\Node\Arg> $call_args
|
||||||
|
* @param ?array<Type\Union> $template_type_parameters
|
||||||
* @return ?Type\Union
|
* @return ?Type\Union
|
||||||
*/
|
*/
|
||||||
public static function getMethodReturnType(
|
public static function getMethodReturnType(
|
||||||
@ -25,6 +26,7 @@ interface MethodReturnTypeProviderInterface
|
|||||||
string $method_name_lowercase,
|
string $method_name_lowercase,
|
||||||
array $call_args,
|
array $call_args,
|
||||||
Context $context,
|
Context $context,
|
||||||
CodeLocation $code_location
|
CodeLocation $code_location,
|
||||||
|
array $template_type_parameters = null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ use Psalm\Type\Atomic\TObject;
|
|||||||
use Psalm\Type\Atomic\TObjectWithProperties;
|
use Psalm\Type\Atomic\TObjectWithProperties;
|
||||||
use Psalm\Type\Atomic\TResource;
|
use Psalm\Type\Atomic\TResource;
|
||||||
use Psalm\Type\Atomic\TSingleLetter;
|
use Psalm\Type\Atomic\TSingleLetter;
|
||||||
|
use Psalm\Type\Atomic\TSqlSelectString;
|
||||||
use Psalm\Type\Atomic\TString;
|
use Psalm\Type\Atomic\TString;
|
||||||
use Psalm\Type\Atomic\TTrue;
|
use Psalm\Type\Atomic\TTrue;
|
||||||
use Psalm\Type\Atomic\TVoid;
|
use Psalm\Type\Atomic\TVoid;
|
||||||
@ -923,9 +924,23 @@ abstract class Type
|
|||||||
*/
|
*/
|
||||||
public static function getString($value = null)
|
public static function getString($value = null)
|
||||||
{
|
{
|
||||||
|
$type = null;
|
||||||
|
|
||||||
if ($value !== null) {
|
if ($value !== null) {
|
||||||
$type = new TLiteralString($value);
|
if (stripos($value, 'select ') === 0) {
|
||||||
} else {
|
$parser = new \PhpMyAdmin\SqlParser\Parser($value);
|
||||||
|
|
||||||
|
if (!$parser->errors) {
|
||||||
|
$type = new TSqlSelectString($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$type && strlen($value) < 50) {
|
||||||
|
$type = new TLiteralString($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$type) {
|
||||||
$type = new TString();
|
$type = new TString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
29
src/Psalm/Type/Atomic/TSqlSelectString.php
Normal file
29
src/Psalm/Type/Atomic/TSqlSelectString.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
namespace Psalm\Type\Atomic;
|
||||||
|
|
||||||
|
class TSqlSelectString extends TLiteralString
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getKey()
|
||||||
|
{
|
||||||
|
return 'sql-select-string';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return 'sql-select-string(' . $this->value . ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function canBeFullyExpressedInPhp()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -78,7 +78,8 @@ class FooMethodProvider implements
|
|||||||
string $method_name,
|
string $method_name,
|
||||||
array $call_args,
|
array $call_args,
|
||||||
Context $context,
|
Context $context,
|
||||||
CodeLocation $code_location
|
CodeLocation $code_location,
|
||||||
|
array $templated_type_parameters = null
|
||||||
) {
|
) {
|
||||||
return Type::getString();
|
return Type::getString();
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,19 @@ trait ValidCodeAnalysisTestTrait
|
|||||||
|
|
||||||
$actual_vars = [];
|
$actual_vars = [];
|
||||||
foreach ($assertions as $var => $_) {
|
foreach ($assertions as $var => $_) {
|
||||||
|
$exact = false;
|
||||||
|
|
||||||
|
if ($var && strpos($var, '===') === strlen($var) - 3) {
|
||||||
|
$var = substr($var, 0, -3);
|
||||||
|
$exact = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($context->vars_in_scope[$var])) {
|
if (isset($context->vars_in_scope[$var])) {
|
||||||
$actual_vars[$var] = (string)$context->vars_in_scope[$var];
|
if ($exact) {
|
||||||
|
$actual_vars[$var . '==='] = $context->vars_in_scope[$var]->getId();
|
||||||
|
} else {
|
||||||
|
$actual_vars[$var] = (string)$context->vars_in_scope[$var];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,6 +468,17 @@ class ValueTest extends TestCase
|
|||||||
'assertions' => [],
|
'assertions' => [],
|
||||||
'error_levels' => ['MissingParamType', 'MixedAssignment'],
|
'error_levels' => ['MissingParamType', 'MixedAssignment'],
|
||||||
],
|
],
|
||||||
|
'sqlTypes' => [
|
||||||
|
'<?php
|
||||||
|
$a = "select * from foo";
|
||||||
|
$b = "select * from";
|
||||||
|
$c = "select * from foo where i = :i";',
|
||||||
|
'assertions' => [
|
||||||
|
'$a===' => 'sql-select-string(select * from foo)',
|
||||||
|
'$b===' => 'string(select * from)',
|
||||||
|
'$c===' => 'sql-select-string(select * from foo where i = :i)',
|
||||||
|
]
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user