Skip to content
Permalink
Browse files

Add error message when psalter asked to work beyond its abilities; al…

…low fixing all issues and output list of fixable issues (#1687)

* Oputput error message when asked to fix non-fixable issue

* Document ability to fix function PossiblyUndefinedGlobalVariable

* Add --issues=all option to fix all possible issues

* Add --list-supported-issues option to psalter

* Fix psalm issues

* Add newline to end of psalter help output

* Adjust messages output from psalter

* Use fwrite(STDERR, instead of die( for issue list related errors in psalter

* Restore missing exits in psalter
  • Loading branch information...
bdsl authored and muglug committed May 27, 2019
1 parent 5861476 commit 04ef20da1fb98d8ec1b3d0d7474dbdb357f9a2f5
@@ -36,7 +36,8 @@ The above example plugin converts all unnecessarily qualified classnames in your

## Supported fixes

This initial release provides support for the following alterations, corresponding to the names of issues Psalm finds:
This initial release provides support for the following alterations, corresponding to the names of issues Psalm finds.
To fix all of these at once, run `vendor/bin/psalter --issues=all`

### MissingReturnType

@@ -252,6 +253,34 @@ function foo() : string {

Running `vendor/bin/psalter --issues=PossiblyUndefinedVariable` on

```php
function foo()
{
if (rand(0, 1)) {
$a = 5;
}
echo $a;
}
```

gives

```php
function foo()
{
$a = null;
if (rand(0, 1)) {
$a = 5;
}
echo $a;
}
```


### PossiblyUndefinedGlobalVariable

Running `vendor/bin/psalter --issues=PossiblyUndefinedGlobalVariable` on

```php
if (rand(0, 1)) {
$a = 5;
@@ -0,0 +1,8 @@
<?php
namespace Psalm\Exception;
class UnsupportedIssueToFixException extends \Exception
{
}
@@ -4,15 +4,32 @@
use Psalm\Codebase;
use Psalm\Config;
use Psalm\Context;
use Psalm\Exception\UnsupportedIssueToFixException;
use Psalm\Internal\LanguageServer\{LanguageServer, ProtocolStreamReader, ProtocolStreamWriter};
use Psalm\Internal\Provider\ClassLikeStorageProvider;
use Psalm\Internal\Provider\FileProvider;
use Psalm\Internal\Provider\FileReferenceProvider;
use Psalm\Internal\Provider\ParserCacheProvider;
use Psalm\Internal\Provider\Providers;
use Psalm\Issue\InvalidFalsableReturnType;
use Psalm\Issue\InvalidNullableReturnType;
use Psalm\Issue\InvalidReturnType;
use Psalm\Issue\LessSpecificReturnType;
use Psalm\Issue\MismatchingDocblockParamType;
use Psalm\Issue\MismatchingDocblockReturnType;
use Psalm\Issue\MissingClosureReturnType;
use Psalm\Issue\MissingParamType;
use Psalm\Issue\MissingReturnType;
use Psalm\Issue\PossiblyUndefinedGlobalVariable;
use Psalm\Issue\PossiblyUndefinedVariable;
use Psalm\Issue\PossiblyUnusedMethod;
use Psalm\Issue\PossiblyUnusedProperty;
use Psalm\Issue\UnusedMethod;
use Psalm\Issue\UnusedProperty;
use Psalm\Progress\Progress;
use Psalm\Progress\VoidProgress;
use Psalm\Type;
use Psalm\Issue\CodeIssue;
/**
* @internal
@@ -156,6 +173,27 @@ class ProjectAnalyzer
self::TYPE_TEXT,
];
/**
* @var array<int, class-string<CodeIssue>>
*/
const SUPPORTED_ISSUES_TO_FIX = [
InvalidFalsableReturnType::class,
InvalidNullableReturnType::class,
InvalidReturnType::class,
LessSpecificReturnType::class,
MismatchingDocblockParamType::class,
MismatchingDocblockReturnType::class,
MissingClosureReturnType::class,
MissingParamType::class,
MissingReturnType::class,
PossiblyUndefinedGlobalVariable::class,
PossiblyUndefinedVariable::class,
PossiblyUnusedMethod::class,
PossiblyUnusedProperty::class,
UnusedMethod::class,
UnusedProperty::class,
];
/**
* @param bool $use_color
* @param bool $show_info
@@ -795,14 +833,34 @@ public function setPhpVersion(string $version)
/**
* @param array<string, bool> $issues
* @throws UnsupportedIssueToFixException
*
* @return void
*/
public function setIssuesToFix(array $issues)
{
$supported_issues_to_fix = static::getSupportedIssuesToFix();
$unsupportedIssues = array_diff(array_keys($issues), $supported_issues_to_fix);
if (! empty($unsupportedIssues)) {
throw new UnsupportedIssueToFixException(
'Psalm doesn\'t know how to fix issue(s): ' . implode(', ', $unsupportedIssues) . PHP_EOL
. 'Supported issues to fix are: ' . implode(',', $supported_issues_to_fix)
);
}
$this->issues_to_fix = $issues;
}
public function setAllIssuesToFix(): void
{
/** @var array<string, true> $keyed_issues */
$keyed_issues = array_fill_keys(static::getSupportedIssuesToFix(), true);
$this->setIssuesToFix($keyed_issues);
}
/**
* @return array<string, bool>
*
@@ -954,4 +1012,19 @@ private function getCpuCount(): int
throw new \LogicException('failed to detect number of CPUs!');
}
/**
* @return array<string>
*/
public static function getSupportedIssuesToFix(): array
{
return array_map(
/** @param class-string $issue_class */
function (string $issue_class): string {
$parts = explode('\\', $issue_class);
return end($parts);
},
self::SUPPORTED_ISSUES_TO_FIX
);
}
}
@@ -21,7 +21,7 @@
$valid_short_options = ['f:', 'm', 'h', 'r:'];
$valid_long_options = [
'help', 'debug', 'debug-by-line', 'config:', 'file:', 'root:',
'plugin:', 'issues:', 'php-version:', 'dry-run', 'safe-types',
'plugin:', 'issues:', 'list-supported-issues', 'php-version:', 'dry-run', 'safe-types',
'find-unused-code', 'threads:', 'codeowner:',
'allow-backwards-incompatible-changes:',
];
@@ -116,7 +116,11 @@ function ($arg) use ($valid_long_options, $valid_short_options) {
--php-version=PHP_MAJOR_VERSION.PHP_MINOR_VERSION
--issues=IssueType1,IssueType2
If any issues can be fixed automatically, Psalm will update the codebase
If any issues can be fixed automatically, Psalm will update the codebase. To fix as many issues as possible,
use --issues=all
--list-supported-issues
Display the list of issues that psalter knows how to fix
--find-unused-code
Include unused code as a candidate for removal
@@ -129,14 +133,19 @@ function ($arg) use ($valid_long_options, $valid_short_options) {
--allow-backwards-incompatible-changes=BOOL
Allow Psalm modify method signatures that could break code outside the project. Defaults to true.
HELP;
exit;
}
if (!isset($options['issues']) && (!isset($options['plugin']) || $options['plugin'] === false)) {
die('Please specify the issues you want to fix with --issues=IssueOne,IssueTwo '
. 'or provide a plugin that has its own manipulations with --plugin=path/to/plugin.php' . PHP_EOL);
if (!isset($options['issues']) &&
!isset($options['list-supported-issues']) &&
(!isset($options['plugin']) || $options['plugin'] === false)
) {
fwrite(STDERR, 'Please specify the issues you want to fix with --issues=IssueOne,IssueTwo or --issues=all, ' .
'or provide a plugin that has its own manipulations with --plugin=path/to/plugin.php' . PHP_EOL);
exit(1);
}
if (isset($options['root'])) {
@@ -189,6 +198,11 @@ function ($arg) use ($valid_long_options, $valid_short_options) {
new Psalm\Internal\Provider\ClassLikeStorageCacheProvider($config)
);
if (array_key_exists('list-supported-issues', $options)) {
echo implode(',', ProjectAnalyzer::getSupportedIssuesToFix()) . PHP_EOL;
exit();
}
$debug = array_key_exists('debug', $options);
$progress = $debug
? new DebugProgress()
@@ -373,7 +387,17 @@ function (string $line) : bool {
array_key_exists('dry-run', $options),
array_key_exists('safe-types', $options)
);
$project_analyzer->setIssuesToFix($keyed_issues);
if ($keyed_issues === ['all' => true]) {
$project_analyzer->setAllIssuesToFix();
} else {
try {
$project_analyzer->setIssuesToFix($keyed_issues);
} catch (\Psalm\Exception\UnsupportedIssueToFixException $e) {
fwrite(STDERR, $e->getMessage() . PHP_EOL);
exit(1);
}
}
$start_time = microtime(true);

0 comments on commit 04ef20d

Please sign in to comment.
You can’t perform that action at this time.