mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 13:51:54 +01:00
Add diagnostic message when shape fields are missing (#8762)
* Add a simple diagnostic for missing array shape fields * Fix dumb mistakes * Allow nested shape extra keys to prompt warning too
This commit is contained in:
parent
d1921b1abf
commit
1819a2d880
@ -56,6 +56,7 @@ use function array_diff;
|
||||
use function array_filter;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
@ -568,7 +569,13 @@ class ReturnTypeAnalyzer
|
||||
. $declared_return_type->getId()
|
||||
. '\' for ' . $cased_method_id
|
||||
. ' is incorrect, got \''
|
||||
. $inferred_return_type->getId() . '\'',
|
||||
. $inferred_return_type->getId()
|
||||
. '\''
|
||||
. ($union_comparison_results->missing_shape_fields
|
||||
? ' which is different due to additional array shape fields ('
|
||||
. implode(', ', $union_comparison_results->missing_shape_fields)
|
||||
. ')'
|
||||
: ''),
|
||||
$return_type_location
|
||||
),
|
||||
$suppressed_issues,
|
||||
|
@ -64,6 +64,7 @@ use Psalm\Type\Union;
|
||||
|
||||
use function count;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function ord;
|
||||
use function preg_split;
|
||||
@ -1044,8 +1045,14 @@ class ArgumentAnalyzer
|
||||
} else {
|
||||
IssueBuffer::maybeAdd(
|
||||
new InvalidArgument(
|
||||
'Argument ' . ($argument_offset + 1) . $method_identifier . ' expects ' . $param_type->getId() .
|
||||
', but ' . $type . ' provided',
|
||||
'Argument ' . ($argument_offset + 1) . $method_identifier . ' expects ' . $param_type->getId()
|
||||
. ', but ' . $type
|
||||
. ($union_comparison_results->missing_shape_fields
|
||||
? ' with additional array shape fields ('
|
||||
. implode(', ', $union_comparison_results->missing_shape_fields)
|
||||
. ') was'
|
||||
: '')
|
||||
. ' provided',
|
||||
$arg_location,
|
||||
$cased_method_id
|
||||
),
|
||||
|
@ -45,6 +45,7 @@ use Psalm\Type\Union;
|
||||
|
||||
use function count;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function reset;
|
||||
use function strtolower;
|
||||
|
||||
@ -480,7 +481,12 @@ class ReturnAnalyzer
|
||||
new InvalidReturnStatement(
|
||||
'The inferred type \'' . $inferred_type->getId()
|
||||
. '\' does not match the declared return '
|
||||
. 'type \'' . $local_return_type->getId() . '\' for ' . $cased_method_id,
|
||||
. 'type \'' . $local_return_type->getId() . '\' for ' . $cased_method_id
|
||||
. ($union_comparison_results->missing_shape_fields
|
||||
? ' due to additional array shape fields ('
|
||||
. implode(', ', $union_comparison_results->missing_shape_fields)
|
||||
. ')'
|
||||
: ''),
|
||||
new CodeLocation($source, $stmt->expr)
|
||||
),
|
||||
$statements_analyzer->getSuppressedIssues()
|
||||
|
@ -9,6 +9,7 @@ use Psalm\Type\Atomic\TKeyedArray;
|
||||
use Psalm\Type\Atomic\TNamedObject;
|
||||
use Psalm\Type\Atomic\TObjectWithProperties;
|
||||
|
||||
use function array_keys;
|
||||
use function is_string;
|
||||
|
||||
/**
|
||||
@ -82,6 +83,11 @@ class KeyedArrayComparator
|
||||
) {
|
||||
$atomic_comparison_result->type_coerced = true;
|
||||
}
|
||||
|
||||
if ($property_type_comparison->missing_shape_fields) {
|
||||
$atomic_comparison_result->missing_shape_fields
|
||||
= $property_type_comparison->missing_shape_fields;
|
||||
}
|
||||
}
|
||||
|
||||
$all_types_contain = false;
|
||||
@ -95,6 +101,9 @@ class KeyedArrayComparator
|
||||
}
|
||||
}
|
||||
if ($container_sealed && $input_properties) {
|
||||
if ($atomic_comparison_result) {
|
||||
$atomic_comparison_result->missing_shape_fields = array_keys($input_properties);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return $all_types_contain;
|
||||
|
@ -48,4 +48,7 @@ class TypeComparisonResult
|
||||
|
||||
/** @var ?Atomic */
|
||||
public $replacement_atomic_type;
|
||||
|
||||
/** @var ?non-empty-list<int|string> */
|
||||
public $missing_shape_fields;
|
||||
}
|
||||
|
@ -100,6 +100,8 @@ class UnionTypeComparator
|
||||
$some_type_coerced = false;
|
||||
$some_type_coerced_from_mixed = false;
|
||||
|
||||
$some_missing_shape_fields = null;
|
||||
|
||||
if ($input_type_part instanceof TArrayKey
|
||||
&& ($container_type->hasInt() && $container_type->hasString())
|
||||
) {
|
||||
@ -272,6 +274,10 @@ class UnionTypeComparator
|
||||
} else {
|
||||
$all_type_coerced_from_as_mixed = true;
|
||||
}
|
||||
|
||||
if ($atomic_comparison_result->missing_shape_fields) {
|
||||
$some_missing_shape_fields = $atomic_comparison_result->missing_shape_fields;
|
||||
}
|
||||
}
|
||||
|
||||
if ($is_atomic_contained_by) {
|
||||
@ -331,6 +337,10 @@ class UnionTypeComparator
|
||||
if (!$scalar_type_match_found) {
|
||||
$union_comparison_result->scalar_type_match_found = false;
|
||||
}
|
||||
|
||||
if ($some_missing_shape_fields && !$some_type_coerced && !$scalar_type_match_found) {
|
||||
$union_comparison_result->missing_shape_fields = $some_missing_shape_fields;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -5,6 +5,8 @@ namespace Psalm\Tests;
|
||||
use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait;
|
||||
use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
class ArgTest extends TestCase
|
||||
{
|
||||
use InvalidCodeAnalysisTestTrait;
|
||||
@ -792,7 +794,7 @@ class ArgTest extends TestCase
|
||||
$sealedExtraKeys = ["test" => "str", "somethingElse" => "test"];
|
||||
a($sealedExtraKeys);
|
||||
',
|
||||
'error_message' => 'InvalidArgument',
|
||||
'error_message' => 'InvalidArgument - src' . DIRECTORY_SEPARATOR . 'somefile.php:8:23 - Argument 1 of a expects array{test: string}, but array{somethingElse: \'test\', test: \'str\'} with additional array shape fields (somethingElse) was provided',
|
||||
],
|
||||
'callbackArgsCountMismatch' => [
|
||||
'code' => '<?php
|
||||
|
Loading…
x
Reference in New Issue
Block a user