1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00
This commit is contained in:
Daniil Gentili 2023-10-22 20:11:28 +02:00
parent 71483b72aa
commit e8b7b30043
9 changed files with 58 additions and 100 deletions

View File

@ -44,6 +44,14 @@
- [BC] `Psalm\CodeLocation\Raw`, `Psalm\CodeLocation\ParseErrorLocation`, `Psalm\CodeLocation\DocblockTypeLocation`, `Psalm\Report\CountReport`, `Psalm\Type\Atomic\TNonEmptyArray` are now all final. - [BC] `Psalm\CodeLocation\Raw`, `Psalm\CodeLocation\ParseErrorLocation`, `Psalm\CodeLocation\DocblockTypeLocation`, `Psalm\Report\CountReport`, `Psalm\Type\Atomic\TNonEmptyArray` are now all final.
- [BC] `Psalm\Config` is now final.
- [BC] The return type of `Psalm\Plugin\ArgTypeInferer::infer` changed from `Union|false` to `Union|null`
- [BC] The `extra_types` property and `setIntersectionTypes` method of `Psalm\Type\Atomic\TTypeAlias` were removed.
- [BC] Methods `convertSeverity` and `calculateFingerprint` of `Psalm\Report\CodeClimateReport` were removed.
# Upgrading from Psalm 4 to Psalm 5 # Upgrading from Psalm 4 to Psalm 5
## Changed ## Changed

View File

