From 42f333bd244b26e0a5557613a26d2fe2a41b22fe Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Fri, 10 Jun 2022 11:11:45 +0200 Subject: [PATCH 1/3] Use explicit mixed for global array variables --- src/Analyser/MutatingScope.php | 2 +- .../CallToFunctionParametersRuleTest.php | 15 ++++++++++ .../Rules/Functions/data/discussion-7450.php | 28 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Rules/Functions/data/discussion-7450.php diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index c65a9d7687..ebad8b7104 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -495,7 +495,7 @@ public function hasVariableType(string $variableName): TrinaryLogic public function getVariableType(string $variableName): Type { if ($this->isGlobalVariable($variableName)) { - return new ArrayType(new StringType(), new MixedType()); + return new ArrayType(new StringType(), new MixedType(true)); } if ($this->hasVariableType($variableName)->no()) { diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 338991338d..85d77ce07e 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1077,4 +1077,19 @@ public function testBug6987(): void $this->analyse([__DIR__ . '/data/bug-6987.php'], []); } + public function testDiscussion7450(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/discussion-7450.php'], [ + [ + 'Parameter #1 $foo of function Discussion7450\foo expects array{policy: non-empty-string, entitlements: array}, array{policy: mixed, entitlements: mixed} given.', + 18, + ], + [ + 'Parameter #1 $foo of function Discussion7450\foo expects array{policy: non-empty-string, entitlements: array}, array{policy: mixed, entitlements: mixed} given.', + 28, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/discussion-7450.php b/tests/PHPStan/Rules/Functions/data/discussion-7450.php new file mode 100644 index 0000000000..33362bf352 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/discussion-7450.php @@ -0,0 +1,28 @@ + $_POST['policy'], // shouldn't this and the next line be an unsafe offset access? + 'entitlements' => $_POST['entitlements'], +]; +assertType('mixed', $_POST['policy']); +assertType('array{policy: mixed, entitlements: mixed}', $args); +foo($args); // I'd expect this to be reported too + +/** @var mixed $mixed */ +$mixed = null; +$args = [ + 'policy' => $mixed, + 'entitlements' => $mixed, +]; +assertType('mixed', $mixed); +assertType('array{policy: mixed, entitlements: mixed}', $args); +foo($args); From 6e5f7eedcf234e0003c6d14457184a49662f0ec5 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Fri, 10 Jun 2022 22:27:24 +0200 Subject: [PATCH 2/3] Only enable explicit mixed via new bleeding edge config `explicitMixedForGlobalVariables` --- conf/bleedingEdge.neon | 1 + conf/config.neon | 2 ++ src/Analyser/DirectScopeFactory.php | 2 ++ src/Analyser/LazyScopeFactory.php | 4 ++++ src/Analyser/MutatingScope.php | 3 ++- src/Testing/PHPStanTestCase.php | 1 + 6 files changed, 12 insertions(+), 1 deletion(-) diff --git a/conf/bleedingEdge.neon b/conf/bleedingEdge.neon index ceee25802a..450b0c8cd3 100644 --- a/conf/bleedingEdge.neon +++ b/conf/bleedingEdge.neon @@ -3,6 +3,7 @@ parameters: bleedingEdge: true skipCheckGenericClasses: [] explicitMixedInUnknownGenericNew: true + explicitMixedForGlobalVariables: true explicitMixedViaIsArray: true arrayFilter: true arrayUnpacking: true diff --git a/conf/config.neon b/conf/config.neon index 27cf2f5bcb..e2ad3fa9a2 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -27,6 +27,7 @@ parameters: - RecursiveCallbackFilterIterator - AppendIterator explicitMixedInUnknownGenericNew: false + explicitMixedForGlobalVariables: false explicitMixedViaIsArray: false arrayFilter: false arrayUnpacking: false @@ -228,6 +229,7 @@ parametersSchema: disableRuntimeReflectionProvider: bool(), skipCheckGenericClasses: listOf(string()), explicitMixedInUnknownGenericNew: bool(), + explicitMixedForGlobalVariables: bool(), explicitMixedViaIsArray: bool(), arrayFilter: bool(), arrayUnpacking: bool(), diff --git a/src/Analyser/DirectScopeFactory.php b/src/Analyser/DirectScopeFactory.php index 3682e7d15a..eb1df50afa 100644 --- a/src/Analyser/DirectScopeFactory.php +++ b/src/Analyser/DirectScopeFactory.php @@ -38,6 +38,7 @@ public function __construct( private bool $treatPhpDocTypesAsCertain, private PhpVersion $phpVersion, private bool $explicitMixedInUnknownGenericNew, + private bool $explicitMixedForGlobalVariables, private ConstantResolver $constantResolver, ) { @@ -110,6 +111,7 @@ public function create( $afterExtractCall, $parentScope, $this->explicitMixedInUnknownGenericNew, + $this->explicitMixedForGlobalVariables, ); } diff --git a/src/Analyser/LazyScopeFactory.php b/src/Analyser/LazyScopeFactory.php index 6ad56ddba8..38b4fe2108 100644 --- a/src/Analyser/LazyScopeFactory.php +++ b/src/Analyser/LazyScopeFactory.php @@ -23,6 +23,8 @@ class LazyScopeFactory implements ScopeFactory private bool $explicitMixedInUnknownGenericNew; + private bool $explicitMixedForGlobalVariables; + /** * @param class-string $scopeClass */ @@ -33,6 +35,7 @@ public function __construct( { $this->treatPhpDocTypesAsCertain = $container->getParameter('treatPhpDocTypesAsCertain'); $this->explicitMixedInUnknownGenericNew = $this->container->getParameter('featureToggles')['explicitMixedInUnknownGenericNew']; + $this->explicitMixedForGlobalVariables = $this->container->getParameter('featureToggles')['explicitMixedForGlobalVariables']; } /** @@ -102,6 +105,7 @@ public function create( $afterExtractCall, $parentScope, $this->explicitMixedInUnknownGenericNew, + $this->explicitMixedForGlobalVariables, ); } diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index ebad8b7104..ea522557a2 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -185,6 +185,7 @@ public function __construct( private bool $afterExtractCall = false, private ?Scope $parentScope = null, private bool $explicitMixedInUnknownGenericNew = false, + private bool $explicitMixedForGlobalVariables = false, ) { if ($namespace === '') { @@ -495,7 +496,7 @@ public function hasVariableType(string $variableName): TrinaryLogic public function getVariableType(string $variableName): Type { if ($this->isGlobalVariable($variableName)) { - return new ArrayType(new StringType(), new MixedType(true)); + return new ArrayType(new StringType(), new MixedType($this->explicitMixedForGlobalVariables)); } if ($this->hasVariableType($variableName)->no()) { diff --git a/src/Testing/PHPStanTestCase.php b/src/Testing/PHPStanTestCase.php index 45b6e6d7e2..17ce442df0 100644 --- a/src/Testing/PHPStanTestCase.php +++ b/src/Testing/PHPStanTestCase.php @@ -175,6 +175,7 @@ public function createScopeFactory(ReflectionProvider $reflectionProvider, TypeS $this->shouldTreatPhpDocTypesAsCertain(), $container->getByType(PhpVersion::class), $container->getParameter('featureToggles')['explicitMixedInUnknownGenericNew'], + $container->getParameter('featureToggles')['explicitMixedForGlobalVariables'], $constantResolver, ); } From 584c30d926e51d2247b9b7652617de13a5a69051 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Sun, 12 Jun 2022 09:48:51 +0200 Subject: [PATCH 3/3] Test also without checkExplicitMixed --- .../Rules/Functions/CallToFunctionParametersRuleTest.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 85d77ce07e..bbd964caf9 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1077,7 +1077,13 @@ public function testBug6987(): void $this->analyse([__DIR__ . '/data/bug-6987.php'], []); } - public function testDiscussion7450(): void + public function testDiscussion7450WithoutCheckExplicitMixed(): void + { + $this->checkExplicitMixed = false; + $this->analyse([__DIR__ . '/data/discussion-7450.php'], []); + } + + public function testDiscussion7450WithCheckExplicitMixed(): void { $this->checkExplicitMixed = true; $this->analyse([__DIR__ . '/data/discussion-7450.php'], [