create(ParserFactory::PREFER_PHP7); } /** * @return void */ public function setUp() { $config = new TestConfig(); \Psalm\Checker\FileChecker::clearCache(); $this->project_checker = new \Psalm\Checker\ProjectChecker(); } /** * @return void */ public function testGenericArrayCreation() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array', (string) $context->vars_in_scope['$out']); } /** * @return void */ public function testGeneric2DArrayCreation() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array>', (string) $context->vars_in_scope['$out']); } /** * @return void */ public function testGeneric2DArrayCreationAddedInIf() { $stmts = self::$parser->parse(' 50) { $out[] = $bits; $bits = []; } $bits[] = 4; } if ($bits) { $out[] = $bits; } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array>', (string) $context->vars_in_scope['$out']); } /** * @return void */ public function testGenericArrayCreationWithObjectAddedInIf() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array', (string) $context->vars_in_scope['$out']); } /** * @return void */ public function testGenericArrayCreationWithElementAddedInSwitch() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array', (string) $context->vars_in_scope['$out']); } /** * @return void */ public function testGenericArrayCreationWithElementsAddedInSwitch() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array', (string) $context->vars_in_scope['$out']); } /** * @return void */ public function testGenericArrayCreationWithElementsAddedInSwitchWithNothing() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array', (string) $context->vars_in_scope['$out']); } /** * @return void */ public function testImplicitIntArrayCreation() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array', (string) $context->vars_in_scope['$foo']); } /** * @return void */ public function testImplicit2DIntArrayCreation() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array>', (string) $context->vars_in_scope['$foo']); } /** * @return void */ public function testImplicit3DIntArrayCreation() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array>>', (string) $context->vars_in_scope['$foo']); } /** * @return void */ public function testImplicit4DIntArrayCreation() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals( 'array>>>', (string) $context->vars_in_scope['$foo'] ); } /** * @return void */ public function testImplicitIndexedIntArrayCreation() { $stmts = self::$parser->parse(' $text) { $bat[$text] = $bar[$i]; } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array', (string) $context->vars_in_scope['$foo']); $this->assertEquals('array', (string) $context->vars_in_scope['$bar']); $this->assertEquals('array', (string) $context->vars_in_scope['$bat']); } /** * @return void */ public function testImplicitStringArrayCreation() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array{bar:string}', (string) $context->vars_in_scope['$foo']); $this->assertEquals('string', (string) $context->vars_in_scope['$foo[\'bar\']']); } /** * @return void */ public function testImplicit2DStringArrayCreation() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array{bar:array{baz:string}}', (string) $context->vars_in_scope['$foo']); $this->assertEquals('string', (string) $context->vars_in_scope['$foo[\'bar\'][\'baz\']']); } /** * @return void */ public function testImplicit3DStringArrayCreation() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array{bar:array{baz:array{bat:string}}}', (string) $context->vars_in_scope['$foo']); $this->assertEquals('string', (string) $context->vars_in_scope['$foo[\'bar\'][\'baz\'][\'bat\']']); } /** * @return void */ public function testImplicit4DStringArrayCreation() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals( 'array{bar:array{baz:array{bat:array{bap:string}}}}', (string) $context->vars_in_scope['$foo'] ); $this->assertEquals('string', (string) $context->vars_in_scope['$foo[\'bar\'][\'baz\'][\'bat\'][\'bap\']']); } /** * @return void */ public function test2Step2DStringArrayCreation() { $stmts = self::$parser->parse(' []]; $foo["bar"]["baz"] = "hello"; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array{bar:array{baz:string}}', (string) $context->vars_in_scope['$foo']); $this->assertEquals('string', (string) $context->vars_in_scope['$foo[\'bar\'][\'baz\']']); } /** * @return void */ public function test2StepImplicit3DStringArrayCreation() { $stmts = self::$parser->parse(' []]; $foo["bar"]["baz"]["bat"] = "hello"; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array{bar:array{baz:array{bat:string}}}', (string) $context->vars_in_scope['$foo']); } /** * @return void */ public function testConflictingTypes() { $stmts = self::$parser->parse(' ["a" => "b"], "baz" => [1] ]; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array{bar:array{a:string}, baz:array}', (string) $context->vars_in_scope['$foo']); } /** * @return void */ public function testImplicitObjectLikeCreation() { $stmts = self::$parser->parse(' 1, ]; $foo["baz"] = "a"; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array{bar:int, baz:string}', (string) $context->vars_in_scope['$foo']); } /** * @return void */ public function testConflictingTypesWithAssignment() { $stmts = self::$parser->parse(' ["a" => "b"], "baz" => [1] ]; $foo["bar"]["bam"]["baz"] = "hello"; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals( 'array{bar:array{a:string, bam:array{baz:string}}, baz:array}', (string) $context->vars_in_scope['$foo'] ); } /** * @return void */ public function testConflictingTypesWithAssignment2() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array{a:string, b:array}', (string) $context->vars_in_scope['$foo']); $this->assertEquals('string', (string) $context->vars_in_scope['$foo[\'a\']']); $this->assertEquals('array', (string) $context->vars_in_scope['$foo[\'b\']']); $this->assertEquals('string', (string) $context->vars_in_scope['$bar']); } /** * @return void */ public function testConflictingTypesWithAssignment3() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array{a:string, b:array{c:array{d:string}}}', (string) $context->vars_in_scope['$foo']); } /** * @return void */ public function testNestedObjectLikeAssignment() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array{a:array{b:string, c:int}}', (string) $context->vars_in_scope['$foo']); } /** * @return void */ public function testConditionalObjectLikeAssignment() { $stmts = self::$parser->parse(' "hello"]; if (rand(0, 10) === 5) { $foo["b"] = 1; } else { $foo["b"] = 2; } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array{a:string, b:int}', (string) $context->vars_in_scope['$foo']); } /** * @return void */ public function testIssetKeyedOffset() { $file_checker = new FileChecker( 'somefile.php', $this->project_checker, self::$parser->parse('vars_in_scope['$foo'] = \Psalm\Type::getArray(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('mixed', (string) $context->vars_in_scope['$foo[\'a\']']); } /** * @return void */ public function testConditionalAssignment() { $file_checker = new FileChecker( 'somefile.php', $this->project_checker, self::$parser->parse('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() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('A', (string) $context->vars_in_scope['$a']); $this->assertFalse(isset($context->vars_in_scope['$a[\'bar\']'])); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage InvalidArrayAssignment * @return void */ public function testObjectAssignment() { $context = new Context(); $stmts = self::$parser->parse('project_checker, $stmts); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testConditionalCheck() { $file_checker = new FileChecker( 'somefile.php', $this->project_checker, self::$parser->parse('visitAndAnalyzeMethods($context); } /** * @return void */ public function testArrayKey() { $file_checker = new FileChecker( 'somefile.php', $this->project_checker, self::$parser->parse(' "foo", "b"=> "bar"]; $d = "a"; $e = $a[$d]; ') ); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('string', (string)$context->vars_in_scope['$b']); $this->assertEquals('string', (string)$context->vars_in_scope['$e']); } /** * @return void */ public function testVariableKeyArrayCreate() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array>', (string) $context->vars_in_scope['$a']); $this->assertEquals('array>>', (string) $context->vars_in_scope['$c']); } /** * @return void */ public function testAssignExplicitValueToGeneric() { $stmts = self::$parser->parse('> */ $a = []; $a["foo"] = ["bar" => "baz"]; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array>', (string) $context->vars_in_scope['$a']); } /** * @return void */ public function testAdditionWithEmpty() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array', (string) $context->vars_in_scope['$a']); $this->assertEquals('array', (string) $context->vars_in_scope['$b']); } /** * @return void */ public function testAdditionDifferentType() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array', (string) $context->vars_in_scope['$a']); $this->assertEquals('array', (string) $context->vars_in_scope['$b']); } /** * @return void */ public function testPreset1DArrayTypeWithVarKeys() { $stmts = self::$parser->parse('> */ $a = []; $foo = "foo"; $a[$foo][] = "bat"; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testPreset2DArrayTypeWithVarKeys() { $stmts = self::$parser->parse('>> */ $b = []; $foo = "foo"; $bar = "bar"; $b[$foo][$bar][] = "bat"; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage InvalidArrayAssignment * @return void */ public function testInvalidArrayAccess() { $context = new Context(); $stmts = self::$parser->parse('project_checker, $stmts); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage MixedStringOffsetAssignment * @return void */ public function testMixedStringOffsetAssignment() { Config::getInstance()->setCustomErrorLevel('MixedAssignment', Config::REPORT_SUPPRESS); $context = new Context(); $stmts = self::$parser->parse('project_checker, $stmts); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage TypeCoercion * @return void */ public function testMixedArrayArgument() { Config::getInstance()->setCustomErrorLevel('MixedAssignment', Config::REPORT_SUPPRESS); $context = new Context(); $stmts = self::$parser->parse(' $foo */ function fooFoo(array $foo) : void { } function barBar(array $bar) : void { fooFoo($bar); } barBar([1, "2"]); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testObjectLikeWithIntegerKeys() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('string', (string) $context->vars_in_scope['$b']); $this->assertEquals('int', (string) $context->vars_in_scope['$c']); $this->assertEquals('string', (string) $context->vars_in_scope['$d']); $this->assertEquals('int', (string) $context->vars_in_scope['$e']); } }