1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Prevent string being used in non-empty-string location

This commit is contained in:
Matthew Brown 2020-01-06 20:57:57 -05:00
parent 07aaa3f99d
commit cc9e0fab67
9 changed files with 54 additions and 36 deletions

View File

@ -561,10 +561,6 @@ class BinaryOpAnalyzer
$context->vars_possibly_in_scope
);
} elseif ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Concat) {
$stmt_type = Type::getString();
$statements_analyzer->node_data->setType($stmt, $stmt_type);
if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->left, $context) === false) {
return false;
}
@ -573,16 +569,31 @@ class BinaryOpAnalyzer
return false;
}
$stmt_left_type = $statements_analyzer->node_data->getType($stmt->left);
$stmt_right_type = $statements_analyzer->node_data->getType($stmt->right);
if ($stmt_left_type
&& $stmt_right_type
&& $stmt_left_type->getId() === 'non-empty-string'
&& $stmt_right_type->getId() === 'non-empty-string'
) {
$stmt_type = new Type\Union([new Type\Atomic\TNonEmptyString()]);
} else {
$stmt_type = Type::getString();
}
$statements_analyzer->node_data->setType($stmt, $stmt_type);
if ($codebase->taint) {
$sources = [];
$either_tainted = 0;
if ($stmt_left_type = $statements_analyzer->node_data->getType($stmt->left)) {
if ($stmt_left_type) {
$sources = $stmt_left_type->sources ?: [];
$either_tainted = $stmt_left_type->tainted;
}
if ($stmt_right_type = $statements_analyzer->node_data->getType($stmt->right)) {
if ($stmt_right_type) {
$sources = array_merge($sources, $stmt_right_type->sources ?: []);
$either_tainted = $either_tainted | $stmt_right_type->tainted;
}

View File

@ -767,6 +767,16 @@ class TypeAnalyzer
}
}
if ($container_type_part instanceof TNonEmptyString
&& get_class($input_type_part) === TString::class
) {
if ($atomic_comparison_result) {
$atomic_comparison_result->type_coerced = true;
}
return false;
}
if ($input_type_part->shallowEquals($container_type_part)
|| ($input_type_part instanceof Type\Atomic\TCallableObjectLikeArray
&& $container_type_part instanceof TArray)
@ -1154,16 +1164,6 @@ class TypeAnalyzer
return true;
}
if ($container_type_part instanceof TNonEmptyString
&& get_class($input_type_part) === TString::class
) {
if ($atomic_comparison_result) {
$atomic_comparison_result->type_coerced = true;
}
return false;
}
if ($container_type_part instanceof TNonEmptyString
&& $input_type_part instanceof TLiteralString
&& $input_type_part->value === ''

View File

@ -7,18 +7,18 @@ use Psalm\Type\Union;
class TemplateResult
{
/**
* @var array<string, array<non-empty-string, array{0: Union}>>
* @var array<string, array<string, array{0: Union}>>
*/
public $template_types;
/**
* @var array<string, array<non-empty-string, array{0: Union, 1?: int}>>
* @var array<string, array<string, array{0: Union, 1?: int}>>
*/
public $generic_params;
/**
* @param array<string, array<non-empty-string, array{0: Union}>> $template_types
* @param array<string, array<non-empty-string, array{0: Union, 1?: int}>> $generic_params
* @param array<string, array<string, array{0: Union}>> $template_types
* @param array<string, array<string, array{0: Union, 1?: int}>> $generic_params
*/
public function __construct(array $template_types, array $generic_params)
{

View File

@ -14,13 +14,10 @@ class TTemplateIndexedAccess extends \Psalm\Type\Atomic
public $offset_param_name;
/**
* @var non-empty-string
* @var string
*/
public $defining_class;
/**
* @param non-empty-string $defining_class
*/
public function __construct(
string $array_param_name,
string $offset_param_name,

View File

@ -9,13 +9,10 @@ class TTemplateKeyOf extends TArrayKey
public $param_name;
/**
* @var non-empty-string
* @var string
*/
public $defining_class;
/**
* @param non-empty-string $defining_class
*/
public function __construct(
string $param_name,
string $defining_class

View File

@ -24,12 +24,12 @@ class TTemplateParam extends \Psalm\Type\Atomic
public $as;
/**
* @var non-empty-string
* @var string
*/
public $defining_class;
/**
* @param non-empty-string $defining_class
* @param string $defining_class
*/
public function __construct(string $param_name, Union $extends, string $defining_class)
{

View File

@ -19,14 +19,10 @@ class TTemplateParamClass extends TClassString
public $as_type;
/**
* @var non-empty-string
* @var string
*/
public $defining_class;
/**
* @param non-empty-string $defining_class
* @param string $param_name
*/
public function __construct(
string $param_name,
string $as,

View File

@ -1091,7 +1091,7 @@ class Union
}
/**
* @param array<string, array<non-empty-string, array{Type\Union, 1?:int}>> $template_types
* @param array<string, array<string, array{Type\Union, 1?:int}>> $template_types
*
* @return void
*/

View File

@ -2872,6 +2872,23 @@ class ConditionalTest extends \Psalm\Tests\TestCase
}',
'error_message' => 'TypeDoesNotContainType',
],
'nonEmptyString' => [
'<?php
/**
* @psalm-param non-empty-string $name
*/
function sayHello(string $name) : void {
echo "Hello " . $name;
}
function takeInput() : void {
if (isset($_GET["name"]) && is_string($_GET["name"])) {
$name = trim($_GET["name"]);
sayHello($name);
}
}',
'error_message' => 'ArgumentTypeCoercion',
],
];
}
}