addFile( 'somefile.php', 'project_checker); $context = new Context(); $context->vars_in_scope['$b'] = \Psalm\Type::getBool(); $context->vars_in_scope['$foo'] = \Psalm\Type::getArray(); $file_checker->visitAndAnalyzeMethods($context); $this->assertFalse(isset($context->vars_in_scope['$foo[\'a\']'])); } /** * @return void */ public function testImplementsArrayAccess() { $this->addFile( 'somefile.php', 'project_checker); $file_checker->visitAndAnalyzeMethods(); } /** * @return array */ public function providerFileCheckerValidCodeParse() { return [ 'genericArrayCreation' => [ ' [ '$out' => 'array', ], ], 'generic2dArrayCreation' => [ ' [ '$out' => 'array>', ], ], 'generic2dArrayCreationAddedInIf' => [ ' 50) { $out[] = $bits; $bits = []; } $bits[] = 4; } if ($bits) { $out[] = $bits; }', 'assertions' => [ '$out' => 'array>', ], ], 'genericArrayCreationWithObjectAddedInIf' => [ ' [ '$out' => 'array', ], ], 'genericArrayCreationWithElementAddedInSwitch' => [ ' [ '$out' => 'array', ], ], 'genericArrayCreationWithElementsAddedInSwitch' => [ ' [ '$out' => 'array', ], ], 'genericArrayCreationWithElementsAddedInSwitchWithNothing' => [ ' [ '$out' => 'array', ], ], 'implicitIntArrayCreation' => [ ' [ '$foo' => 'array', ], ], 'implicit2dIntArrayCreation' => [ ' [ '$foo' => 'array>', ], ], 'implicit3dIntArrayCreation' => [ ' [ '$foo' => 'array>>', ], ], 'implicit4dIntArrayCreation' => [ ' [ '$foo' => 'array>>>', ], ], 'implicitIndexedIntArrayCreation' => [ ' $text) { $bat[$text] = $bar[$i]; }', 'assertions' => [ '$foo' => 'array', '$bar' => 'array', '$bat' => 'array', ], ], 'implicitStringArrayCreation' => [ ' [ '$foo' => 'array{bar:string}', '$foo[\'bar\']' => 'string', ], ], 'implicit2dStringArrayCreation' => [ ' [ '$foo' => 'array{bar:array{baz:string}}', '$foo[\'bar\'][\'baz\']' => 'string', ], ], 'implicit3dStringArrayCreation' => [ ' [ '$foo' => 'array{bar:array{baz:array{bat:string}}}', '$foo[\'bar\'][\'baz\'][\'bat\']' => 'string', ], ], 'implicit4dStringArrayCreation' => [ ' [ '$foo' => 'array{bar:array{baz:array{bat:array{bap:string}}}}', '$foo[\'bar\'][\'baz\'][\'bat\'][\'bap\']' => 'string', ], ], '2Step2dStringArrayCreation' => [ ' []]; $foo["bar"]["baz"] = "hello";', 'assertions' => [ '$foo' => 'array{bar:array{baz:string}}', '$foo[\'bar\'][\'baz\']' => 'string', ], ], '2StepImplicit3dStringArrayCreation' => [ ' []]; $foo["bar"]["baz"]["bat"] = "hello";', 'assertions' => [ '$foo' => 'array{bar:array{baz:array{bat:string}}}', ], ], 'conflictingTypes' => [ ' ["a" => "b"], "baz" => [1] ];', 'assertions' => [ '$foo' => 'array{bar:array{a:string}, baz:array}', ], ], 'implicitObjectLikeCreation' => [ ' 1, ]; $foo["baz"] = "a";', 'assertions' => [ '$foo' => 'array{bar:int, baz:string}', ], ], 'conflictingTypesWithAssignment' => [ ' ["a" => "b"], "baz" => [1] ]; $foo["bar"]["bam"]["baz"] = "hello";', 'assertions' => [ '$foo' => 'array{bar:array{a:string, bam:array{baz:string}}, baz:array}', ], ], 'conflictingTypesWithAssignment2' => [ ' [ '$foo' => 'array{a:string, b:array}', '$foo[\'a\']' => 'string', '$foo[\'b\']' => 'array', '$bar' => 'string', ], ], 'conflictingTypesWithAssignment3' => [ ' [ '$foo' => 'array{a:string, b:array{c:array{d:string}}}', ], ], 'nestedObjectLikeAssignment' => [ ' [ '$foo' => 'array{a:array{b:string, c:int}}', ], ], 'conditionalObjectLikeAssignment' => [ ' "hello"]; if (rand(0, 10) === 5) { $foo["b"] = 1; } else { $foo["b"] = 2; }', 'assertions' => [ '$foo' => 'array{a:string, b:int}', ], ], 'arrayKey' => [ ' "foo", "b"=> "bar"]; $d = "a"; $e = $c[$d];', 'assertions' => [ '$b' => 'string', '$e' => 'string', ], ], 'conditionalCheck' => [ ' [], ], 'variableKeyArrayCreate' => [ ' [ '$a' => 'array>', '$c' => 'array>>', ], ], 'assignExplicitValueToGeneric' => [ '> */ $a = []; $a["foo"] = ["bar" => "baz"];', 'assertions' => [ '$a' => 'array>', ], ], 'additionWithEmpty' => [ ' [ '$a' => 'array', '$b' => 'array', ], ], 'additionDifferentType' => [ ' [ '$a' => 'array', '$b' => 'array', ], ], 'present1dArrayTypeWithVarKeys' => [ '> */ $a = []; $foo = "foo"; $a[$foo][] = "bat";', 'assertions' => [], ], 'present2dArrayTypeWithVarKeys' => [ '>> */ $b = []; $foo = "foo"; $bar = "bar"; $b[$foo][$bar][] = "bat";', 'assertions' => [], ], 'objectLikeWithIntegerKeys' => [ ' [ '$b' => 'string', '$c' => 'int', '$d' => 'string', '$e' => 'int', ], ], 'objectLikeArrayAddition' => [ ' [2, 3]];', 'assertions' => [ '$foo' => 'array{a:int, b:array}', ], ], ]; } /** * @return array */ public function providerFileCheckerInvalidCodeParse() { return [ 'objectAssignment' => [ ' 'InvalidArrayAssignment', ], 'invalidArrayAccess' => [ ' 'InvalidArrayAssignment', ], 'mixedStringOffsetAssignment' => [ ' 'MixedStringOffsetAssignment', 'error_level' => ['MixedAssignment'], ], 'mixedArrayArgument' => [ ' $foo */ function fooFoo(array $foo) : void { } function barBar(array $bar) : void { fooFoo($bar); } barBar([1, "2"]);', 'error_message' => 'TypeCoercion', 'error_level' => ['MixedAssignment'], ], 'arrayPropertyAssignment' => [ 'strs = [new stdClass()]; // no issue emitted } }', 'error_message' => 'InvalidPropertyAssignment', ], 'incrementalArrayPropertyAssignment' => [ 'strs[] = new stdClass(); // no issue emitted } }', 'error_message' => 'InvalidPropertyAssignment', ], ]; } }