1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +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:
Matthew Brown 2022-11-26 11:26:36 -05:00 committed by GitHub
parent d1921b1abf
commit 1819a2d880
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 49 additions and 5 deletions

View File

@ -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,

View File

@ -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
),

View File

@ -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()

View File

@ -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;

View File

@ -48,4 +48,7 @@ class TypeComparisonResult
/** @var ?Atomic */
public $replacement_atomic_type;
/** @var ?non-empty-list<int|string> */
public $missing_shape_fields;
}

View File

@ -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;

View File

@ -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