Skip to content

Commit 1826359

Browse files
authored
Merge branch '2.1.x' into equals
2 parents a36fd15 + a5f7c06 commit 1826359

32 files changed

+265
-103
lines changed

build/phpstan.neon

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ parameters:
6161
- 'PHPStan\Reflection\MissingPropertyFromReflectionException'
6262
- 'PHPStan\Reflection\MissingConstantFromReflectionException'
6363
- 'PHPStan\Type\CircularTypeAliasDefinitionException'
64-
- 'PHPStan\Broker\ClassAutoloadingException'
6564
- 'LogicException'
6665
- 'Error'
6766
check:

conf/bleedingEdge.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ parameters:
66
stricterFunctionMap: true
77
reportPreciseLineForUnusedFunctionParameter: true
88
internalTag: true
9+
newStaticInAbstractClassStaticMethod: true

conf/config.level0.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ conditionalTags:
110110
phpstan.restrictedClassNameUsageExtension: %featureToggles.internalTag%
111111
PHPStan\Rules\InternalTag\RestrictedInternalFunctionUsageExtension:
112112
phpstan.restrictedFunctionUsageExtension: %featureToggles.internalTag%
113+
PHPStan\Rules\Classes\NewStaticInAbstractClassStaticMethodRule:
114+
phpstan.rules.rule: %featureToggles.newStaticInAbstractClassStaticMethod%
113115

114116
services:
115117
-
@@ -178,6 +180,9 @@ services:
178180
checkFunctionNameCase: %checkFunctionNameCase%
179181
discoveringSymbolsTip: %tips.discoveringSymbols%
180182

183+
-
184+
class: PHPStan\Rules\Classes\NewStaticInAbstractClassStaticMethodRule
185+
181186
-
182187
class: PHPStan\Rules\Constants\OverridingConstantRule
183188
arguments:

conf/config.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ parameters:
2727
stricterFunctionMap: false
2828
reportPreciseLineForUnusedFunctionParameter: false
2929
internalTag: false
30+
newStaticInAbstractClassStaticMethod: false
3031
fileExtensions:
3132
- php
3233
checkAdvancedIsset: false

conf/parametersSchema.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ parametersSchema:
3333
stricterFunctionMap: bool()
3434
reportPreciseLineForUnusedFunctionParameter: bool()
3535
internalTag: bool()
36+
newStaticInAbstractClassStaticMethod: bool()
3637
])
3738
fileExtensions: listOf(string())
3839
checkAdvancedIsset: bool()

src/Analyser/MutatingScope.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,7 @@ private function resolveType(string $exprString, Expr $node): Type
843843
}
844844

845845
if ($node instanceof AlwaysRememberedExpr) {
846-
return $node->getExprType();
846+
return $this->nativeTypesPromoted ? $node->getNativeExprType() : $node->getExprType();
847847
}
848848

849849
if ($node instanceof Expr\BinaryOp\Smaller) {

src/Analyser/NodeScopeResolver.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5404,7 +5404,7 @@ private function processAssignVar(
54045404
$conditionalExpressions = $this->processSureTypesForConditionalExpressionsAfterAssign($scope, $var->name, $conditionalExpressions, $falseySpecifiedTypes, $falseyType);
54055405
$conditionalExpressions = $this->processSureNotTypesForConditionalExpressionsAfterAssign($scope, $var->name, $conditionalExpressions, $falseySpecifiedTypes, $falseyType);
54065406

5407-
$nodeCallback(new VariableAssignNode($var, $assignedExpr, $isAssignOp), $result->getScope());
5407+
$nodeCallback(new VariableAssignNode($var, $assignedExpr), $result->getScope());
54085408
$scope = $scope->assignVariable($var->name, $type, $scope->getNativeType($assignedExpr), TrinaryLogic::createYes());
54095409
foreach ($conditionalExpressions as $exprString => $holders) {
54105410
$scope = $scope->addConditionalExpressions($exprString, $holders);
@@ -5542,7 +5542,7 @@ private function processAssignVar(
55425542

55435543
if ($varType->isArray()->yes() || !(new ObjectType(ArrayAccess::class))->isSuperTypeOf($varType)->yes()) {
55445544
if ($var instanceof Variable && is_string($var->name)) {
5545-
$nodeCallback(new VariableAssignNode($var, $assignedPropertyExpr, $isAssignOp), $scope);
5545+
$nodeCallback(new VariableAssignNode($var, $assignedPropertyExpr), $scope);
55465546
$scope = $scope->assignVariable($var->name, $valueToWrite, $nativeValueToWrite, TrinaryLogic::createYes());
55475547
} else {
55485548
if ($var instanceof PropertyFetch || $var instanceof StaticPropertyFetch) {
@@ -5574,7 +5574,7 @@ private function processAssignVar(
55745574
}
55755575
} else {
55765576
if ($var instanceof Variable) {
5577-
$nodeCallback(new VariableAssignNode($var, $assignedPropertyExpr, $isAssignOp), $scope);
5577+
$nodeCallback(new VariableAssignNode($var, $assignedPropertyExpr), $scope);
55785578
} elseif ($var instanceof PropertyFetch || $var instanceof StaticPropertyFetch) {
55795579
$nodeCallback(new PropertyAssignNode($var, $assignedPropertyExpr, $isAssignOp), $scope);
55805580
if ($var instanceof PropertyFetch && $var->name instanceof Node\Identifier && !$isAssignOp) {
@@ -5861,7 +5861,7 @@ static function (): void {
58615861
}
58625862

58635863
if ($var instanceof Variable && is_string($var->name)) {
5864-
$nodeCallback(new VariableAssignNode($var, $assignedPropertyExpr, $isAssignOp), $scope);
5864+
$nodeCallback(new VariableAssignNode($var, $assignedPropertyExpr), $scope);
58655865
$scope = $scope->assignVariable($var->name, $valueToWrite, $nativeValueToWrite, TrinaryLogic::createYes());
58665866
} else {
58675867
if ($var instanceof PropertyFetch || $var instanceof StaticPropertyFetch) {

src/Analyser/UndefinedVariableException.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
use PHPStan\AnalysedCodeException;
66
use function sprintf;
77

8+
/**
9+
* @api
10+
*
11+
* Unchecked exception thrown from `PHPStan\Analyser\Scope::getVariableType()`
12+
* in case the user doesn't check `hasVariableType()` is not `no()`.
13+
*/
814
final class UndefinedVariableException extends AnalysedCodeException
915
{
1016

src/Broker/ClassAutoloadingException.php

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

src/Broker/ClassNotFoundException.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
use PHPStan\AnalysedCodeException;
66
use function sprintf;
77

8+
/**
9+
* @api
10+
*
11+
* Unchecked exception thrown from `ReflectionProvider` and other places
12+
* in case the user does not check the existence of the class beforehand
13+
* with `hasClass()` or similar.
14+
*/
815
final class ClassNotFoundException extends AnalysedCodeException
916
{
1017

src/Broker/ConstantNotFoundException.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
use PHPStan\AnalysedCodeException;
66
use function sprintf;
77

8+
/**
9+
* @api
10+
*
11+
* Unchecked exception thrown from `ReflectionProvider`
12+
* in case the user does not check the existence of the constant beforehand
13+
* with `hasConstant()`.
14+
*/
815
final class ConstantNotFoundException extends AnalysedCodeException
916
{
1017

src/Broker/FunctionNotFoundException.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
use PHPStan\AnalysedCodeException;
66
use function sprintf;
77

8+
/**
9+
* @api
10+
*
11+
* Unchecked exception thrown from `ReflectionProvider`
12+
* in case the user does not check the existence of the function beforehand
13+
* with `hasFunction()`.
14+
*/
815
final class FunctionNotFoundException extends AnalysedCodeException
916
{
1017

src/File/PathNotFoundException.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,9 @@
88
final class PathNotFoundException extends Exception
99
{
1010

11-
public function __construct(private string $path)
11+
public function __construct(string $path)
1212
{
1313
parent::__construct(sprintf('Path %s does not exist', $path));
1414
}
1515

16-
public function getPath(): string
17-
{
18-
return $this->path;
19-
}
20-
2116
}

src/Node/PropertyHookStatementNode.php

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,9 @@
2020
final class PropertyHookStatementNode extends Stmt implements VirtualNode
2121
{
2222

23-
public function __construct(private PropertyHook $propertyHook)
23+
public function __construct(PropertyHook $propertyHook)
2424
{
25-
parent::__construct($this->propertyHook->getAttributes());
26-
}
27-
28-
public function getPropertyHook(): PropertyHook
29-
{
30-
return $this->propertyHook;
25+
parent::__construct($propertyHook->getAttributes());
3126
}
3227

3328
/**

src/Node/VariableAssignNode.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ final class VariableAssignNode extends NodeAbstract implements VirtualNode
1111
public function __construct(
1212
private Expr\Variable $variable,
1313
private Expr $assignedExpr,
14-
private bool $assignOp,
1514
)
1615
{
1716
parent::__construct($variable->getAttributes());
@@ -27,11 +26,6 @@ public function getAssignedExpr(): Expr
2726
return $this->assignedExpr;
2827
}
2928

30-
public function isAssignOp(): bool
31-
{
32-
return $this->assignOp;
33-
}
34-
3529
public function getType(): string
3630
{
3731
return 'PHPStan_Node_VariableAssignNodeNode';

src/Process/ProcessPromise.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ final class ProcessPromise
2424

2525
public function __construct(private LoopInterface $loop, private string $name, private string $command)
2626
{
27-
$this->deferred = new Deferred();
27+
$this->deferred = new Deferred(function (): void {
28+
$this->cancel();
29+
});
2830
}
2931

3032
public function getName(): string
@@ -85,7 +87,7 @@ public function run(): PromiseInterface
8587
return $this->deferred->promise();
8688
}
8789

88-
public function cancel(): void
90+
private function cancel(): void
8991
{
9092
if ($this->process === null) {
9193
throw new ShouldNotHappenException('Cancelling process before running');

src/Reflection/MethodPrototypeReflection.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ public function __construct(
1717
private bool $isPrivate,
1818
private bool $isPublic,
1919
private bool $isAbstract,
20-
private bool $isFinal,
2120
private bool $isInternal,
2221
private array $variants,
2322
private ?Type $tentativeReturnType,
@@ -55,11 +54,6 @@ public function isAbstract(): bool
5554
return $this->isAbstract;
5655
}
5756

58-
public function isFinal(): bool
59-
{
60-
return $this->isFinal;
61-
}
62-
6357
public function isInternal(): bool
6458
{
6559
return $this->isInternal;

src/Reflection/Native/NativeMethodReflection.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ public function getPrototype(): ClassMemberReflection
9494
$prototypeMethod->isPrivate(),
9595
$prototypeMethod->isPublic(),
9696
$prototypeMethod->isAbstract(),
97-
$prototypeMethod->isFinal(),
9897
$prototypeMethod->isInternal(),
9998
$prototypeDeclaringClass->getNativeMethod($prototypeMethod->getName())->getVariants(),
10099
$tentativeReturnType,

src/Reflection/Php/PhpMethodReflection.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,6 @@ public function getPrototype(): ClassMemberReflection
135135
$prototypeMethod->isPrivate(),
136136
$prototypeMethod->isPublic(),
137137
$prototypeMethod->isAbstract(),
138-
$prototypeMethod->isFinal(),
139138
$prototypeMethod->isInternal(),
140139
$prototypeDeclaringClass->getNativeMethod($prototypeMethod->getName())->getVariants(),
141140
$tentativeReturnType,

src/Reflection/ResolvedFunctionVariant.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,4 @@ public function getOriginalParametersAcceptor(): ParametersAcceptor;
1111

1212
public function getReturnTypeWithUnresolvableTemplateTypes(): Type;
1313

14-
public function getPhpDocReturnTypeWithUnresolvableTemplateTypes(): Type;
15-
1614
}

src/Reflection/ResolvedFunctionVariantWithCallable.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,6 @@ public function getReturnTypeWithUnresolvableTemplateTypes(): Type
6767
return $this->parametersAcceptor->getReturnTypeWithUnresolvableTemplateTypes();
6868
}
6969

70-
public function getPhpDocReturnTypeWithUnresolvableTemplateTypes(): Type
71-
{
72-
return $this->parametersAcceptor->getPhpDocReturnTypeWithUnresolvableTemplateTypes();
73-
}
74-
7570
public function getReturnType(): Type
7671
{
7772
return $this->parametersAcceptor->getReturnType();
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Classes;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\Php\PhpMethodFromParserNodeReflection;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
use function sprintf;
11+
use function strtolower;
12+
13+
/**
14+
* @implements Rule<Node\Expr\New_>
15+
*/
16+
final class NewStaticInAbstractClassStaticMethodRule implements Rule
17+
{
18+
19+
public function getNodeType(): string
20+
{
21+
return Node\Expr\New_::class;
22+
}
23+
24+
public function processNode(Node $node, Scope $scope): array
25+
{
26+
if (!$node->class instanceof Node\Name) {
27+
return [];
28+
}
29+
30+
if (!$scope->isInClass()) {
31+
return [];
32+
}
33+
34+
if (strtolower($node->class->toString()) !== 'static') {
35+
return [];
36+
}
37+
38+
$classReflection = $scope->getClassReflection();
39+
if (!$classReflection->isAbstract()) {
40+
return [];
41+
}
42+
43+
$inMethod = $scope->getFunction();
44+
if (!$inMethod instanceof PhpMethodFromParserNodeReflection) {
45+
return [];
46+
}
47+
48+
if (!$inMethod->isStatic()) {
49+
return [];
50+
}
51+
52+
return [
53+
RuleErrorBuilder::message(sprintf(
54+
'Unsafe usage of new static() in abstract class %s in static method %s().',
55+
$classReflection->getDisplayName(),
56+
$inMethod->getName(),
57+
))
58+
->identifier('new.staticInAbstractClassStaticMethod')
59+
->tip(sprintf('Direct call to %s::%s() would crash because an abstract class cannot be instantiated.', $classReflection->getName(), $inMethod->getName()))
60+
->build(),
61+
];
62+
}
63+
64+
}

0 commit comments

Comments
 (0)