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 testClassTemplate() { $stmts = self::$parser->parse('T = $T; } /** * @return T */ public function bar() { $t = $this->T; return new $t(); } } $at = "A"; /** @var Foo */ $afoo = new Foo($at); $afoo_bar = $afoo->bar(); $bfoo = new Foo(B::class); $bfoo_bar = $bfoo->bar(); $cfoo = new Foo("C"); $cfoo_bar = $cfoo->bar(); $dt = "D"; $dfoo = new Foo($dt); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('Foo', (string) $context->vars_in_scope['$afoo']); $this->assertEquals('A', (string) $context->vars_in_scope['$afoo_bar']); $this->assertEquals('Foo', (string) $context->vars_in_scope['$bfoo']); $this->assertEquals('B', (string) $context->vars_in_scope['$bfoo_bar']); $this->assertEquals('Foo', (string) $context->vars_in_scope['$cfoo']); $this->assertEquals('C', (string) $context->vars_in_scope['$cfoo_bar']); $this->assertEquals('Foo', (string) $context->vars_in_scope['$dfoo']); } /** * @return void */ public function testClassTemplateContainer() { Config::getInstance()->setCustomErrorLevel('MixedOperand', Config::REPORT_SUPPRESS); $stmts = self::$parser->parse('obj = $obj; } /** * @return T */ public function bar() { return $this->obj; } public function __toString() : string { return "hello " . $this->obj; } } $afoo = new Foo(new A()); $afoo_bar = $afoo->bar(); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); $this->assertEquals('Foo', (string) $context->vars_in_scope['$afoo']); $this->assertEquals('A', (string) $context->vars_in_scope['$afoo_bar']); } /** * @return void */ public function testPhanTuple() { $stmts = self::$parser->parse('_0 = $_0; } /** * @return int * The arity of this tuple */ public function arity() : int { return (int)static::ARITY; } /** * @return array * An array of all elements in this tuple. */ public function toArray() : array { return [ $this->_0, ]; } } /** * A tuple of 2 elements. * * @template T0 * The type of element zero * * @template T1 * The type of element one * * @inherits Tuple1 */ class Tuple2 extends Tuple1 { /** @var int */ const ARITY = 2; /** @var T1 */ public $_1; /** * @param T0 $_0 * The 0th element * * @param T1 $_1 * The 1st element */ public function __construct($_0, $_1) { parent::__construct($_0); $this->_1 = $_1; } /** * @return array * An array of all elements in this tuple. */ public function toArray() : array { return [ $this->_0, $this->_1, ]; } } $a = new Tuple2("cool", 5); /** @return void */ function takes_int(int $i) {} /** @return void */ function takes_string(string $s) {} takes_string($a->_0); takes_int($a->_1); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testValidTemplatedType() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage InvalidScalarArgument * @return void */ public function testInvalidTemplatedType() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testValidTemplatedStaticMethodType() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage InvalidScalarArgument * @return void */ public function testInvalidTemplatedStaticMethodType() { $stmts = self::$parser->parse('project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testValidTemplatedInstanceMethodType() { $stmts = self::$parser->parse('foo("string")); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @expectedException \Psalm\Exception\CodeException * @expectedExceptionMessage InvalidScalarArgument * @return void */ public function testInvalidTemplatedInstanceMethodType() { $stmts = self::$parser->parse('foo(4)); '); $file_checker = new FileChecker('somefile.php', $this->project_checker, $stmts); $context = new Context(); $file_checker->visitAndAnalyzeMethods($context); } /** * @return void */ public function testGenericArrayKeys() { $stmts = self::$parser->parse(' $arr * @return array */ function my_array_keys($arr) { return array_keys($arr); } $a = my_array_keys(["hello" => 5, "goodbye" => new Exception()]); '); $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 testGenericArrayReverse() { $stmts = self::$parser->parse(' $arr * @return array */ function my_array_reverse($arr) { return array_reverse($arr); } $b = my_array_reverse(["hello" => 5, "goodbye" => 6]); '); $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['$b']); } }