@ -69,7 +69,6 @@ use function file_get_contents;
use function flock; use function flock;
use function fopen; use function fopen;
use function function_exists; use function function_exists;
use function get_class;
use function get_defined_constants; use function get_defined_constants;
use function get_defined_functions; use function get_defined_functions;
use function getcwd; use function getcwd;
@ -98,7 +97,9 @@ use function rtrim;
use function scandir; use function scandir;
use function sha1; use function sha1;
use function simplexml_import_dom; use function simplexml_import_dom;
use function str_contains;
use function str_replace; use function str_replace;
use function str_starts_with;
use function strlen; use function strlen;
use function strpos; use function strpos;
use function strrpos; use function strrpos;
@ -127,13 +128,13 @@ use const SCANDIR_SORT_NONE;
* @psalm-suppress PropertyNotSetInConstructor * @psalm-suppress PropertyNotSetInConstructor
* @psalm-consistent-constructor * @psalm-consistent-constructor
*/ */
class Config final class Config
{ {
private const DEFAULT_FILE_NAME = 'psalm.xml'; private const DEFAULT_FILE_NAME = 'psalm.xml';
public const CONFIG_NAMESPACE = 'https://getpsalm.org/schema/config'; final public const CONFIG_NAMESPACE = 'https://getpsalm.org/schema/config';
public const REPORT_INFO = 'info'; final public const REPORT_INFO = 'info';
public const REPORT_ERROR = 'error'; final public const REPORT_ERROR = 'error';
public const REPORT_SUPPRESS = 'suppress'; final public const REPORT_SUPPRESS = 'suppress';
/** /**
* @var array<string> * @var array<string>
@ -172,7 +173,7 @@ class Config
* *
* @var array<int, lowercase-string> * @var array<int, lowercase-string>
*/ */
protected array $universal_object_crates; private array $universal_object_crates;
/** /**
* @var static|null * @var static|null
@ -222,7 +223,7 @@ class Config
protected ?ProjectFileFilter $project_files = null; protected ?ProjectFileFilter $project_files = null;
protected ?ProjectFileFilter $extra_files = null; private ?ProjectFileFilter $extra_files = null;
/** /**
* The base directory of this config file * The base directory of this config file
@ -426,7 +427,7 @@ class Config
private ?IncludeCollector $include_collector = null; private ?IncludeCollector $include_collector = null;
protected ?TaintAnalysisFileFilter $taint_analysis_ignored_files = null; private ?TaintAnalysisFileFilter $taint_analysis_ignored_files = null;
/** /**
* @var bool whether to emit a backtrace of emitted issues to stderr * @var bool whether to emit a backtrace of emitted issues to stderr
@ -874,7 +875,6 @@ class Config
/** /**
* @param non-empty-string $file_contents * @param non-empty-string $file_contents
* @psalm-suppress MixedAssignment * @psalm-suppress MixedAssignment
* @psalm-suppress MixedArgument
* @psalm-suppress MixedPropertyFetch * @psalm-suppress MixedPropertyFetch
* @throws ConfigException * @throws ConfigException
*/ */
@ -963,15 +963,15 @@ class Config
if (file_exists($composer_json_path)) { if (file_exists($composer_json_path)) {
$composer_json_contents = file_get_contents($composer_json_path); $composer_json_contents = file_get_contents($composer_json_path);
assert($composer_json_contents !== false); assert($composer_json_contents !== false);
$composer_json = json_decode($composer_json_contents, true); $composer_json = json_decode($composer_json_contents, true, 512, JSON_THROW_ON_ERROR);
if (!is_array($composer_json)) { if (!is_array($composer_json)) {
throw new UnexpectedValueException('Invalid composer.json at ' . $composer_json_path); throw new UnexpectedValueException('Invalid composer.json at ' . $composer_json_path);
} }
} }
$required_extensions = []; $required_extensions = [];
foreach (($composer_json["require"] ?? []) as $required => $_) { foreach (($composer_json["require"] ?? []) as $required => $_) {
if (strpos($required, "ext-") === 0) { if (str_starts_with((string) $required, "ext-")) {
$required_extensions[strtolower(substr($required, 4))] = true; $required_extensions[strtolower(substr((string) $required, 4))] = true;
} }
} }
foreach ($required_extensions as $required_ext => $_) { foreach ($required_extensions as $required_ext => $_) {
@ -1649,7 +1649,7 @@ class Config
try { try {
$file_storage = $codebase->file_storage_provider->get($file_path); $file_storage = $codebase->file_storage_provider->get($file_path);
$dependent_files += $file_storage->required_by_file_paths; $dependent_files += $file_storage->required_by_file_paths;
} catch (InvalidArgumentException $e) { } catch (InvalidArgumentException) {
// do nothing // do nothing
} }
} }
@ -1700,7 +1700,7 @@ class Config
public function getReportingLevelForIssue(CodeIssue $e): string public function getReportingLevelForIssue(CodeIssue $e): string
{ {
$fqcn_parts = explode('\\', get_class($e)); $fqcn_parts = explode('\\', $e::class);
$issue_type = array_pop($fqcn_parts); $issue_type = array_pop($fqcn_parts);
$reporting_level = null; $reporting_level = null;
@ -1765,17 +1765,17 @@ class Config
return null; return null;
} }
if (strpos($issue_type, 'Possibly') === 0) { if (str_starts_with($issue_type, 'Possibly')) {
$stripped_issue_type = (string) preg_replace('/^Possibly(False|Null)?/', '', $issue_type, 1); $stripped_issue_type = (string) preg_replace('/^Possibly(False|Null)?/', '', $issue_type, 1);
if (strpos($stripped_issue_type, 'Invalid') === false && strpos($stripped_issue_type, 'Un') !== 0) { if (!str_contains($stripped_issue_type, 'Invalid') && !str_starts_with($stripped_issue_type, 'Un')) {
$stripped_issue_type = 'Invalid' . $stripped_issue_type; $stripped_issue_type = 'Invalid' . $stripped_issue_type;
} }
return $stripped_issue_type; return $stripped_issue_type;
} }
if (strpos($issue_type, 'Tainted') === 0) { if (str_starts_with($issue_type, 'Tainted')) {
return 'TaintedInput'; return 'TaintedInput';
} }
@ -2298,7 +2298,7 @@ class Config
$codebase->classlikes->forgetMissingClassLikes(); $codebase->classlikes->forgetMissingClassLikes();
$this->include_collector->runAndCollect( $this->include_collector->runAndCollect(
[$this, 'requireAutoloader'], $this->requireAutoloader(...),
); );
} }
@ -2324,7 +2324,8 @@ class Config
} }
} }
public function getComposerFilePathForClassLike(string $fq_classlike_name): string|false /** @return string|false */
public function getComposerFilePathForClassLike(string $fq_classlike_name): string|bool
{ {
if (!$this->composer_class_loader) { if (!$this->composer_class_loader) {
return false; return false;
@ -2502,7 +2503,7 @@ class Config
$composer_json_contents = file_get_contents($composer_json_path); $composer_json_contents = file_get_contents($composer_json_path);
assert($composer_json_contents !== false); assert($composer_json_contents !== false);
$composer_json = json_decode($composer_json_contents, true, 512, JSON_THROW_ON_ERROR); $composer_json = json_decode($composer_json_contents, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) { } catch (JsonException) {
$composer_json = null; $composer_json = null;
} }

View File

@ -13,19 +13,16 @@ use Psalm\Type\Union;
final class ArgTypeInferer final class ArgTypeInferer
{ {
private Context $context;
private StatementsAnalyzer $statements_analyzer;
/** /**
* @internal * @internal
*/ */
public function __construct(Context $context, StatementsAnalyzer $statements_analyzer) public function __construct(
{ private readonly Context $context,
$this->context = $context; private readonly StatementsAnalyzer $statements_analyzer,
$this->statements_analyzer = $statements_analyzer; ) {
} }
public function infer(PhpParser\Node\Arg $arg): false|Union public function infer(PhpParser\Node\Arg $arg): null|Union
{ {
$already_inferred_type = $this->statements_analyzer->node_data->getType($arg->value); $already_inferred_type = $this->statements_analyzer->node_data->getType($arg->value);
@ -34,7 +31,7 @@ final class ArgTypeInferer
} }
if (ExpressionAnalyzer::analyze($this->statements_analyzer, $arg->value, $this->context) === false) { if (ExpressionAnalyzer::analyze($this->statements_analyzer, $arg->value, $this->context) === false) {
return false; return null;
} }
return $this->statements_analyzer->node_data->getType($arg->value) ?? Type::getMixed(); return $this->statements_analyzer->node_data->getType($arg->value) ?? Type::getMixed();

View File

@ -28,7 +28,7 @@ final class CodeClimateReport extends Report
$options = $this->pretty ? Json::PRETTY : Json::DEFAULT; $options = $this->pretty ? Json::PRETTY : Json::DEFAULT;
$issues_data = array_map( $issues_data = array_map(
[$this, 'mapToNewStructure'], $this->mapToNewStructure(...),
$this->issues_data, $this->issues_data,
); );
@ -39,7 +39,7 @@ final class CodeClimateReport extends Report
* convert our own severity to CodeClimate format * convert our own severity to CodeClimate format
* Values can be : info, minor, major, critical, or blocker * Values can be : info, minor, major, critical, or blocker
*/ */
protected function convertSeverity(string $input): string private function convertSeverity(string $input): string
{ {
if (Config::REPORT_INFO === $input) { if (Config::REPORT_INFO === $input) {
return 'info'; return 'info';
@ -58,7 +58,7 @@ final class CodeClimateReport extends Report
/** /**
* calculate a unique fingerprint for a given issue * calculate a unique fingerprint for a given issue
*/ */
protected function calculateFingerprint(IssueData $issue): string private function calculateFingerprint(IssueData $issue): string
{ {
return md5($issue->type.$issue->message.$issue->file_name.$issue->from.$issue->to); return md5($issue->type.$issue->message.$issue->file_name.$issue->from.$issue->to);
} }

View File

@ -6,55 +6,17 @@ namespace Psalm\Type\Atomic;
use Psalm\Type\Atomic; use Psalm\Type\Atomic;
use function array_map;
use function implode;
/** /**
* @psalm-immutable * @psalm-immutable
*/ */
final class TTypeAlias extends Atomic final class TTypeAlias extends Atomic
{ {
/** public function __construct(
* @var array<string, TTypeAlias>|null public string $declaring_fq_classlike_name,
* @deprecated type aliases are resolved within {@see TypeParser::resolveTypeAliases()} and therefore the public string $alias_name,
* referencing type(s) are part of other intersection types. The intersection types are not set anymore ) {
* and with v6 this property along with its related methods will get removed.
*/
public ?array $extra_types = null;
public string $declaring_fq_classlike_name;
public string $alias_name;
/**
* @param array<string, TTypeAlias>|null $extra_types
*/
public function __construct(string $declaring_fq_classlike_name, string $alias_name, ?array $extra_types = null)
{
$this->declaring_fq_classlike_name = $declaring_fq_classlike_name;
$this->alias_name = $alias_name;
/** @psalm-suppress DeprecatedProperty For backwards compatibility, we have to keep this here. */
$this->extra_types = $extra_types;
parent::__construct(true); parent::__construct(true);
} }
/**
* @param array<string, TTypeAlias>|null $extra_types
* @deprecated type aliases are resolved within {@see TypeParser::resolveTypeAliases()} and therefore the
* referencing type(s) are part of other intersection types. This method will get removed with v6.
* @psalm-suppress PossiblyUnusedMethod For backwards compatibility, we have to keep this here.
*/
public function setIntersectionTypes(?array $extra_types): self
{
/** @psalm-suppress DeprecatedProperty For backwards compatibility, we have to keep this here. */
if ($extra_types === $this->extra_types) {
return $this;
}
return new self(
$this->declaring_fq_classlike_name,
$this->alias_name,
$extra_types,
);
}
public function getKey(bool $include_extra = true): string public function getKey(bool $include_extra = true): string
{ {
@ -63,17 +25,6 @@ final class TTypeAlias extends Atomic
public function getId(bool $exact = true, bool $nested = false): string public function getId(bool $exact = true, bool $nested = false): string
{ {
/** @psalm-suppress DeprecatedProperty For backwards compatibility, we have to keep this here. */
if ($this->extra_types) {
return $this->getKey() . '&' . implode(
'&',
array_map(
static fn(Atomic $type): string => $type->getId($exact, true),
$this->extra_types,
),
);
}
return $this->getKey(); return $this->getKey();
} }

View File

@ -632,7 +632,7 @@ class EnumTest extends TestCase
$foo = FooEnum::Foo->value; $foo = FooEnum::Foo->value;
noop($foo); noop($foo);
noop(FooEnum::Foo->value); noop(FooEnum::Foo->value);
PHP, PHP,
'assertions' => [], 'assertions' => [],
'ignored_issues' => [], 'ignored_issues' => [],
'php_version' => '8.1', 'php_version' => '8.1',

View File

@ -25,9 +25,9 @@ class PsalmPluginTest extends TestCase
{ {
use MockeryPHPUnitIntegration; use MockeryPHPUnitIntegration;
private PluginList&MockInterface $plugin_list; private MockInterface $plugin_list;
private PluginListFactory&MockInterface $plugin_list_factory; private MockInterface $plugin_list_factory;
private Application $app; private Application $app;

View File

@ -712,6 +712,7 @@ class ReportOutputTest extends TestCase
$issue_data = [ $issue_data = [
[ [
'link' => 'https://psalm.dev/024',
'severity' => 'error', 'severity' => 'error',
'line_from' => 3, 'line_from' => 3,
'line_to' => 3, 'line_to' => 3,
@ -727,13 +728,13 @@ class ReportOutputTest extends TestCase
'snippet_to' => 83, 'snippet_to' => 83,
'column_from' => 10, 'column_from' => 10,
'column_to' => 26, 'column_to' => 26,
'error_level' => -1,
'shortcode' => 24, 'shortcode' => 24,
'link' => 'https://psalm.dev/024', 'error_level' => -1,
'taint_trace' => null, 'taint_trace' => null,
'other_references' => null, 'other_references' => null,
], ],
[ [
'link' => 'https://psalm.dev/138',
'severity' => 'error', 'severity' => 'error',
'line_from' => 3, 'line_from' => 3,
'line_to' => 3, 'line_to' => 3,
@ -749,13 +750,13 @@ class ReportOutputTest extends TestCase
'snippet_to' => 83, 'snippet_to' => 83,
'column_from' => 10, 'column_from' => 10,
'column_to' => 26, 'column_to' => 26,
'error_level' => 1,
'shortcode' => 138, 'shortcode' => 138,
'link' => 'https://psalm.dev/138', 'error_level' => 1,
'taint_trace' => null, 'taint_trace' => null,
'other_references' => null, 'other_references' => null,
], ],
[ [
'link' => 'https://psalm.dev/047',
'severity' => 'error', 'severity' => 'error',
'line_from' => 2, 'line_from' => 2,
'line_to' => 2, 'line_to' => 2,
@ -771,13 +772,13 @@ class ReportOutputTest extends TestCase
'snippet_to' => 56, 'snippet_to' => 56,
'column_from' => 42, 'column_from' => 42,
'column_to' => 49, 'column_to' => 49,
'error_level' => 1,
'shortcode' => 47, 'shortcode' => 47,
'link' => 'https://psalm.dev/047', 'error_level' => 1,
'taint_trace' => null, 'taint_trace' => null,
'other_references' => null, 'other_references' => null,
], ],
[ [
'link' => 'https://psalm.dev/020',
'severity' => 'error', 'severity' => 'error',
'line_from' => 8, 'line_from' => 8,
'line_to' => 8, 'line_to' => 8,
@ -793,13 +794,13 @@ class ReportOutputTest extends TestCase
'snippet_to' => 172, 'snippet_to' => 172,
'column_from' => 6, 'column_from' => 6,
'column_to' => 15, 'column_to' => 15,
'error_level' => -1,
'shortcode' => 20, 'shortcode' => 20,
'link' => 'https://psalm.dev/020', 'error_level' => -1,
'taint_trace' => null, 'taint_trace' => null,
'other_references' => null, 'other_references' => null,
], ],
[ [
'link' => 'https://psalm.dev/126',
'severity' => 'info', 'severity' => 'info',
'line_from' => 17, 'line_from' => 17,
'line_to' => 17, 'line_to' => 17,
@ -815,9 +816,8 @@ class ReportOutputTest extends TestCase
'snippet_to' => 277, 'snippet_to' => 277,
'column_from' => 6, 'column_from' => 6,
'column_to' => 8, 'column_to' => 8,
'error_level' => 3,
'shortcode' => 126, 'shortcode' => 126,
'link' => 'https://psalm.dev/126', 'error_level' => 3,
'taint_trace' => null, 'taint_trace' => null,
'other_references' => null, 'other_references' => null,
], ],

View File

@ -61,7 +61,8 @@ class TestConfig extends Config
</projectFiles>'; </projectFiles>';
} }
public function getComposerFilePathForClassLike(string $fq_classlike_name): string|false /** @return false */
public function getComposerFilePathForClassLike(string $fq_classlike_name): bool
{ {
return false; return false;
} }