Skip to content

Commit a2181b0

Browse files
committed
fixed PHPStan errors
1 parent e7d0fda commit a2181b0

12 files changed

Lines changed: 132 additions & 89 deletions

phpstan-baseline.neon

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,115 @@
11
parameters:
22
ignoreErrors:
33
-
4-
message: '#^Match expression does not handle remaining value\: true$#'
4+
message: '#^Parameter \#1 \$class of method Nette\\PhpGenerator\\Printer\:\:printClass\(\) expects Nette\\PhpGenerator\\ClassType\|Nette\\PhpGenerator\\EnumType\|Nette\\PhpGenerator\\InterfaceType\|Nette\\PhpGenerator\\TraitType, \$this\(Nette\\PhpGenerator\\ClassLike\) given\.$#'
5+
identifier: argument.type
56
count: 1
67
path: src/PhpGenerator/ClassLike.php
78

89
-
9-
message: '#^Method Nette\\PhpGenerator\\ClassType\:\:addTrait\(\) has parameter \$deprecatedParam with no value type specified in iterable type array\.$#'
10+
message: '#^Method Nette\\PhpGenerator\\Closure\:\:from\(\) should return Nette\\PhpGenerator\\Closure but returns Nette\\PhpGenerator\\Closure\|Nette\\PhpGenerator\\GlobalFunction\.$#'
11+
identifier: return.type
1012
count: 1
11-
path: src/PhpGenerator/ClassType.php
13+
path: src/PhpGenerator/Closure.php
1214

1315
-
14-
message: '#^Method Nette\\PhpGenerator\\EnumType\:\:addTrait\(\) has parameter \$deprecatedParam with no value type specified in iterable type array\.$#'
16+
message: '#^Call to an undefined method object\:\:__unserialize\(\)\.$#'
17+
identifier: method.notFound
1518
count: 1
16-
path: src/PhpGenerator/EnumType.php
17-
18-
-
19-
message: '#^Access to an undefined property PhpParser\\Node\:\:\$attrGroups\.$#'
20-
count: 1
21-
path: src/PhpGenerator/Extractor.php
22-
23-
-
24-
message: '#^Access to an undefined property PhpParser\\Node\\Expr\:\:\$value\.$#'
25-
count: 1
26-
path: src/PhpGenerator/Extractor.php
19+
path: src/PhpGenerator/Dumper.php
2720

2821
-
2922
message: '#^Call to an undefined method Nette\\PhpGenerator\\ClassLike\:\:addConstant\(\)\.$#'
23+
identifier: method.notFound
3024
count: 1
3125
path: src/PhpGenerator/Extractor.php
3226

3327
-
3428
message: '#^Call to an undefined method Nette\\PhpGenerator\\ClassLike\:\:addMethod\(\)\.$#'
29+
identifier: method.notFound
3530
count: 1
3631
path: src/PhpGenerator/Extractor.php
3732

3833
-
3934
message: '#^Call to an undefined method Nette\\PhpGenerator\\ClassLike\:\:addProperty\(\)\.$#'
35+
identifier: method.notFound
4036
count: 1
4137
path: src/PhpGenerator/Extractor.php
4238

4339
-
4440
message: '#^Call to an undefined method Nette\\PhpGenerator\\ClassLike\:\:addTrait\(\)\.$#'
41+
identifier: method.notFound
4542
count: 1
4643
path: src/PhpGenerator/Extractor.php
4744

4845
-
49-
message: '#^Method Nette\\PhpGenerator\\Extractor\:\:addCommentAndAttributes\(\) has parameter \$element with no type specified\.$#'
46+
message: '#^Call to an undefined method Nette\\PhpGenerator\\ClassLike\|Nette\\PhpGenerator\\Constant\|Nette\\PhpGenerator\\EnumCase\|Nette\\PhpGenerator\\GlobalFunction\|Nette\\PhpGenerator\\Method\|Nette\\PhpGenerator\\Parameter\|Nette\\PhpGenerator\\Property\|Nette\\PhpGenerator\\PropertyHook\|Nette\\PhpGenerator\\TraitUse\:\:addAttribute\(\)\.$#'
47+
identifier: method.notFound
5048
count: 1
5149
path: src/PhpGenerator/Extractor.php
5250

