Skip to content

Commit 796edd3

Browse files
committed
fixed PHPStan errors
1 parent 28fd694 commit 796edd3

7 files changed

Lines changed: 115 additions & 22 deletions

File tree

phpstan-baseline.neon

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ parameters:
66
count: 1
77
path: src/PhpGenerator/ClassLike.php
88

9+
-
10+
message: '#^Method Nette\\PhpGenerator\\Closure\:\:from\(\) should return Nette\\PhpGenerator\\Closure but returns Nette\\PhpGenerator\\Closure\|Nette\\PhpGenerator\\GlobalFunction\.$#'
11+
identifier: return.type
12+
count: 1
13+
path: src/PhpGenerator/Closure.php
14+
15+
-
16+
message: '#^Call to an undefined method object\:\:__unserialize\(\)\.$#'
17+
identifier: method.notFound
18+
count: 1
19+
path: src/PhpGenerator/Dumper.php
20+
921
-
1022
message: '#^Call to an undefined method Nette\\PhpGenerator\\ClassLike\:\:addConstant\(\)\.$#'
1123
identifier: method.notFound
@@ -61,7 +73,38 @@ parameters:
6173
path: src/PhpGenerator/Factory.php
6274

6375
-
64-
message: '#^Method Nette\\PhpGenerator\\Printer\:\:printDocComment\(\) has parameter \$commentable with no type specified\.$#'
65-
identifier: missingType.parameter
76+
message: '#^Method Nette\\PhpGenerator\\GlobalFunction\:\:from\(\) should return Nette\\PhpGenerator\\GlobalFunction but returns Nette\\PhpGenerator\\Closure\|Nette\\PhpGenerator\\GlobalFunction\.$#'
77+
identifier: return.type
6678
count: 1
67-
path: src/PhpGenerator/Printer.php
79+
path: src/PhpGenerator/GlobalFunction.php
80+
81+
-
82+
message: '#^Parameter \#1 \$callable of static method Nette\\Utils\\Callback\:\:toReflection\(\) expects callable\(\)\: mixed, \(Closure\(\)\: mixed\)\|string given\.$#'
83+
identifier: argument.type
84+
count: 1
85+
path: src/PhpGenerator/GlobalFunction.php
86+
87+
-
88+
message: '#^Parameter \#1 \$from of method Nette\\PhpGenerator\\Factory\:\:fromFunctionReflection\(\) expects ReflectionFunction, ReflectionFunction\|ReflectionMethod given\.$#'
89+
identifier: argument.type
90+
count: 1
91+
path: src/PhpGenerator/GlobalFunction.php
92+
93+
-
94+
message: '#^Parameter \#1 \$callable of static method Nette\\Utils\\Callback\:\:toReflection\(\) expects callable\(\)\: mixed, array\{object\|string, string\}\|\(Closure\(\)\: mixed\)\|string given\.$#'
95+
identifier: argument.type
96+
count: 1
97+
path: src/PhpGenerator/Method.php
98+
99+
-
100+
message: '#^Parameter \#1 \$from of method Nette\\PhpGenerator\\Factory\:\:fromMethodReflection\(\) expects ReflectionMethod, ReflectionFunction\|ReflectionMethod given\.$#'
101+
identifier: argument.type
102+
count: 1
103+
path: src/PhpGenerator/Method.php
104+
105+
-
106+
message: '#^Method Nette\\PhpGenerator\\Helpers\:\:unindent\(\) should return string but returns string\|null\.$#'
107+
identifier: return.type
108+
count: 1
109+
path: src/PhpGenerator/Helpers.php
110+

phpstan.neon

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,40 @@
11
parameters:
2-
level: 6
2+
level: 8
33

44
paths:
55
- src
66

77
ignoreErrors:
8-
# Intentional runtime type validation pattern: (function (Type ...$items) {})(...$items)
9-
- identifier: expr.resultUnused
8+
# addMember() uses dynamic property assignment through match expression — type-safe by design
9+
-
10+
identifier: assign.propertyType
11+
path: src/PhpGenerator/ClassType.php
12+
-
13+
identifier: assign.propertyType
14+
path: src/PhpGenerator/TraitType.php
15+
-
16+
identifier: assign.propertyType
17+
path: src/PhpGenerator/InterfaceType.php
18+
-
19+
identifier: assign.propertyType
20+
path: src/PhpGenerator/EnumType.php
21+
22+
# Exhaustive match arms — last branch is always true by definition
23+
-
24+
identifier: instanceof.alwaysTrue
25+
path: src/PhpGenerator/ClassType.php
26+
-
27+
identifier: instanceof.alwaysTrue
28+
path: src/PhpGenerator/TraitType.php
29+
-
30+
identifier: instanceof.alwaysTrue
31+
path: src/PhpGenerator/InterfaceType.php
32+
-
33+
identifier: instanceof.alwaysTrue
34+
path: src/PhpGenerator/EnumType.php
35+
-
36+
identifier: instanceof.alwaysTrue
37+
path: src/PhpGenerator/Printer.php
1038

