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",
|
||||
"symfony/console": "^3.0||^4.0",
|
||||
"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"],
|
||||
"autoload": {
|
||||
|
@ -876,7 +876,8 @@ class MethodCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\
|
||||
$stmt->name->name,
|
||||
$stmt->args,
|
||||
$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) {
|
||||
ConstFetchAnalyzer::analyze($statements_analyzer, $stmt, $context);
|
||||
} 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) {
|
||||
// do nothing
|
||||
} elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst) {
|
||||
|
@ -20,7 +20,8 @@ class MethodReturnTypeProvider
|
||||
* string,
|
||||
* array<PhpParser\Node\Arg>,
|
||||
* Context,
|
||||
* CodeLocation
|
||||
* CodeLocation,
|
||||
* ?array<Type\Union>=
|
||||
* ) : ?Type\Union>
|
||||
* >
|
||||
*/
|
||||
@ -68,7 +69,8 @@ class MethodReturnTypeProvider
|
||||
* string,
|
||||
* array<PhpParser\Node\Arg>,
|
||||
* Context,
|
||||
* CodeLocation
|
||||
* CodeLocation,
|
||||
* ?array<Type\Union>=
|
||||
* ) : ?Type\Union $c
|
||||
*
|
||||
* @return void
|
||||
@ -85,6 +87,7 @@ class MethodReturnTypeProvider
|
||||
|
||||
/**
|
||||
* @param array<PhpParser\Node\Arg> $call_args
|
||||
* @param ?array<Type\Union> $template_type_parameters
|
||||
* @return ?Type\Union
|
||||
*/
|
||||
public function getReturnType(
|
||||
@ -93,7 +96,8 @@ class MethodReturnTypeProvider
|
||||
string $method_name,
|
||||
array $call_args,
|
||||
Context $context,
|
||||
CodeLocation $code_location
|
||||
CodeLocation $code_location,
|
||||
array $template_type_parameters = null
|
||||
) {
|
||||
foreach (self::$handlers[strtolower($fq_classlike_name)] as $class_handler) {
|
||||
$result = $class_handler(
|
||||
@ -102,7 +106,8 @@ class MethodReturnTypeProvider
|
||||
strtolower($method_name),
|
||||
$call_args,
|
||||
$context,
|
||||
$code_location
|
||||
$code_location,
|
||||
$template_type_parameters
|
||||
);
|
||||
|
||||
if ($result) {
|
||||
|
@ -25,7 +25,8 @@ class DomNodeAppendChild implements \Psalm\Plugin\Hook\MethodReturnTypeProviderI
|
||||
string $method_name_lowercase,
|
||||
array $call_args,
|
||||
Context $context,
|
||||
CodeLocation $code_location
|
||||
CodeLocation $code_location,
|
||||
array $template_type_parameters = null
|
||||
) {
|
||||
if ($method_name_lowercase === 'appendchild'
|
||||
&& isset($call_args[0]->value->inferredType)
|
||||
|
@ -25,7 +25,8 @@ class SimpleXmlElementAsXml implements \Psalm\Plugin\Hook\MethodReturnTypeProvid
|
||||
string $method_name_lowercase,
|
||||
array $call_args,
|
||||
Context $context,
|
||||
CodeLocation $code_location
|
||||
CodeLocation $code_location,
|
||||
array $template_type_parameters = null
|
||||
) {
|
||||
if ($method_name_lowercase === 'asxml'
|
||||
&& !count($call_args)
|
||||
|
@ -17,6 +17,7 @@ interface MethodReturnTypeProviderInterface
|
||||
|
||||
/**
|
||||
* @param array<PhpParser\Node\Arg> $call_args
|
||||
* @param ?array<Type\Union> $template_type_parameters
|
||||
* @return ?Type\Union
|
||||
*/
|
||||
public static function getMethodReturnType(
|
||||
@ -25,6 +26,7 @@ interface MethodReturnTypeProviderInterface
|
||||
string $method_name_lowercase,
|
||||
array $call_args,
|
||||
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\TResource;
|
||||
use Psalm\Type\Atomic\TSingleLetter;
|
||||
use Psalm\Type\Atomic\TSqlSelectString;
|
||||
use Psalm\Type\Atomic\TString;
|
||||
use Psalm\Type\Atomic\TTrue;
|
||||
use Psalm\Type\Atomic\TVoid;
|
||||
@ -923,9 +924,23 @@ abstract class Type
|
||||
*/
|
||||
public static function getString($value = null)
|
||||
{
|
||||
$type = null;
|
||||
|
||||
if ($value !== null) {
|
||||
$type = new TLiteralString($value);
|
||||
} else {
|
||||
if (stripos($value, 'select ') === 0) {
|
||||
$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();
|
||||
}
|
||||
|
||||
|
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,
|
||||
array $call_args,
|
||||
Context $context,
|
||||
CodeLocation $code_location
|
||||
CodeLocation $code_location,
|
||||
array $templated_type_parameters = null
|
||||
) {
|
||||
return Type::getString();
|
||||
}
|
||||
|
@ -61,8 +61,19 @@ trait ValidCodeAnalysisTestTrait
|
||||
|
||||
$actual_vars = [];
|
||||
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])) {
|
||||
$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' => [],
|
||||
'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