file_provider = new Provider\FakeFileProvider(); } /** * @dataProvider providerValidCodeParse * * @param string $input_code * @param string $output_code * @param string $php_version * @param string[] $issues_to_fix * @param bool $safe_types * * @return void */ public function testValidCode($input_code, $output_code, $php_version, array $issues_to_fix, $safe_types) { $test_name = $this->getTestName(); if (strpos($test_name, 'PHP7-') !== false) { if (version_compare(PHP_VERSION, '7.0.0dev', '<')) { $this->markTestSkipped('Test case requires PHP 7.'); return; } } elseif (strpos($test_name, 'SKIPPED-') !== false) { $this->markTestSkipped('Skipped due to a bug.'); } $config = new TestConfig(); $this->project_analyzer = new \Psalm\Internal\Analyzer\ProjectAnalyzer( $config, new \Psalm\Internal\Provider\Providers( $this->file_provider, new Provider\FakeParserCacheProvider() ) ); if (empty($issues_to_fix)) { $config->addPluginPath('examples/plugins/ClassUnqualifier.php'); $config->initializePlugins($this->project_analyzer); } $context = new Context(); $file_path = self::$src_dir_path . 'somefile.php'; $this->addFile( $file_path, $input_code ); list($php_major_version, $php_minor_version) = explode('.', $php_version); $keyed_issues_to_fix = []; foreach ($issues_to_fix as $issue) { $keyed_issues_to_fix[$issue] = true; } $this->project_analyzer->setIssuesToFix($keyed_issues_to_fix); $this->project_analyzer->alterCodeAfterCompletion( (int) $php_major_version, (int) $php_minor_version, false, $safe_types ); $this->analyzeFile($file_path, $context); $this->project_analyzer->getCodebase()->analyzer->updateFile($file_path, false); $this->assertSame($output_code, $this->project_analyzer->getCodebase()->getFileContents($file_path)); } /** * @return array */ public function providerValidCodeParse() { return [ 'addMissingVoidReturnType56' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' "hello"] : ["a" => "goodbye", "b" => "hello again"]; }', ' "hello"] : ["a" => "goodbye", "b" => "hello again"]; }', '7.0', ['MissingReturnType'], true, ], 'addMissingObjectLikeReturnTypeWithEmptyArray' => [ ' 1, "b" => 2, ]; }', ' 1, "b" => 2, ]; }', '7.0', ['MissingReturnType'], true, ], 'addMissingObjectLikeReturnTypeSeparateStatements70' => [ ' "hello", "b" => "hello again"]; } if (rand(0, 1)) { return ["a" => "hello", "b" => "hello again"]; } return ["a" => "goodbye"]; }', ' "hello", "b" => "hello again"]; } if (rand(0, 1)) { return ["a" => "hello", "b" => "hello again"]; } return ["a" => "goodbye"]; }', '7.0', ['MissingReturnType'], true, ], 'addMissingStringArrayReturnTypeFromCall71' => [ ' */ function bar(): array { return foo(); }', '7.1', ['MissingReturnType'], true, ], 'addMissingDocblockStringArrayReturnTypeFromCall71' => [ ' */ function bar() { return foo(); }', '7.1', ['MissingReturnType'], true, ], 'addMissingNullableStringReturnType71' => [ ' [ ' [ ' [ ' [ ' [ ' */ function bar(): iterable { return [1, 2, 3]; }', ' */ function foo(): iterable { return bar(); } /** @return iterable */ function bar(): iterable { return [1, 2, 3]; }', '7.1', ['MissingReturnType'], false, ], 'addMissingNullableReturnTypeInDocblockOnly71' => [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ ' [ '