5351
-
54-
message: '#^Property class@anonymous/PhpGenerator/Extractor\.php\:176\:\:\$callback has no type specified\.$#'
52+
message: '#^Parameter \#1 \$class of method Nette\\PhpGenerator\\Extractor\:\:addEnumCaseToClass\(\) expects Nette\\PhpGenerator\\EnumType, Nette\\PhpGenerator\\ClassLike given\.$#'
53+
identifier: argument.type
5554
count: 1
5655
path: src/PhpGenerator/Extractor.php
5756

5857
-
5958
message: '#^Variable \$trait might not be defined\.$#'
60-
count: 2
61-
path: src/PhpGenerator/Extractor.php
62-
63-
-
64-
message: '#^Call to an undefined method ReflectionClass\<object\>\:\:hasCase\(\)\.$#'
59+
identifier: variable.undefined
6560
count: 1
66-
path: src/PhpGenerator/Factory.php
67-
68-
-
69-
message: '#^Elseif branch is unreachable because previous condition is always true\.$#'
70-
count: 1
71-
path: src/PhpGenerator/Factory.php
72-
73-
-
74-
message: '#^Method Nette\\PhpGenerator\\Factory\:\:getAttributes\(\) has parameter \$from with no type specified\.$#'
75-
count: 1
76-
path: src/PhpGenerator/Factory.php
61+
path: src/PhpGenerator/Extractor.php
7762

7863
-
79-
message: '#^Method Nette\\PhpGenerator\\Factory\:\:getAttributes\(\) return type has no value type specified in iterable type array\.$#'
64+
message: '#^Parameter &\$from by\-ref type of method Nette\\PhpGenerator\\Factory\:\:createClassObject\(\) expects ReflectionClass\<object\>, ReflectionEnum\<UnitEnum\> given\.$#'
65+
identifier: parameterByRef.type
8066
count: 1
8167
path: src/PhpGenerator/Factory.php
8268

8369
-
84-
message: '#^Method Nette\\PhpGenerator\\Factory\:\:getExtractor\(\) has parameter \$from with no type specified\.$#'
70+
message: '#^Variable \$bodies on left side of \?\?\= always exists and is not nullable\.$#'
71+
identifier: nullCoalesce.variable
8572
count: 1
8673
path: src/PhpGenerator/Factory.php
8774

8875
-
89-
message: '#^Method Nette\\PhpGenerator\\Factory\:\:getVisibility\(\) has parameter \$from with no type specified\.$#'
76+
message: '#^Variable \$cache on left side of \?\?\= always exists and is not nullable\.$#'
77+
identifier: nullCoalesce.variable
9078
count: 1
9179
path: src/PhpGenerator/Factory.php
9280

9381
-
94-
message: '#^Parameter \#1 \$name of method Nette\\PhpGenerator\\ClassType\:\:setExtends\(\) expects string\|null, array\<int, string\> given\.$#'
82+
message: '#^Method Nette\\PhpGenerator\\GlobalFunction\:\:from\(\) should return Nette\\PhpGenerator\\GlobalFunction but returns Nette\\PhpGenerator\\Closure\|Nette\\PhpGenerator\\GlobalFunction\.$#'
83+
identifier: return.type
9584
count: 1
96-
path: src/PhpGenerator/Factory.php
85+
path: src/PhpGenerator/GlobalFunction.php
9786

9887
-
99-
message: '#^Unreachable statement \- code above always terminates\.$#'
88+
message: '#^Parameter \#1 \$callable of static method Nette\\Utils\\Callback\:\:toReflection\(\) expects callable\(\)\: mixed, \(Closure\(\)\: mixed\)\|string given\.$#'
89+
identifier: argument.type
10090
count: 1
101-
path: src/PhpGenerator/Factory.php
91+
path: src/PhpGenerator/GlobalFunction.php
10292

10393
-
104-
message: '#^Variable \$bodies on left side of \?\?\= always exists and is not nullable\.$#'
94+
message: '#^Parameter \#1 \$from of method Nette\\PhpGenerator\\Factory\:\:fromFunctionReflection\(\) expects ReflectionFunction, ReflectionFunction\|ReflectionMethod given\.$#'
95+
identifier: argument.type
10596
count: 1
106-
path: src/PhpGenerator/Factory.php
97+
path: src/PhpGenerator/GlobalFunction.php
10798

