mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Support array_combine types and introduce a MoreSpecificReturnType issue
This commit is contained in:
parent
e4cceee140
commit
dae7718ae8
@ -117,6 +117,7 @@
|
||||
<xs:element name="MixedPropertyAssignment" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="MixedPropertyFetch" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="MixedStringOffsetAssignment" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="MoreSpecificReturnType" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="NoInterfaceProperties" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="NonStaticSelfCall" type="IssueHandlerType" minOccurs="0" />
|
||||
<xs:element name="NullArgument" type="IssueHandlerType" minOccurs="0" />
|
||||
|
@ -405,6 +405,7 @@ class FunctionChecker extends FunctionLikeChecker
|
||||
}
|
||||
|
||||
$first_arg = isset($call_args[0]->value) ? $call_args[0]->value : null;
|
||||
$second_arg = isset($call_args[1]->value) ? $call_args[1]->value : null;
|
||||
|
||||
$first_arg_array_generic = $first_arg
|
||||
&& isset($first_arg->inferredType)
|
||||
@ -493,6 +494,49 @@ class FunctionChecker extends FunctionLikeChecker
|
||||
return Type::getArray();
|
||||
}
|
||||
|
||||
if ($call_map_key === 'array_combine') {
|
||||
$second_arg_array_generic = $second_arg
|
||||
&& isset($second_arg->inferredType)
|
||||
&& isset($second_arg->inferredType->types['array'])
|
||||
&& $second_arg->inferredType->types['array'] instanceof Type\Atomic\TArray
|
||||
? $second_arg->inferredType->types['array']
|
||||
: null;
|
||||
|
||||
$second_arg_array_objectlike = $second_arg
|
||||
&& isset($second_arg->inferredType)
|
||||
&& isset($second_arg->inferredType->types['array'])
|
||||
&& $second_arg->inferredType->types['array'] instanceof Type\Atomic\ObjectLike
|
||||
? $second_arg->inferredType->types['array']
|
||||
: null;
|
||||
|
||||
if ($second_arg_array_generic || $second_arg_array_objectlike) {
|
||||
if ($first_arg && isset($first_arg->inferredType) && $first_arg->inferredType->hasArray()) {
|
||||
if ($first_arg_array_generic) {
|
||||
$keys_inner_type = clone $first_arg_array_generic->type_params[1];
|
||||
} else {
|
||||
/** @var Type\Atomic\ObjectLike $first_arg_array_objectlike */
|
||||
$keys_inner_type = $first_arg_array_objectlike->getGenericTypeParam();
|
||||
}
|
||||
} else {
|
||||
$keys_inner_type = Type::getMixed();
|
||||
}
|
||||
|
||||
if ($second_arg_array_generic) {
|
||||
$values_inner_type = clone $second_arg_array_generic->type_params[1];
|
||||
} else {
|
||||
/** @var Type\Atomic\ObjectLike $second_arg_array_objectlike */
|
||||
$values_inner_type = $second_arg_array_objectlike->getGenericTypeParam();
|
||||
}
|
||||
|
||||
return new Type\Union([
|
||||
new Type\Atomic\TArray([
|
||||
$keys_inner_type,
|
||||
$values_inner_type
|
||||
])
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($call_map_key === 'array_diff') {
|
||||
if (!$first_arg_array_generic) {
|
||||
return Type::getArray();
|
||||
@ -644,7 +688,7 @@ class FunctionChecker extends FunctionLikeChecker
|
||||
* @return array<string, array<string, string>>
|
||||
* @psalm-suppress MixedInferredReturnType as the use of require buggers things up
|
||||
* @psalm-suppress MixedAssignment
|
||||
* @psalm-suppress InvalidReturnType
|
||||
* @psalm-suppress MoreSpecificReturnType
|
||||
*/
|
||||
protected static function getCallMap()
|
||||
{
|
||||
|
@ -23,6 +23,7 @@ use Psalm\Issue\MisplacedRequiredParam;
|
||||
use Psalm\Issue\MissingClosureReturnType;
|
||||
use Psalm\Issue\MissingReturnType;
|
||||
use Psalm\Issue\MixedInferredReturnType;
|
||||
use Psalm\Issue\MoreSpecificReturnType;
|
||||
use Psalm\Issue\OverriddenMethodAccess;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\StatementsSource;
|
||||
@ -953,6 +954,21 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
||||
return null;
|
||||
}
|
||||
|
||||
// is the declared return type more specific than the inferred one?
|
||||
if ($declared_return_type->isNullable() === $inferred_return_type->isNullable() &&
|
||||
TypeChecker::isContainedBy($declared_return_type, $inferred_return_type, $this->getFileChecker())
|
||||
) {
|
||||
if (IssueBuffer::accepts(
|
||||
new MoreSpecificReturnType(
|
||||
'The given return type \'' . $declared_return_type . '\' for ' . $cased_method_id .
|
||||
' is more specific than the inferred return type \'' . $inferred_return_type . '\'',
|
||||
$secondary_return_type_location ?: $return_type_location
|
||||
),
|
||||
$this->suppressed_issues
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (IssueBuffer::accepts(
|
||||
new InvalidReturnType(
|
||||
'The given return type \'' . $declared_return_type . '\' for ' . $cased_method_id .
|
||||
@ -965,6 +981,9 @@ abstract class FunctionLikeChecker extends SourceChecker implements StatementsSo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (!TypeChecker::hasIdenticalTypes(
|
||||
$declared_return_type,
|
||||
$inferred_return_type,
|
||||
|
@ -25,7 +25,7 @@ class AssertionFinder
|
||||
* @param string|null $this_class_name
|
||||
* @param StatementsSource $source
|
||||
* @return array<string, string>
|
||||
* @psalm-suppress InvalidReturnType
|
||||
* @psalm-suppress MoreSpecificReturnType
|
||||
*/
|
||||
public static function getAssertions(
|
||||
PhpParser\Node\Expr $conditional,
|
||||
|
6
src/Psalm/Issue/MoreSpecificReturnType.php
Normal file
6
src/Psalm/Issue/MoreSpecificReturnType.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Psalm\Issue;
|
||||
|
||||
class MoreSpecificReturnType extends CodeError
|
||||
{
|
||||
}
|
Loading…
Reference in New Issue
Block a user