1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 13:51:54 +01:00

Adds support for fixing missing throws doc block

This commit is contained in:
asrar 2022-05-21 17:39:51 +02:00
parent 50fb396bbb
commit c6854cf567
5 changed files with 77 additions and 0 deletions

View File

@ -706,6 +706,10 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer
}
}
/**
* @var list<class-string>
*/
$missingThrowsDocblockErrors = [];
foreach ($statements_analyzer->getUncaughtThrows($context) as $possibly_thrown_exception => $codelocations) {
$is_expected = false;
@ -719,6 +723,7 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer
}
if (!$is_expected) {
$missingThrowsDocblockErrors[] = $possibly_thrown_exception;
foreach ($codelocations as $codelocation) {
// issues are suppressed in ThrowAnalyzer, CallAnalyzer, etc.
IssueBuffer::maybeAdd(
@ -732,6 +737,18 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer
}
}
if (
$codebase->alter_code
&& isset($project_analyzer->getIssuesToFix()['MissingThrowsDocblock'])
) {
$manipulator = FunctionDocblockManipulator::getForFunction(
$project_analyzer,
$this->source->getFilePath(),
$this->function
);
$manipulator->addThrowsDocblock($missingThrowsDocblockErrors);
}
if ($codebase->taint_flow_graph
&& $this->function instanceof ClassMethod
&& $cased_method_id

View File

@ -1322,6 +1322,7 @@ class ProjectAnalyzer
$supported_issues_to_fix[] = 'MissingImmutableAnnotation';
$supported_issues_to_fix[] = 'MissingPureAnnotation';
$supported_issues_to_fix[] = 'MissingThrowsDocblock';
$unsupportedIssues = array_diff(array_keys($issues), $supported_issues_to_fix);

View File

@ -96,6 +96,9 @@ class FunctionDocblockManipulator
/** @var bool */
private $is_pure = false;
/** @var list<class-string> */
private $throwsExceptions = [];
/**
* @param Closure|Function_|ClassMethod|ArrowFunction $stmt
*/
@ -395,6 +398,16 @@ class FunctionDocblockManipulator
$modified_docblock = true;
$parsed_docblock->tags['psalm-pure'] = [''];
}
if (\count($this->throwsExceptions) > 0) {
$modified_docblock = true;
$parsed_docblock->tags['throws'] = [
\array_reduce(
$this->throwsExceptions,
fn(string $throwsClause, string $exception) => $throwsClause === '' ? $exception : $throwsClause.'|'.$exception,
''
)
];
}
if ($this->new_phpdoc_return_type && $this->new_phpdoc_return_type !== $old_phpdoc_return_type) {
@ -528,6 +541,14 @@ class FunctionDocblockManipulator
$this->is_pure = true;
}
/**
* @param list<class-string> $exceptions
*/
public function addThrowsDocblock(array $exceptions): void
{
$this->throwsExceptions = $exceptions;
}
public static function clearCache(): void
{
self::$manipulators = [];

View File

@ -86,6 +86,7 @@ abstract class FileManipulationTestCase extends TestCase
$safe_types
);
$this->project_analyzer->getCodebase()->allow_backwards_incompatible_changes = $allow_backwards_incompatible_changes;
$this->project_analyzer->getConfig()->check_for_throws_docblock = true;
if (strpos(static::class, 'Unused') || strpos(static::class, 'Unnecessary')) {
$this->project_analyzer->getCodebase()->reportUnusedCode();

View File

@ -0,0 +1,37 @@
<?php
namespace Psalm\Tests\FileManipulation;
class ThrowsBlockAdditionTest extends FileManipulationTestCase
{
/**
* @return array<string,array{string,string,string,string[],bool}>
*/
public function providerValidCodeParse(): array
{
return [
'addThrowsAnnotationToFunction' => [
'<?php
function foo(string $s): string {
if("" === $s) {
throw new \InvalidArgumentException();
}
return $s;
}',
'<?php
/**
* @throws InvalidArgumentException
*/
function foo(string $s): string {
if("" === $s) {
throw new \InvalidArgumentException();
}
return $s;
}',
'7.4',
['MissingThrowsDocblock'],
true,
],
];
}
}