10899
-
109-
message: '#^Method Nette\\PhpGenerator\\Helpers\:\:formatArgs\(\) has parameter \$args with no value type specified in iterable type array\.$#'
100+
message: '#^Method Nette\\PhpGenerator\\Helpers\:\:unindent\(\) should return string but returns string\|null\.$#'
101+
identifier: return.type
110102
count: 1
111103
path: src/PhpGenerator/Helpers.php
112104

113105
-
114-
message: '#^Method Nette\\PhpGenerator\\Method\:\:from\(\) has parameter \$method with no value type specified in iterable type array\.$#'
106+
message: '#^Parameter \#1 \$callable of static method Nette\\Utils\\Callback\:\:toReflection\(\) expects callable\(\)\: mixed, array\{object\|string, string\}\|\(Closure\(\)\: mixed\)\|string given\.$#'
107+
identifier: argument.type
115108
count: 1
116109
path: src/PhpGenerator/Method.php
117110

118111
-
119-
message: '#^Method Nette\\PhpGenerator\\TraitType\:\:addTrait\(\) has parameter \$deprecatedParam with no value type specified in iterable type array\.$#'
112+
message: '#^Parameter \#1 \$from of method Nette\\PhpGenerator\\Factory\:\:fromMethodReflection\(\) expects ReflectionMethod, ReflectionFunction\|ReflectionMethod given\.$#'
113+
identifier: argument.type
120114
count: 1
121-
path: src/PhpGenerator/TraitType.php
115+
path: src/PhpGenerator/Method.php

phpstan.neon

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,28 @@
1-
includes:
2-
- phpstan-baseline.neon
3-
41
parameters:
5-
level: 6
2+
level: 8
63

74
paths:
85
- src
6+
7+
ignoreErrors:
8+
# addMember() uses dynamic property assignment through match expression — type-safe by design
9+
-
10+
identifier: assign.propertyType
11+
paths:
12+
- src/PhpGenerator/ClassType.php
13+
- src/PhpGenerator/TraitType.php
14+
- src/PhpGenerator/InterfaceType.php
15+
- src/PhpGenerator/EnumType.php
16+
17+
# Exhaustive match arms — last branch is always true by definition
18+
-
19+
identifier: instanceof.alwaysTrue
20+
paths:
21+
- src/PhpGenerator/ClassType.php
22+
- src/PhpGenerator/TraitType.php
23+
- src/PhpGenerator/InterfaceType.php
24+
- src/PhpGenerator/EnumType.php
25+
- src/PhpGenerator/Printer.php
26+
27+
includes:
28+
- phpstan-baseline.neon

src/PhpGenerator/ClassManipulator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public function inheritProperty(string $name, bool $returnIfExists = false): Pro
3434
?: throw new Nette\InvalidStateException("Class '{$this->class->getName()}' has neither setExtends() nor setImplements() set.");
3535

