Skip to content

Commit ace7eee

Browse files
author
Kirill Nesmeyanov
committed
psalm est mort, vive le phpstan!
1 parent f09313d commit ace7eee

19 files changed

+139
-167
lines changed

composer.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@
2424
"jetbrains/phpstorm-attributes": "^1.0",
2525
"nikic/php-parser": "^4.19|^5.0",
2626
"phplrt/compiler": "^3.6",
27-
"phpunit/phpunit": "^10.5|^11.0",
27+
"phpstan/extension-installer": "^1.3",
28+
"phpstan/phpstan": "^1.10",
29+
"phpstan/phpstan-strict-rules": "^1.5",
30+
"phpunit/phpunit": "^10.5",
2831
"rector/rector": "^1.0",
2932
"symfony/finder": "^5.4|^6.0|^7.0",
3033
"symfony/var-dumper": "^5.4|^6.0|^7.0",
3134
"type-lang/printer": ">=1.0.0-beta2 <2.0",
3235
"type-lang/phpdoc": ">=1.0.0-beta8 <2.0",
33-
"type-lang/phpdoc-standard-tags": "^1.0",
34-
"vimeo/psalm": "^5.23"
36+
"type-lang/phpdoc-standard-tags": "^1.0"
3537
},
3638
"autoload-dev": {
3739
"psr-4": {
@@ -51,6 +53,9 @@
5153
"optimize-autoloader": true,
5254
"preferred-install": {
5355
"*": "dist"
56+
},
57+
"allow-plugins": {
58+
"phpstan/extension-installer": true
5459
}
5560
},
5661
"scripts": {
@@ -61,8 +66,8 @@
6166
"test:functional": "phpunit --testsuite=functional",
6267

6368
"linter": "@linter:check",
64-
"linter:check": "psalm --no-cache",
65-
"linter:fix": "psalm --no-cache --alter",
69+
"linter:check": "phpstan analyse --configuration phpstan.neon",
70+
"linter:baseline": "phpstan analyse --configuration phpstan.neon --generate-baseline",
6671

6772
"phpcs": "@phpcs:check",
6873
"phpcs:check": "php-cs-fixer fix --config=.php-cs-fixer.php --allow-risky=yes --dry-run --verbose --diff",

phpstan.neon

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
includes:
2+
- phar://phpstan.phar/conf/bleedingEdge.neon
3+
parameters:
4+
level: 9
5+
strictRules:
6+
allRules: true
7+
fileExtensions:
8+
- php
9+
paths:
10+
- src
11+
tmpDir: vendor/.cache.phpstan
12+
reportUnmatchedIgnoredErrors: false

psalm.xml

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/Builder.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ public function build(Context $context, mixed $result): mixed
2727
return $result;
2828
}
2929

30-
/** @psalm-suppress MixedAssignment */
3130
$result = ($this->reducers[$context->state])($context, $result);
3231

3332
if ($result instanceof Node && $result->offset === 0) {

src/Node/Identifier.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,14 @@ final class Identifier extends Node implements \Stringable
4242

4343
/**
4444
* @param non-empty-string $value
45-
*
46-
* @psalm-suppress RedundantCondition
4745
*/
4846
public function __construct(string $value)
4947
{
50-
/** @psalm-suppress PropertyTypeCoercion */
51-
$this->value = \trim($value);
48+
$value = \trim($value);
49+
50+
assert($value !== '', new \InvalidArgumentException('Identifier value cannot be empty'));
5251

53-
assert($this->value !== '', new \InvalidArgumentException(
54-
'Identifier value cannot be empty',
55-
));
52+
$this->value = $value;
5653
}
5754

5855
/**
@@ -128,13 +125,17 @@ public function __toString(): string
128125
return $this->value;
129126
}
130127

128+
/**
129+
* @return array{int<0, max>, non-empty-string}
130+
*/
131131
public function __serialize(): array
132132
{
133133
return [$this->offset, $this->value];
134134
}
135135

136136
/**
137-
* @psalm-suppress MixedAssignment
137+
* @param array{0?: int<0, max>, 1?: non-empty-string} $data
138+
* @throws \UnexpectedValueException
138139
*/
139140
public function __unserialize(array $data): void
140141
{

src/Node/Literal/BoolLiteralNode.php

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,20 @@
55
namespace TypeLang\Parser\Node\Literal;
66

77
/**
8-
* @template TValue of bool
9-
* @template-extends LiteralNode<TValue>
8+
* @template-extends LiteralNode<bool>
109
*
1110
* @psalm-consistent-constructor
12-
* @psalm-consistent-templates
11+
* @phpstan-consistent-constructor
1312
*/
1413
class BoolLiteralNode extends LiteralNode implements ParsableLiteralNodeInterface
1514
{
16-
/**
17-
* @param TValue $value
18-
*/
1915
public function __construct(
2016
public readonly bool $value,
2117
string $raw = null,
2218
) {
2319
parent::__construct($raw ?? ($value ? 'true' : 'false'));
2420
}
2521

26-
/**
27-
* @param non-empty-string $value
28-
*
29-
* @return static<bool>
30-
* @psalm-suppress MoreSpecificImplementedParamType : Strengthening the
31-
* precondition will violate the LSP, but in this case it is
32-
* acceptable.
33-
*/
3422
public static function parse(string $value): static
3523
{
3624
return new static(\strtolower($value) === 'true', $value);

src/Node/Literal/FloatLiteralNode.php

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,26 @@
55
namespace TypeLang\Parser\Node\Literal;
66

77
/**
8-
* @template TValue of float
9-
* @template-extends LiteralNode<TValue>
8+
* @template-extends LiteralNode<float>
109
*
1110
* @psalm-consistent-constructor
12-
* @psalm-consistent-templates
11+
* @phpstan-consistent-constructor
1312
*/
1413
class FloatLiteralNode extends LiteralNode implements ParsableLiteralNodeInterface
1514
{
16-
/**
17-
* @param TValue $value
18-
*/
1915
public function __construct(
2016
public readonly float $value,
2117
string $raw = null,
2218
) {
2319
parent::__construct($raw ?? (string) $this->value);
2420
}
2521

26-
/**
27-
* @param numeric-string $value
28-
*
29-
* @return static<float>
30-
* @psalm-suppress MoreSpecificImplementedParamType : Strengthening the
31-
* precondition will violate the LSP, but in this case it is
32-
* acceptable.
33-
*/
3422
public static function parse(string $value): static
3523
{
24+
if (!\is_numeric($value)) {
25+
return new static(0.0, $value);
26+
}
27+
3628
return new static((float) $value, $value);
3729
}
3830

src/Node/Literal/IntLiteralNode.php

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,26 @@
55
namespace TypeLang\Parser\Node\Literal;
66

77
/**
8-
* @template TValue of int
9-
* @template-extends LiteralNode<TValue>
8+
* @template-extends LiteralNode<int>
109
*
1110
* @psalm-consistent-constructor
12-
* @psalm-consistent-templates
11+
* @phpstan-consistent-constructor
1312
*/
1413
class IntLiteralNode extends LiteralNode implements ParsableLiteralNodeInterface
1514
{
16-
/**
17-
* @param TValue $value
18-
*/
1915
public function __construct(
2016
public readonly int $value,
2117
string $raw = null,
2218
) {
2319
parent::__construct($raw ?? (string) $this->value);
2420
}
2521

26-
/**
27-
* @param numeric-string $value
28-
*
29-
* @return static<int>
30-
* @psalm-suppress MoreSpecificImplementedParamType : Strengthening the
31-
* precondition will violate the LSP, but in this case it is
32-
* acceptable.
33-
*/
3422
public static function parse(string $value): static
3523
{
24+
if (!\is_numeric($value)) {
25+
return new static(0, $value);
26+
}
27+
3628
[$isNegative, $decimal] = self::split($value);
3729

3830
return new static($isNegative ? -$decimal : $decimal, $value);

src/Node/Literal/NullLiteralNode.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@
66

77
/**
88
* @template-extends LiteralNode<null>
9-
*
10-
* @psalm-consistent-constructor
119
*/
1210
class NullLiteralNode extends LiteralNode
1311
{
14-
final public function __construct(string $raw = null)
12+
public function __construct(string $raw = null)
1513
{
1614
parent::__construct($raw ?? 'null');
1715
}

src/Node/Literal/StringLiteralNode.php

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
namespace TypeLang\Parser\Node\Literal;
66

77
/**
8-
* @template TValue of string
9-
* @template-extends LiteralNode<TValue>
8+
* @template-extends LiteralNode<string>
109
*
1110
* @psalm-consistent-constructor
12-
* @psalm-consistent-templates
11+
* @phpstan-consistent-constructor
1312
*/
1413
class StringLiteralNode extends LiteralNode implements ParsableLiteralNodeInterface
1514
{
@@ -36,27 +35,16 @@ class StringLiteralNode extends LiteralNode implements ParsableLiteralNodeInterf
3635
'\$' => "\$",
3736
];
3837

39-
/**
40-
* @param TValue $value
41-
*/
4238
final public function __construct(
4339
public readonly string $value,
4440
string $raw = null,
4541
) {
4642
parent::__construct($raw ?? $this->value);
4743
}
4844

49-
/**
50-
* @param non-empty-string $value
51-
*
52-
* @return static<string>
53-
* @psalm-suppress MoreSpecificImplementedParamType : Strengthening the
54-
* precondition will violate the LSP, but in this case it is
55-
* acceptable.
56-
*/
5745
public static function parse(string $value): static
5846
{
59-
assert(\strlen($value) >= 2);
47+
assert(\strlen($value) >= 2, new \InvalidArgumentException('Could not parse non-quoted string'));
6048

6149
if ($value[0] === '"') {
6250
return static::createFromDoubleQuotedString($value);
@@ -67,29 +55,25 @@ public static function parse(string $value): static
6755

6856
/**
6957
* @param non-empty-string $value
70-
*
71-
* @return static<string>
7258
*/
7359
public static function createFromDoubleQuotedString(string $value): static
7460
{
75-
assert(\strlen($value) >= 2);
61+
assert(\strlen($value) >= 2, new \InvalidArgumentException('Could not parse non-quoted string'));
7662

7763
$body = \substr($value, 1, -1);
7864

79-
return static::parseEncodedValue(
65+
return self::parseEncodedValue(
8066
string: \str_replace('\"', '"', $body),
8167
raw: $value,
8268
);
8369
}
8470

8571
/**
8672
* @param non-empty-string $value
87-
*
88-
* @return static<string>
8973
*/
9074
public static function createFromSingleQuotedString(string $value): static
9175
{
92-
assert(\strlen($value) >= 2);
76+
assert(\strlen($value) >= 2, new \InvalidArgumentException('Could not parse non-quoted string'));
9377

9478
$body = \substr($value, 1, -1);
9579

@@ -146,7 +130,9 @@ private static function renderEscapeSequences(string $body): string
146130
*/
147131
private static function renderHexadecimalSequences(string $body): string
148132
{
149-
$callee = static fn(array $matches): string => \chr(\hexdec((string) $matches[1]));
133+
$callee = static fn(array $matches): string
134+
=> \chr((int) \hexdec((string) $matches[1]))
135+
;
150136

151137
/** @psalm-suppress InvalidArgument */
152138
return @\preg_replace_callback(self::HEX_SEQUENCE_PATTERN, $callee, $body) ?? $body;
@@ -161,11 +147,10 @@ private static function renderHexadecimalSequences(string $body): string
161147
private static function renderUtfSequences(string $body): string
162148
{
163149
$callee = static function (array $matches): string {
164-
$code = \hexdec((string) $matches[1]);
150+
$code = (int) \hexdec((string) $matches[1]);
165151

166-
if (\function_exists('\\mb_chr')
167-
&& ($result = \mb_chr($code)) !== false
168-
) {
152+
// @phpstan-ignore-next-line : PHPStan false-positive mb_chr evaluation
153+
if (\function_exists('\\mb_chr') && ($result = \mb_chr($code)) !== false) {
169154
return $result;
170155
}
171156

0 commit comments

Comments
 (0)