create(ParserFactory::PREFER_PHP7); } /** * @return void */ public function setUp() { FileChecker::clearCache(); $this->project_checker = new \Psalm\Checker\ProjectChecker(); $this->project_checker->setConfig(new TestConfig()); } /** * @return void */ public function testNewVarInIf() { $stmts = self::$parser->parse('foo = []; } if (!is_array($this->foo)) { // do something } } } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testPropertyWithoutTypeSuppressingIssue() { Config::getInstance()->setCustomErrorLevel('MissingPropertyType', Config::REPORT_SUPPRESS); Config::getInstance()->setCustomErrorLevel('MixedAssignment', Config::REPORT_SUPPRESS); $stmts = self::$parser->parse('foo; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testPropertyWithoutTypeSuppressingIssueAndAssertingNull() { Config::getInstance()->setCustomErrorLevel('UndefinedThisPropertyFetch', Config::REPORT_SUPPRESS); Config::getInstance()->setCustomErrorLevel('MixedAssignment', Config::REPORT_SUPPRESS); Config::getInstance()->setCustomErrorLevel('MixedMethodCall', Config::REPORT_SUPPRESS); Config::getInstance()->setCustomErrorLevel('MixedPropertyFetch', Config::REPORT_SUPPRESS); $stmts = self::$parser->parse('foo === null && rand(0,1); echo $this->foo->baz; } } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage UndefinedPropertyAssignment * @return void */ public function testUndefinedPropertyAssignment() { $stmts = self::$parser->parse('foo = "cool"; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage UndefinedPropertyFetch * @return void */ public function testUndefinedPropertyFetch() { $stmts = self::$parser->parse('foo; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage UndefinedThisPropertyAssignment * @return void */ public function testUndefinedThisPropertyAssignment() { $stmts = self::$parser->parse('foo = "cool"; } } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage UndefinedThisPropertyFetch * @return void */ public function testUndefinedThisPropertyFetch() { $stmts = self::$parser->parse('foo; } } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage MissingPropertyDeclaration * @return void */ public function testMissingPropertyDeclaration() { $stmts = self::$parser->parse('foo = "cool"; } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage MissingPropertyType * @return void */ public function testMissingPropertyType() { $stmts = self::$parser->parse('project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage InvalidPropertyAssignment * @return void */ public function testBadAssignment() { $stmts = self::$parser->parse('foo = 5; } } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage InvalidPropertyAssignment * @return void */ public function testBadAssignmentAsWell() { $stmts = self::$parser->parse('foo = "bar"; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage InvalidPropertyFetch * @return void */ public function testBadFetch() { $stmts = self::$parser->parse('foo; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testSharedPropertyInIf() { $stmts = self::$parser->parse('foo; } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('null|string|int', (string) $context->vars_in_scope['$b']); } /** * @return void */ public function testSharedPropertyInElseIf() { $stmts = self::$parser->parse('foo; } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('null|string|int', (string) $context->vars_in_scope['$b']); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage MixedPropertyFetch * @return void */ public function testMixedPropertyFetch() { Config::getInstance()->setCustomErrorLevel('MissingPropertyType', Config::REPORT_SUPPRESS); Config::getInstance()->setCustomErrorLevel('MixedAssignment', Config::REPORT_SUPPRESS); $stmts = self::$parser->parse('foo; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage MixedPropertyAssignment * @return void */ public function testMixedPropertyAssignment() { Config::getInstance()->setCustomErrorLevel('MissingPropertyType', Config::REPORT_SUPPRESS); Config::getInstance()->setCustomErrorLevel('MixedAssignment', Config::REPORT_SUPPRESS); $stmts = self::$parser->parse('foo = "hello"; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage PossiblyNullPropertyAssignment * @return void */ public function testPossiblyNullablePropertyAssignment() { $stmts = self::$parser->parse('foo = "hello"; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage NullPropertyAssignment * @return void */ public function testNullablePropertyAssignment() { $stmts = self::$parser->parse('foo = "hello"; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage PossiblyNullPropertyFetch * @return void */ public function testPossiblyNullablePropertyFetch() { $stmts = self::$parser->parse('foo; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage NullPropertyFetch * @return void */ public function testNullablePropertyFetch() { $stmts = self::$parser->parse('foo; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testNullablePropertyCheck() { $stmts = self::$parser->parse('bb) && $b->bb->aa === "aa") { echo $b->bb->aa; } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testNullablePropertyAfterGuard() { $stmts = self::$parser->parse('aa) { $a->aa = "hello"; } echo substr($a->aa, 1); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testNullableStaticPropertyWithIfCheck() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testReflectionProperties() { $stmts = self::$parser->parse('name . " - " . $a->class; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testGrandparentReflectedProperties() { $stmts = self::$parser->parse('ownerDocument; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('DOMDocument', (string) $context->vars_in_scope['$owner']); } /** * @return void */ public function testGoodArrayProperties() { Config::getInstance()->setCustomErrorLevel('MixedAssignment', Config::REPORT_SUPPRESS); $context = new Context(); $stmts = self::$parser->parse(' */ public $is = []; } $c = new C1; $c->is = [new A1]; $c->is = [new A1, new A1]; $c->is = [new A1, new B1]; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage InvalidPropertyAssignment * @return void */ public function testBadArrayProperty() { Config::getInstance()->setCustomErrorLevel('MixedAssignment', Config::REPORT_SUPPRESS); $context = new Context(); $stmts = self::$parser->parse(' */ public $bb; } $c = new C; $c->bb = [new A, new B]; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testIssetPropertyDoesNotExist() { $stmts = self::$parser->parse('bar)) { } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage PropertyNotSetInConstructor * @return void */ public function testNotSetInEmptyConstructor() { $this->project_checker->registerFile( getcwd() . '/somefile.php', 'project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage MissingConstructor * @return void */ public function testNoConstructor() { $this->project_checker->registerFile( getcwd() . '/somefile.php', 'project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testNotSetInConstructorButHasDefault() { $this->project_checker->registerFile( getcwd() . '/somefile.php', 'project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage PropertyNotSetInConstructor * @return void */ public function testNotSetInAllBranchesOfIf() { $this->project_checker->registerFile( getcwd() . '/somefile.php', 'a = 5; } } }' ); $file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testPropertySetInPrivateMethod() { $this->project_checker->registerFile( getcwd() . '/somefile.php', 'foo(); } private function foo() : void { $this->a = 5; } }' ); $file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage PropertyNotSetInConstructor * @return void */ public function testPropertySetInProtectedMethod() { $this->project_checker->registerFile( getcwd() . '/somefile.php', 'foo(); } protected function foo() : void { $this->a = 5; } }' ); $file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage PropertyNotSetInConstructor * @return void */ public function testDefinedInTraitNotSetInEmptyConstructor() { $this->project_checker->registerFile( getcwd() . '/somefile.php', 'project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testDefinedInTraitSetInConstructor() { $this->project_checker->registerFile( getcwd() . '/somefile.php', 'a = "hello"; } }' ); $file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testPropertySetInNestedPrivateMethod() { $this->project_checker->registerFile( getcwd() . '/somefile.php', 'foo(); } private function foo() : void { $this->bar(); } private function bar() : void { $this->a = 5; } }' ); $file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage PropertyNotSetInConstructor * @return void */ public function testPropertySetInPrivateMethodWithIf() { $this->project_checker->registerFile( getcwd() . '/somefile.php', 'foo(); } } private function foo() : void { $this->a = 5; } }' ); $file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage PropertyNotSetInConstructor * @return void */ public function testPropertySetInPrivateMethodWithIfAndElse() { $this->project_checker->registerFile( getcwd() . '/somefile.php', 'foo(); } else { $this->bar(); } } private function foo() : void { $this->a = 5; } private function bar() : void { $this->a = 5; } }' ); $file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testPropertyArrayIssetAssertion() { $this->project_checker->registerFile( getcwd() . '/somefile.php', ' */ public $a = []; private function foo() : void { if (isset($this->a["hello"])) { bar($this->a["hello"]); } } }' ); $file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testPropertyArrayIssetAssertionWithVariableOffset() { $this->project_checker->registerFile( getcwd() . '/somefile.php', ' */ public $a = []; private function foo() : void { $b = "hello"; if (!isset($this->a[$b])) { return; } bar($this->a[$b]); } }' ); $file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testStaticPropertyArrayIssetAssertionWithVariableOffset() { $this->project_checker->registerFile( getcwd() . '/somefile.php', ' */ public static $a = []; } function foo() : void { $b = "hello"; if (!isset(A::$a[$b])) { return; } bar(A::$a[$b]); }' ); $file_checker = new FileChecker(getcwd() . '/somefile.php', $this->project_checker); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage UndefinedClass * @return void */ public function testUndefinedPropertyClass() { $stmts = self::$parser->parse('project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } }