Skip to content

Commit 17de848

Browse files
committed
fixed PHPStan errors
1 parent 2c08a05 commit 17de848

7 files changed

Lines changed: 102 additions & 22 deletions

File tree

phpstan-baseline.neon

Lines changed: 45 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,37 @@ 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
78+
count: 1
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
66108
count: 1
67-
path: src/PhpGenerator/Printer.php
109+
path: src/PhpGenerator/Helpers.php

phpstan.neon

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
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+
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
1026

1127
includes:
1228
- 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/Extractor.php

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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

@@ -269,7 +276,7 @@ public function extractAll(): PhpFile
269276
$phpFile->setStrictTypes((bool) $node->declares[0]->value->value);
270277

271278
} elseif ($node instanceof Node\Stmt\Namespace_) {
272-
$namespaces[$node->name->toString()] = $node->stmts;
279+
$namespaces[$node->name?->toString() ?? ''] = $node->stmts;
273280
}
274281
}
275282

@@ -303,6 +310,7 @@ private function addUseToNamespace(PhpNamespace $namespace, Node\Stmt\Use_ $node
303310

304311
private function addClassLikeToFile(PhpFile $phpFile, Node\Stmt\ClassLike $node): ClassLike
305312
{
313+
assert($node->namespacedName !== null);
306314
if ($node instanceof Node\Stmt\Class_) {
307315
$class = $phpFile->addClass($node->namespacedName->toString());
308316
$class->setFinal($node->isFinal());
@@ -396,7 +404,9 @@ private function addHooksToProperty(Property|PromotedParameter $prop, Node\Stmt\
396404
}
397405

398406
foreach ($node->hooks as $hookNode) {
399-
$hook = $prop->addHook($hookNode->name->toString());
407+
/** @var 'set'|'get' $hookType */
408+
$hookType = $hookNode->name->toString();
409+
$hook = $prop->addHook($hookType);
400410
$hook->setFinal((bool) ($hookNode->flags & Modifiers::FINAL));
401411
$this->setupFunction($hook, $hookNode);
402412
if ($hookNode->body === null) {
@@ -447,6 +457,7 @@ private function addEnumCaseToClass(EnumType $class, Node\Stmt\EnumCase $node):
447457

448458
private function addFunctionToFile(PhpFile $phpFile, Node\Stmt\Function_ $node): void
449459
{
460+
assert($node->namespacedName !== null);
450461
$function = $phpFile->addFunction($node->namespacedName->toString());
451462
$this->setupFunction($function, $node);
452463
}
@@ -497,7 +508,7 @@ private function setupFunction(GlobalFunction|Method|PropertyHook $function, Nod
497508
assert($function instanceof Method);
498509
$param = $function->addPromotedParameter($item->var->name)
499510
->setVisibility($getVisibility, $setVisibility)
500-
->setReadonly($item->isReadonly())
511+
->setReadOnly($item->isReadonly())
501512
->setFinal($final);
502513
$this->addHooksToProperty($param, $item);
503514
} else {
@@ -598,7 +609,9 @@ private function toPhp(Node $value): string
598609
private function getNodeContents(Node ...$nodes): string
599610
{
600611
$start = $this->getNodeStartPos($nodes[0]);
601-
return substr($this->code, $start, end($nodes)->getEndFilePos() - $start + 1);
612+
$last = end($nodes);
613+
assert($last !== false);
614+
return substr($this->code, $start, $last->getEndFilePos() - $start + 1);
602615
}
603616

604617

src/PhpGenerator/Factory.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ private function createClassObject(\ReflectionClass &$from): ClassLike
4848
if ($from->isAnonymous()) {
4949
return new ClassType;
5050
} elseif ($from->isEnum()) {
51-
$from = new \ReflectionEnum($from->getName());
51+
$name = $from->getName();
52+
/** @var class-string<\UnitEnum> $name */
53+
$from = new \ReflectionEnum($name);
5254
$class = new EnumType($from->getName());
5355
} elseif ($from->isInterface()) {
5456
$class = new InterfaceType($from->getName());
@@ -109,8 +111,10 @@ private function populateMembers(ClassLike $class, \ReflectionClass $from, bool
109111
$props[] = $p = $this->fromPropertyReflection($prop);
110112
if ($withBodies && ($file = $declaringClass->getFileName())) {
111113
$hookBodies ??= $this->getExtractor($file)->extractPropertyHookBodies($declaringClass->name);
112-
foreach ($hookBodies[$prop->getName()] ?? [] as $hookType => [$body, $short]) {
113-
$p->getHook($hookType)->setBody($body, short: $short);
114+
/** @var array<'set'|'get', array{string, bool}> $propHookBodies */
115+
$propHookBodies = $hookBodies[$prop->getName()] ?? [];
116+
foreach ($propHookBodies as $hookType => [$body, $short]) {
117+
$p->getHook($hookType)?->setBody($body, short: $short);
114118
}
115119
}
116120
}
@@ -241,10 +245,11 @@ public function fromCallable(callable $from): Method|GlobalFunction|Closure
241245
public function fromParameterReflection(\ReflectionParameter $from): Parameter
242246
{
243247
if ($from->isPromoted()) {
244-
$property = $from->getDeclaringClass()->getProperty($from->name);
248+
$property = $from->getDeclaringClass()?->getProperty($from->name);
249+
\assert($property instanceof \ReflectionProperty);
245250
$param = (new PromotedParameter($from->name))
246251
->setVisibility($this->getVisibility($property))
247-
->setReadOnly($property->isReadonly())
252+
->setReadOnly($property->isReadOnly())
248253
->setFinal(PHP_VERSION_ID >= 80500 && $property->isFinal() && !$property->isPrivateSet());
249254
$this->addHooks($property, $param);
250255
} else {
@@ -255,7 +260,7 @@ public function fromParameterReflection(\ReflectionParameter $from): Parameter
255260

256261
if ($from->isDefaultValueAvailable()) {
257262
if ($from->isDefaultValueConstant()) {
258-
$parts = explode('::', $from->getDefaultValueConstantName());
263+
$parts = explode('::', $from->getDefaultValueConstantName() ?? '');
259264
if (count($parts) > 1) {
260265
$parts[0] = Helpers::tagName($parts[0]);
261266
}
@@ -335,6 +340,7 @@ private function addHooks(\ReflectionProperty $from, Property|PromotedParameter
335340
$prop->setVisibility($getV === Visibility::Public ? null : $getV, $setV);
336341
}
337342

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

src/PhpGenerator/PhpNamespace.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,9 @@ public function getClasses(): array
341341
{
342342
$res = [];
343343
foreach ($this->classes as $class) {
344-
$res[$class->getName()] = $class;
344+
$name = $class->getName();
345+
assert($name !== null);
346+
$res[$name] = $class;
345347
}
346348

347349
return $res;

src/PhpGenerator/Printer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class Printer
2727
public bool $singleParameterOnOneLine = false;
2828
public bool $omitEmptyNamespaces = true;
2929
protected ?PhpNamespace $namespace = null;
30-
protected ?Dumper $dumper;
30+
protected Dumper $dumper;
3131
private bool $resolveTypes = true;
3232

3333

@@ -144,7 +144,7 @@ public function printClass(
144144
$this->namespace = $this->resolveTypes ? $namespace : null;
145145
$class->validate();
146146
$resolver = $this->namespace
147-
? $namespace->simplifyType(...)
147+
? $this->namespace->simplifyType(...)
148148
: fn($s) => $s;
149149

150150
$traits = [];

0 commit comments

Comments
 (0)