1139
includes:
1240
- phpstan-baseline.neon

src/PhpGenerator/ClassManipulator.php

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

3838
foreach ($parents as $parent) {
39+
/** @var class-string $parent */
3940
try {
4041
$rp = new \ReflectionProperty($parent, $name);
4142
} catch (\ReflectionException) {

src/PhpGenerator/Extractor.php

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ private function parseCode(string $code): void
5151
$this->code = Nette\Utils\Strings::unixNewLines($code);
5252
$parser = (new ParserFactory)->createForNewestSupportedVersion();
5353
$stmts = $parser->parse($this->code);
54+
assert($stmts !== null);
5455

5556
$traverser = new PhpParser\NodeTraverser;
5657
$traverser->addVisitor(new PhpParser\NodeVisitor\ParentConnectingVisitor);
@@ -65,8 +66,11 @@ public function extractMethodBodies(string $className): array
6566
$nodeFinder = new NodeFinder;
6667
$classNode = $nodeFinder->findFirst(
6768
$this->statements,
68-
fn(Node $node) => $node instanceof Node\Stmt\ClassLike && $node->namespacedName->toString() === $className,
69+
fn(Node $node) => $node instanceof Node\Stmt\ClassLike && $node->namespacedName !== null && $node->namespacedName->toString() === $className,
6970
);
71+
if (!$classNode) {
72+
return [];
73+
}
7074

7175
$res = [];
7276
foreach ($nodeFinder->findInstanceOf($classNode, Node\Stmt\ClassMethod::class) as $methodNode) {
@@ -89,8 +93,11 @@ public function extractPropertyHookBodies(string $className): array
8993
$nodeFinder = new NodeFinder;
9094
$classNode = $nodeFinder->findFirst(
9195
$this->statements,
92-
fn(Node $node) => $node instanceof Node\Stmt\ClassLike && $node->namespacedName->toString() === $className,
96+
fn(Node $node) => $node instanceof Node\Stmt\ClassLike && $node->namespacedName !== null && $node->namespacedName->toString() === $className,
9397
);
98+
if (!$classNode) {
99+
return [];
100+
}
94101

95102
$res = [];
96103
foreach ($nodeFinder->findInstanceOf($classNode, Node\Stmt\Property::class) as $propertyNode) {
@@ -113,7 +120,7 @@ public function extractFunctionBody(string $name): string
113120
{
114121
$functionNode = (new NodeFinder)->findFirst(
115122
$this->statements,
116-
fn(Node $node) => $node instanceof Node\Stmt\Function_ && $node->namespacedName->toString() === $name,
123+
fn(Node $node) => $node instanceof Node\Stmt\Function_ && $node->namespacedName !== null && $node->namespacedName->toString() === $name,
117124
);
118125
assert($functionNode instanceof Node\Stmt\Function_);
119126

@@ -255,7 +262,7 @@ public function extractAll(): PhpFile
255262
$phpFile->setStrictTypes((bool) $node->declares[0]->value->value);
256263

257264
} elseif ($node instanceof Node\Stmt\Namespace_) {
258-
$namespaces[$node->name->toString()] = $node->stmts;
265+
$namespaces[$node->name?->toString() ?? ''] = $node->stmts;
259266
}
260267
}
261268

@@ -289,6 +296,7 @@ private function addUseToNamespace(PhpNamespace $namespace, Node\Stmt\Use_ $node
289296

290297
private function addClassLikeToFile(PhpFile $phpFile, Node\Stmt\ClassLike $node): ClassLike
291298
{
299+
assert($node->namespacedName !== null);
292300
if ($node instanceof Node\Stmt\Class_) {
293301
$class = $phpFile->addClass($node->namespacedName->toString());
294302
$class->setFinal($node->isFinal());
@@ -382,7 +390,9 @@ private function addHooksToProperty(Property|PromotedParameter $prop, Node\Stmt\
382390
}
383391

384392
foreach ($node->hooks as $hookNode) {
385-
$hook = $prop->addHook($hookNode->name->toString());
393+
/** @var 'set'|'get' $hookType */
394+
$hookType = $hookNode->name->toString();
395+
$hook = $prop->addHook($hookType);
386396
$hook->setFinal((bool) ($hookNode->flags & Modifiers::FINAL));
387397
$this->setupFunction($hook, $hookNode);
388398
if ($hookNode->body === null) {
@@ -433,6 +443,7 @@ private function addEnumCaseToClass(EnumType $class, Node\Stmt\EnumCase $node):
433443

434444
private function addFunctionToFile(PhpFile $phpFile, Node\Stmt\Function_ $node): void
435445
{
446+
assert($node->namespacedName !== null);
436447
$function = $phpFile->addFunction($node->namespacedName->toString());
437448
$this->setupFunction($function, $node);
438449
}
@@ -483,7 +494,7 @@ private function setupFunction(GlobalFunction|Method|PropertyHook $function, Nod
483494
assert($function instanceof Method);
484495
$param = $function->addPromotedParameter($item->var->name)
485496
->setVisibility($getVisibility, $setVisibility)
486-
->setReadonly($item->isReadonly())
497+
->setReadOnly($item->isReadonly())
487498
->setFinal($final);
488499
$this->addHooksToProperty($param, $item);
489500
} else {
@@ -584,7 +595,9 @@ private function toPhp(Node $value): string
584595
private function getNodeContents(Node ...$nodes): string
585596
{
586597
$start = $this->getNodeStartPos($nodes[0]);
587-
return substr($this->code, $start, end($nodes)->getEndFilePos() - $start + 1);
598+
$last = end($nodes);
599+
assert($last !== false);
600+
return substr($this->code, $start, $last->getEndFilePos() - $start + 1);
588601
}
589602

590603

src/PhpGenerator/Factory.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ private function createClassObject(\ReflectionClass &$from): ClassLike
5050
if ($from->isAnonymous()) {
5151
return new ClassType;
5252
} elseif ($from->isEnum()) {
53-
$from = new \ReflectionEnum($from->getName());
53+
$name = $from->getName();
54+
/** @var class-string<\UnitEnum> $name */
55+
$from = new \ReflectionEnum($name);
5456
$class = new EnumType($from->getName());
5557
} elseif ($from->isInterface()) {
5658
$class = new InterfaceType($from->getName());
@@ -111,8 +113,10 @@ private function populateMembers(ClassLike $class, \ReflectionClass $from, bool
111113
$props[] = $p = $this->fromPropertyReflection($prop);
112114
if ($withBodies && ($file = $declaringClass->getFileName())) {
113115
$hookBodies ??= $this->getExtractor($file)->extractPropertyHookBodies($declaringClass->name);
114-
foreach ($hookBodies[$prop->getName()] ?? [] as $hookType => [$body, $short]) {
115-
$p->getHook($hookType)->setBody($body, short: $short);
116+
/** @var array<'set'|'get', array{string, bool}> $propHookBodies */
117+
$propHookBodies = $hookBodies[$prop->getName()] ?? [];
118+
foreach ($propHookBodies as $hookType => [$body, $short]) {
119+
$p->getHook($hookType)?->setBody($body, short: $short);
116120
}
117121
}
118122
}
@@ -243,10 +247,11 @@ public function fromCallable(callable $from): Method|GlobalFunction|Closure
243247
public function fromParameterReflection(\ReflectionParameter $from): Parameter
244248
{
245249
if ($from->isPromoted()) {
246-
$property = $from->getDeclaringClass()->getProperty($from->name);
250+
$property = $from->getDeclaringClass()?->getProperty($from->name);
251+
\assert($property instanceof \ReflectionProperty);
247252
$param = (new PromotedParameter($from->name))
248253
->setVisibility($this->getVisibility($property))
249-
->setReadOnly($property->isReadonly())
254+
->setReadOnly($property->isReadOnly())
250255
->setFinal(PHP_VERSION_ID >= 80500 && $property->isFinal() && !$property->isPrivateSet());
251256
$this->addHooks($property, $param);
252257
} else {
@@ -257,7 +262,7 @@ public function fromParameterReflection(\ReflectionParameter $from): Parameter
257262

258263
if ($from->isDefaultValueAvailable()) {
259264
if ($from->isDefaultValueConstant()) {
260-
$parts = explode('::', $from->getDefaultValueConstantName());
265+
$parts = explode('::', $from->getDefaultValueConstantName() ?? '');
261266
if (count($parts) > 1) {
262267
$parts[0] = Helpers::tagName($parts[0]);
263268
}
@@ -337,6 +342,7 @@ private function addHooks(\ReflectionProperty $from, Property|PromotedParameter
337342
$prop->setVisibility($getV === Visibility::Public ? null : $getV, $setV);
338343
}
339344

345+
/** @var 'set'|'get' $type */
340346
foreach ($from->getHooks() as $type => $hook) {
341347
$params = $hook->getParameters();
342348
if (

src/PhpGenerator/PhpNamespace.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,9 @@ public function getClasses(): array
343343
{
344344
$res = [];
345345
foreach ($this->classes as $class) {
346-
$res[$class->getName()] = $class;
346+
$name = $class->getName();
347+
assert($name !== null);
348+
$res[$name] = $class;
347349
}
348350

349351
return $res;

src/PhpGenerator/Printer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class Printer
2929
public bool $singleParameterOnOneLine = false;
3030
public bool $omitEmptyNamespaces = true;
3131
protected ?PhpNamespace $namespace = null;
32-
protected ?Dumper $dumper;
32+
protected Dumper $dumper;
3333
private bool $resolveTypes = true;
3434

3535

@@ -146,7 +146,7 @@ public function printClass(
146146
$this->namespace = $this->resolveTypes ? $namespace : null;
147147
$class->validate();
148148
$resolver = $this->namespace
149-
? $namespace->simplifyType(...)
149+
? $this->namespace->simplifyType(...)
150150
: fn($s) => $s;
151151

152152
$traits = [];

0 commit comments

Comments
 (0)