3636
foreach ($parents as $parent) {
37+
/** @var class-string $parent */
3738
try {
3839
$rp = new \ReflectionProperty($parent, $name);
3940
} catch (\ReflectionException) {

src/PhpGenerator/ClassType.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public function addImplement(string $name): static
127127

128128
public function removeImplement(string $name): static
129129
{
130-
$this->implements = array_diff($this->implements, [$name]);
130+
$this->implements = array_values(array_diff($this->implements, [$name]));
131131
return $this;
132132
}
133133

@@ -183,10 +183,9 @@ public function validate(): void
183183
public function __clone(): void
184184
{
185185
parent::__clone();
186-
$clone = fn($item) => clone $item;
187-
$this->consts = array_map($clone, $this->consts);
188-
$this->methods = array_map($clone, $this->methods);
189-
$this->properties = array_map($clone, $this->properties);
190-
$this->traits = array_map($clone, $this->traits);
186+
$this->consts = array_map(fn(Constant $c) => clone $c, $this->consts);
187+
$this->methods = array_map(fn(Method $m) => clone $m, $this->methods);
188+
$this->properties = array_map(fn(Property $p) => clone $p, $this->properties);
189+
$this->traits = array_map(fn(TraitUse $t) => clone $t, $this->traits);
191190
}
192191
}

src/PhpGenerator/Dumper.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ private function dumpObject(object $var, array $parents, int $level, int $column
146146
return '(object) ' . $this->dumpArray($var, $parents, $level, $column + 10);
147147

148148
} elseif ($class === \DateTime::class || $class === \DateTimeImmutable::class) {
149+
assert($var instanceof \DateTimeInterface);
149150
return $this->format(
150151
"new \\$class(?, new \\DateTimeZone(?))",
151152
$var->format('Y-m-d H:i:s.u'),

src/PhpGenerator/EnumType.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public function addImplement(string $name): static
6666

6767
public function removeImplement(string $name): static
6868
{
69-
$this->implements = array_diff($this->implements, [$name]);
69+
$this->implements = array_values(array_diff($this->implements, [$name]));
7070
return $this;
7171
}
7272

@@ -135,10 +135,9 @@ public function addMember(Method|Constant|EnumCase|TraitUse $member, bool $overw
135135
public function __clone(): void
136136
{
137137
parent::__clone();
138-
$clone = fn($item) => clone $item;
139-
$this->consts = array_map($clone, $this->consts);
140-
$this->methods = array_map($clone, $this->methods);
141-
$this->traits = array_map($clone, $this->traits);
142-
$this->cases = array_map($clone, $this->cases);
138+
$this->consts = array_map(fn(Constant $c) => clone $c, $this->consts);
139+
$this->methods = array_map(fn(Method $m) => clone $m, $this->methods);
140+
$this->traits = array_map(fn(TraitUse $t) => clone $t, $this->traits);
141+
$this->cases = array_map(fn(EnumCase $c) => clone $c, $this->cases);
143142
}
144143
}

src/PhpGenerator/Extractor.php

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
use PhpParser\Node;
1414
use PhpParser\NodeFinder;
1515
use PhpParser\ParserFactory;
16-
use function addcslashes, array_map, assert, class_exists, end, in_array, is_array, rtrim, str_contains, str_repeat, str_replace, str_starts_with, strlen, substr, substr_replace, usort;
16+
use function addcslashes, array_map, assert, class_exists, end, in_array, is_array, is_string, rtrim, str_contains, str_repeat, str_replace, str_starts_with, strlen, substr, substr_replace, usort;
1717

1818

1919
/**
@@ -49,6 +49,7 @@ private function parseCode(string $code): void
4949
$this->code = Nette\Utils\Strings::unixNewLines($code);
5050
$parser = (new ParserFactory)->createForNewestSupportedVersion();
5151
$stmts = $parser->parse($this->code);
52+
assert($stmts !== null);
5253

5354
$traverser = new PhpParser\NodeTraverser;
5455
$traverser->addVisitor(new PhpParser\NodeVisitor\ParentConnectingVisitor);
@@ -63,8 +64,11 @@ public function extractMethodBodies(string $className): array
6364
$nodeFinder = new NodeFinder;
6465
$classNode = $nodeFinder->findFirst(
6566
$this->statements,
66-
fn(Node $node) => $node instanceof Node\Stmt\ClassLike && $node->namespacedName->toString() === $className,
67+
fn(Node $node) => $node instanceof Node\Stmt\ClassLike && $node->namespacedName !== null && $node->namespacedName->toString() === $className,
6768
);
69+
if (!$classNode) {
70+
return [];
71+
}
6872

6973
$res = [];
7074
foreach ($nodeFinder->findInstanceOf($classNode, Node\Stmt\ClassMethod::class) as $methodNode) {
@@ -87,8 +91,11 @@ public function extractPropertyHookBodies(string $className): array
8791
$nodeFinder = new NodeFinder;
8892
$classNode = $nodeFinder->findFirst(
8993
$this->statements,
90-
fn(Node $node) => $node instanceof Node\Stmt\ClassLike && $node->namespacedName->toString() === $className,
94+
fn(Node $node) => $node instanceof Node\Stmt\ClassLike && $node->namespacedName !== null && $node->namespacedName->toString() === $className,
9195
);
96+
if (!$classNode) {
97+
return [];
98+
}
9299

93100
$res = [];
94101
foreach ($nodeFinder->findInstanceOf($classNode, Node\Stmt\Property::class) as $propertyNode) {
@@ -111,7 +118,7 @@ public function extractFunctionBody(string $name): string
111118
{
112119
$functionNode = (new NodeFinder)->findFirst(
113120
$this->statements,
114-
fn(Node $node) => $node instanceof Node\Stmt\Function_ && $node->namespacedName->toString() === $name,
121+
fn(Node $node) => $node instanceof Node\Stmt\Function_ && $node->namespacedName !== null && $node->namespacedName->toString() === $name,
115122
);
116123
assert($functionNode instanceof Node\Stmt\Function_);
117124

@@ -260,7 +267,7 @@ public function extractAll(): PhpFile
260267
$phpFile->setStrictTypes((bool) $node->declares[0]->value->value);
261268

262269
} elseif ($node instanceof Node\Stmt\Namespace_) {
263-
$namespaces[$node->name->toString()] = $node->stmts;
270+
$namespaces[$node->name?->toString() ?? ''] = $node->stmts;
264271
}
265272
}
266273

@@ -294,6 +301,7 @@ private function addUseToNamespace(PhpNamespace $namespace, Node\Stmt\Use_ $node
294301

295302
private function addClassLikeToFile(PhpFile $phpFile, Node\Stmt\ClassLike $node): ClassLike
296303
{
304+
assert($node->namespacedName !== null);
297305
if ($node instanceof Node\Stmt\Class_) {
298306
$class = $phpFile->addClass($node->namespacedName->toString());
299307
$class->setFinal($node->isFinal());
@@ -387,7 +395,9 @@ private function addHooksToProperty(Property|PromotedParameter $prop, Node\Stmt\
387395
}
388396

389397
foreach ($node->hooks as $hookNode) {
390-
$hook = $prop->addHook($hookNode->name->toString());
398+
/** @var 'set'|'get' $hookType */
399+
$hookType = $hookNode->name->toString();
400+
$hook = $prop->addHook($hookType);
391401
$hook->setFinal((bool) ($hookNode->flags & Modifiers::FINAL));
392402
$this->setupFunction($hook, $hookNode);
393403
if ($hookNode->body === null) {
@@ -438,6 +448,7 @@ private function addEnumCaseToClass(EnumType $class, Node\Stmt\EnumCase $node):
438448

439449
private function addFunctionToFile(PhpFile $phpFile, Node\Stmt\Function_ $node): void
440450
{
451+
assert($node->namespacedName !== null);
441452
$function = $phpFile->addFunction($node->namespacedName->toString());
442453
$this->setupFunction($function, $node);
443454
}
@@ -480,13 +491,15 @@ private function setupFunction(GlobalFunction|Method|PropertyHook $function, Nod
480491
}
481492

482493
foreach ($node->getParams() as $item) {
494+
assert($item->var instanceof Node\Expr\Variable && is_string($item->var->name));
483495
$getVisibility = $this->toVisibility($item->flags);
484496
$setVisibility = $this->toSetterVisibility($item->flags);
485497
$final = (bool) ($item->flags & Modifiers::FINAL);
486498
if ($getVisibility || $setVisibility || $final) {
499+
assert($function instanceof Method);
487500
$param = $function->addPromotedParameter($item->var->name)
488501
->setVisibility($getVisibility, $setVisibility)
489-
->setReadonly($item->isReadonly())
502+
->setReadOnly($item->isReadonly())
490503
->setFinal($final);
491504
$this->addHooksToProperty($param, $item);
492505
} else {
@@ -587,7 +600,9 @@ private function toPhp(Node $value): string
587600
private function getNodeContents(Node ...$nodes): string
588601
{
589602
$start = $this->getNodeStartPos($nodes[0]);
590-
return substr($this->code, $start, end($nodes)->getEndFilePos() - $start + 1);
603+
$last = end($nodes);
604+
assert($last !== false);
605+
return substr($this->code, $start, $last->getEndFilePos() - $start + 1);
591606
}
592607

593608

0 commit comments

Comments
 (0)