create(ParserFactory::PREFER_PHP7); $config = new TestConfig(); $config->throw_exception = true; } /** * @return void */ public function setUp() { FileChecker::clearCache(); $this->project_checker = new \Psalm\Checker\ProjectChecker(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testNullableMethodCall() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithTernaryGuard() { $stmts = self::$parser->parse('fooFoo() : null; } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithTernaryIfNullGuard() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithTernaryEmptyGuard() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithTernaryIsNullGuard() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithIfGuard() { $stmts = self::$parser->parse('fooFoo(); } } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testNullableMethodCallWithThis() { $stmts = self::$parser->parse('a = $a; $this->a->fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithTernaryGuardWithThis() { $stmts = self::$parser->parse('a = $a; $b = $this->a ? $this->a->fooFoo() : null; } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithTernaryIfNullGuardWithThis() { $stmts = self::$parser->parse('a = $a; $b = $this->a === null ? null : $this->a->fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithIfGuardWithThis() { $stmts = self::$parser->parse('a = $a; if ($this->a) { $this->a->fooFoo(); } } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testNullableMethodWithWrongIfGuard() { $stmts = self::$parser->parse('fooFoo(); } } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithExceptionThrown() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithRedefinitionAndElse() { $stmts = self::$parser->parse('two = 3; } $one->fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testNullableMethodWithWrongBooleanIfGuard() { $stmts = self::$parser->parse('fooFoo(); } } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithBooleanIfGuard() { $stmts = self::$parser->parse('fooFoo(); } } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithNonNullBooleanIfGuard() { $stmts = self::$parser->parse('fooFoo(); } } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithNonNullBooleanIfGuardAndBooleanAnd() { $stmts = self::$parser->parse('fooFoo(); } } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodInConditionWithIfGuardBefore() { $stmts = self::$parser->parse('a && $one->fooFoo()) { // do something } } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testNullableMethodWithWrongIfGuardBefore() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithBooleanIfGuardBefore() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testNullableMethodWithWrongBooleanIfGuardBefore() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithGuardedRedefinition() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithGuardedRedefinitionInElse() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testMethodWithMeaninglessCheck() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testNullableMethodWithGuardedNestedIncompleteRedefinition() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithGuardedNestedRedefinition() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithGuardedSwitchRedefinition() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testNullableMethodWithGuardedSwitchRedefinitionNoDefault() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testNullableMethodWithGuardedSwitchRedefinitionEmptyDefault() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithGuardedSwitchRedefinitionDueToException() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithGuardedSwitchThatAlwaysReturns() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithGuardedNestedRedefinitionWithReturn() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithGuardedNestedRedefinitionWithElseReturn() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testNullableMethodWithGuardedNestedRedefinitionWithUselessElseReturn() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithGuardedNestedRedefinitionWithElseifReturn() { $stmts = self::$parser->parse('fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithGuardedSwitchBreak() { $stmts = self::$parser->parse('fooFoo(); break; } } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullableMethodWithGuardedRedefinitionOnThis() { $stmts = self::$parser->parse('one = $one; if ($this->one === null) { $this->one = new One(); } $this->one->fooFoo(); } }'); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testArrayUnionTypeAssertion() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array', (string) $context->vars_in_scope['$ids']); } /** * @return void */ public function testArrayUnionTypeAssertionWithIsArray() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('array', (string) $context->vars_in_scope['$ids']); } /** * @return void */ public function test2DArrayUnionTypeAssertionWithIsArray() { $stmts = self::$parser->parse('>|null */ function foo() { $ids = rand(0, 1) ? [["hello"]] : null; if (is_array($ids)) { return $ids; } return null; } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testVariableReassignment() { $stmts = self::$parser->parse('barBar(); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testVariableReassignmentInIf() { $stmts = self::$parser->parse('barBar(); } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testVariableReassignmentInIfWithOutsideCall() { $stmts = self::$parser->parse('barBar(); } $one->barBar(); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testUnionTypeFlow() { $stmts = self::$parser->parse('fooFoo(); } else { if ($var instanceof Two) { $var->barBar(); } else if ($var) { $var->baz(); } } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testUnionTypeFlowWithThrow() { $stmts = self::$parser->parse('fooFoo(); } } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testUnionTypeFlowWithElseif() { $stmts = self::$parser->parse('fooFoo(); } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testUnnecessaryInstanceof() { $stmts = self::$parser->parse('fooFoo(); } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testUnNegatableInstanceof() { $stmts = self::$parser->parse('fooFoo(); } else { // do something } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testTypeAdjustment() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('int|string', (string) $context->vars_in_scope['$var']); } /** * @return void */ public function testTypeMixedAdjustment() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('int|string', (string) $context->vars_in_scope['$var']); } /** * @return void */ public function testTypeAdjustmentIfNull() { $stmts = self::$parser->parse(' 5 ? new A : null; if ($var === null) { $var = new B; } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('A|B', (string) $context->vars_in_scope['$var']); } /** * @return void */ public function testWhileTrue() { $stmts = self::$parser->parse('fooFoo()) { $row[0] = "bad"; } } } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testWrongParam() { $stmts = self::$parser->parse('barBar(5); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testPassingParam() { $stmts = self::$parser->parse('barBar(new A); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullToNullableParam() { $stmts = self::$parser->parse('barBar(null); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testIntToNullableObjectParam() { $stmts = self::$parser->parse('barBar(5); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testObjectToNullableObjectParam() { $stmts = self::$parser->parse('barBar(new A); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @expectedException \Psalm\Exception\CodeException * @return void */ public function testParamCoercionWithBadArg() { $stmts = self::$parser->parse('barBar(); } } } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testParamCoercion() { $stmts = self::$parser->parse('barBar(); } } } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testParamElseifCoercion() { $stmts = self::$parser->parse('barBar(); } elseif ($a instanceof C) { $a->baz(); } } } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testAssignInsideForeach() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertSame('bool', (string) $context->vars_in_scope['$b']); } /** * @return void */ public function testAssignInsideForeachWithBreak() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertSame('bool', (string) $context->vars_in_scope['$b']); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage NullReference * @return void */ public function testNullCheckInsideForeachWithNoLeaveStatement() { $stmts = self::$parser->parse(' */ public static function loadMultiple() { return [new A, null]; } /** @return void */ public function barBar() { } } foreach (A::loadMultiple() as $a) { if ($a === null) { // do nothing } $a->barBar(); } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testNullCheckInsideForeachWithContinue() { $stmts = self::$parser->parse(' */ public static function loadMultiple() { return [new A, null]; } /** @return void */ public function barBar() { } } foreach (A::loadMultiple() as $a) { if ($a === null) { continue; } $a->barBar(); } '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods(); } /** * @return void */ public function testPlusPlus() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertSame('int', (string) $context->vars_in_scope['$a']); } /** * @return void */ public function testTypedValueAssertion() { $context = new Context(); $stmts = self::$parser->parse('project_checker, $stmts); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testIssetWithSimpleAssignment() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testIssetWithMultipleAssignments() { $stmts = self::$parser->parse(' 2) { $arr = [5 => [3 => "hello"]]; } if (isset($arr[$a = 5][$b = 3])) { } echo $a; echo $b; '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } }