Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
6ad22ee
Support for `#[Deprecated]` attribute
ondrejmirtes Nov 27, 2024
03cc9bc
Detect hooked properties outside of constructor
ondrejmirtes Dec 11, 2024
25d18f0
Invoke virtual ClassPropertyNode for hooked promoted properties witho…
ondrejmirtes Dec 11, 2024
acf6d03
Process property hooks
ondrejmirtes Dec 11, 2024
6f95825
Fix PHPDocs with generics in property hooks
ondrejmirtes Dec 13, 2024
a6b230f
Adjust method's ReturnTypeRule for property hooks
ondrejmirtes Dec 14, 2024
6aa0e44
ShortGetPropertyHookReturnTypeRule - level 3
ondrejmirtes Dec 14, 2024
aeec077
Invoke PropertyAssignNode in short set property hook
ondrejmirtes Dec 14, 2024
ec4035d
Set checkUninitializedProperties to false in UnusedPrivatePropertyRul…
ondrejmirtes Dec 15, 2024
df2db6d
Fix UnusedPrivatePropertyRule in regard to property hooks
ondrejmirtes Dec 15, 2024
0d22056
Fix MissingReturnRule for property hooks
ondrejmirtes Dec 15, 2024
21fbe1e
ExtendedPropertyReflection - methods describing hooked properties
ondrejmirtes Dec 15, 2024
bdf6316
Allow wider set hook parameter type when assigning properties outside…
ondrejmirtes Dec 16, 2024
373000a
Fix canChangeTypeAfterAssignment
ondrejmirtes Dec 16, 2024
294ba3e
Test WritingToReadOnlyPropertiesRule for hooked properties
ondrejmirtes Dec 16, 2024
69f2815
Test ReadingWriteOnlyPropertiesRule for hooked properties
ondrejmirtes Dec 16, 2024
81f8ed3
Test generics in TypesAssignedToPropertiesRule for hooked properties
ondrejmirtes Dec 16, 2024
3d5a204
CleaningParser - clean up property hooks
ondrejmirtes Dec 16, 2024
2da91f8
Fix ReadOnlyByPhpDocPropertyAssignRule for hooked properties
ondrejmirtes Dec 18, 2024
a9202b0
Introduce PropertyHookReturnStatementsNode similar to MethodReturnSta…
ondrejmirtes Dec 18, 2024
c72e822
SetNonVirtualPropertyHookAssignRule - level 3
ondrejmirtes Dec 18, 2024
abe0eaa
GetNonVirtualPropertyHookReadRule - level 3
ondrejmirtes Dec 19, 2024
7f6cd04
Test ReturnNullsafeByRefRule with property hooks
ondrejmirtes Dec 19, 2024
a72355e
Support `#[Deprecated]` attribute in property hooks
ondrejmirtes Dec 19, 2024
0bff31c
Support magic `__PROPERTY__` constant in hooks
ondrejmirtes Dec 19, 2024
fe255fe
Test ContinueBreakInLoopRule for property hooks
ondrejmirtes Dec 19, 2024
278cc9e
PropertyHookAttributesRule - level 0
ondrejmirtes Dec 20, 2024
b81fc37
Hooked properties can throw custom exceptions
ondrejmirtes Dec 20, 2024
d858543
TooWidePropertyHookThrowTypeRule - level 4
ondrejmirtes Dec 20, 2024
ca79b6e
ThrowsVoidPropertyHookWithExplicitThrowPointRule - level 3
ondrejmirtes Dec 20, 2024
f9dad4d
MissingCheckedExceptionInPropertyHookThrowsRule
ondrejmirtes Dec 20, 2024
2450039
Adjust InvalidThrowsPhpDocValueRule for property hooks
ondrejmirtes Dec 21, 2024
ffe82b3
Adjust InvalidPhpDocTagValueRule and InvalidPHPStanDocTagRule for pro…
ondrejmirtes Dec 21, 2024
a57eea5
Test MatchExpressionRule with property hooks
ondrejmirtes Dec 21, 2024
e28118e
Extract IncompatiblePhpDocTypeCheck from IncompatiblePhpDocTypeRule
ondrejmirtes Dec 21, 2024
7f3c340
IncompatiblePropertyHookPhpDocTypeRule - level 2
ondrejmirtes Dec 21, 2024
4030fc1
ExistingClassesInPropertyHookTypehintsRule - level 0
ondrejmirtes Dec 21, 2024
7f51614
Useful `getPropertyReflection()` shortcut in property hook virtual nodes
ondrejmirtes Dec 24, 2024
97710e6
ExtendedParameterReflection::hasNativeType()
ondrejmirtes Dec 26, 2024
2f6808a
SetPropertyHookParameterRule - level 0 and 3
ondrejmirtes Dec 24, 2024
85e0c1f
Report missing types in SetPropertyHookParameterRule - level 6
ondrejmirtes Dec 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ lint:
--exclude tests/PHPStan/Rules/Functions/data/arrow-function-nullsafe-by-ref.php \
--exclude tests/PHPStan/Levels/data/namedArguments.php \
--exclude tests/PHPStan/Rules/Keywords/data/continue-break.php \
--exclude tests/PHPStan/Rules/Keywords/data/continue-break-property-hook.php \
--exclude tests/PHPStan/Rules/Properties/data/invalid-callable-property-type.php \
--exclude tests/PHPStan/Rules/Properties/data/properties-in-interface.php \
--exclude tests/PHPStan/Rules/Properties/data/read-only-property.php \
Expand Down Expand Up @@ -89,6 +90,11 @@ lint:
--exclude tests/PHPStan/Rules/Properties/data/hooked-properties-without-bodies-in-class.php \
--exclude tests/PHPStan/Rules/Classes/data/bug-12281.php \
--exclude tests/PHPStan/Rules/Traits/data/bug-12281.php \
--exclude tests/PHPStan/Rules/Classes/data/invalid-hooked-properties.php \
--exclude tests/PHPStan/Parser/data/cleaning-property-hooks-before.php \
--exclude tests/PHPStan/Parser/data/cleaning-property-hooks-after.php \
--exclude tests/PHPStan/Rules/Properties/data/existing-classes-property-hooks.php \
--exclude tests/PHPStan/Rules/Properties/data/set-property-hook-parameter.php \
src tests

cs:
Expand Down
2 changes: 2 additions & 0 deletions build/collision-detector.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"../tests/PHPStan/Analyser/data/multipleParseErrors.php",
"../tests/PHPStan/Parser/data/cleaning-1-before.php",
"../tests/PHPStan/Parser/data/cleaning-1-after.php",
"../tests/PHPStan/Parser/data/cleaning-property-hooks-before.php",
"../tests/PHPStan/Parser/data/cleaning-property-hooks-after.php",
"../tests/PHPStan/Rules/Functions/data/duplicate-function.php",
"../tests/PHPStan/Rules/Classes/data/duplicate-class.php",
"../tests/PHPStan/Rules/Names/data/multiple-namespaces.php",
Expand Down
10 changes: 10 additions & 0 deletions conf/config.level0.neon
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,13 @@ rules:
- PHPStan\Rules\Operators\InvalidIncDecOperationRule
- PHPStan\Rules\Properties\AccessPropertiesInAssignRule
- PHPStan\Rules\Properties\AccessStaticPropertiesInAssignRule
- PHPStan\Rules\Properties\ExistingClassesInPropertyHookTypehintsRule
- PHPStan\Rules\Properties\InvalidCallablePropertyTypeRule
- PHPStan\Rules\Properties\MissingReadOnlyPropertyAssignRule
- PHPStan\Rules\Properties\MissingReadOnlyByPhpDocPropertyAssignRule
- PHPStan\Rules\Properties\PropertiesInInterfaceRule
- PHPStan\Rules\Properties\PropertyAttributesRule
- PHPStan\Rules\Properties\PropertyHookAttributesRule
- PHPStan\Rules\Properties\PropertyInClassRule
- PHPStan\Rules\Properties\ReadOnlyPropertyRule
- PHPStan\Rules\Properties\ReadOnlyByPhpDocPropertyRule
Expand Down Expand Up @@ -210,6 +212,14 @@ services:
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Properties\SetPropertyHookParameterRule
arguments:
checkPhpDocMethodSignatures: %checkPhpDocMethodSignatures%
checkMissingTypehints: %checkMissingTypehints%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Properties\UninitializedPropertyRule

Expand Down
1 change: 1 addition & 0 deletions conf/config.level2.neon
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ rules:
- PHPStan\Rules\PhpDoc\IncompatibleSelfOutTypeRule
- PHPStan\Rules\PhpDoc\IncompatibleClassConstantPhpDocTypeRule
- PHPStan\Rules\PhpDoc\IncompatiblePhpDocTypeRule
- PHPStan\Rules\PhpDoc\IncompatiblePropertyHookPhpDocTypeRule
- PHPStan\Rules\PhpDoc\IncompatiblePropertyPhpDocTypeRule
- PHPStan\Rules\PhpDoc\InvalidThrowsPhpDocValueRule
- PHPStan\Rules\PhpDoc\IncompatibleParamImmediatelyInvokedCallableRule
Expand Down
11 changes: 11 additions & 0 deletions conf/config.level3.neon
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ rules:
- PHPStan\Rules\Generators\YieldTypeRule
- PHPStan\Rules\Methods\ReturnTypeRule
- PHPStan\Rules\Properties\DefaultValueTypesAssignedToPropertiesRule
- PHPStan\Rules\Properties\GetNonVirtualPropertyHookReadRule
- PHPStan\Rules\Properties\ReadOnlyPropertyAssignRule
- PHPStan\Rules\Properties\ReadOnlyByPhpDocPropertyAssignRule
- PHPStan\Rules\Properties\ReadOnlyPropertyAssignRefRule
- PHPStan\Rules\Properties\ReadOnlyByPhpDocPropertyAssignRefRule
- PHPStan\Rules\Properties\SetNonVirtualPropertyHookAssignRule
- PHPStan\Rules\Properties\ShortGetPropertyHookReturnTypeRule
- PHPStan\Rules\Properties\TypesAssignedToPropertiesRule
- PHPStan\Rules\Variables\ParameterOutAssignedTypeRule
- PHPStan\Rules\Variables\ParameterOutExecutionEndTypeRule
Expand Down Expand Up @@ -66,6 +69,14 @@ services:
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Exceptions\ThrowsVoidPropertyHookWithExplicitThrowPointRule
arguments:
exceptionTypeResolver: @exceptionTypeResolver
missingCheckedExceptionInThrows: %exceptions.check.missingCheckedExceptionInThrows%
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Generators\YieldFromTypeRule
arguments:
Expand Down
5 changes: 5 additions & 0 deletions conf/config.level4.neon
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ conditionalTags:
phpstan.rules.rule: %exceptions.check.tooWideThrowType%
PHPStan\Rules\Exceptions\TooWideMethodThrowTypeRule:
phpstan.rules.rule: %exceptions.check.tooWideThrowType%
PHPStan\Rules\Exceptions\TooWidePropertyHookThrowTypeRule:
phpstan.rules.rule: %exceptions.check.tooWideThrowType%

parameters:
checkAdvancedIsset: true
Expand Down Expand Up @@ -241,6 +243,9 @@ services:
-
class: PHPStan\Rules\Exceptions\TooWideMethodThrowTypeRule

-
class: PHPStan\Rules\Exceptions\TooWidePropertyHookThrowTypeRule

-
class: PHPStan\Rules\TooWideTypehints\TooWideMethodReturnTypehintRule
arguments:
Expand Down
13 changes: 13 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ conditionalTags:
phpstan.rules.rule: %exceptions.check.missingCheckedExceptionInThrows%
PHPStan\Rules\Exceptions\MissingCheckedExceptionInMethodThrowsRule:
phpstan.rules.rule: %exceptions.check.missingCheckedExceptionInThrows%
PHPStan\Rules\Exceptions\MissingCheckedExceptionInPropertyHookThrowsRule:
phpstan.rules.rule: %exceptions.check.missingCheckedExceptionInThrows%

services:
-
Expand Down Expand Up @@ -318,6 +320,11 @@ services:
tags:
- phpstan.parser.richParserNodeVisitor

-
class: PHPStan\Parser\PropertyHookNameVisitor
tags:
- phpstan.parser.richParserNodeVisitor

-
class: PHPStan\Node\Printer\ExprPrinter

Expand Down Expand Up @@ -901,6 +908,9 @@ services:
-
class: PHPStan\Rules\Exceptions\MissingCheckedExceptionInMethodThrowsRule

-
class: PHPStan\Rules\Exceptions\MissingCheckedExceptionInPropertyHookThrowsRule

-
class: PHPStan\Rules\Exceptions\MissingCheckedExceptionInThrowsCheck
arguments:
Expand Down Expand Up @@ -1011,6 +1021,9 @@ services:
-
class: PHPStan\Rules\PhpDoc\GenericCallableRuleHelper

-
class: PHPStan\Rules\PhpDoc\IncompatiblePhpDocTypeCheck

-
class: PHPStan\Rules\PhpDoc\VarTagTypeRuleHelper
arguments:
Expand Down
87 changes: 87 additions & 0 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Generator;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\ComplexType;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\BinaryOp;
Expand All @@ -21,6 +22,7 @@
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\Node\InterpolatedStringPart;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
Expand Down Expand Up @@ -2961,6 +2963,7 @@ public function enterClassMethod(
new PhpMethodFromParserNodeReflection(
$this->getClassReflection(),
$classMethod,
null,
$this->getFile(),
$templateTypeMap,
$this->getRealParameterTypes($classMethod),
Expand All @@ -2986,6 +2989,90 @@ public function enterClassMethod(
);
}

/**
* @param Type[] $phpDocParameterTypes
*/
public function enterPropertyHook(
Node\PropertyHook $hook,
string $propertyName,
Identifier|Name|ComplexType|null $nativePropertyTypeNode,
?Type $phpDocPropertyType,
array $phpDocParameterTypes,
?Type $throwType,
?string $deprecatedDescription,
bool $isDeprecated,
?string $phpDocComment,
): self
{
if (!$this->isInClass()) {
throw new ShouldNotHappenException();
}

$phpDocParameterTypes = array_map(fn (Type $type): Type => $this->transformStaticType(TemplateTypeHelper::toArgument($type)), $phpDocParameterTypes);

$hookName = $hook->name->toLowerString();
if ($hookName === 'set') {
if ($hook->params === []) {
$hook = clone $hook;
$hook->params = [
new Node\Param(new Variable('value'), null, $nativePropertyTypeNode),
];
}

$firstParam = $hook->params[0] ?? null;
if (
$firstParam !== null
&& $phpDocPropertyType !== null
&& $firstParam->var instanceof Variable
&& is_string($firstParam->var->name)
) {
$valueParamPhpDocType = $phpDocParameterTypes[$firstParam->var->name] ?? null;
if ($valueParamPhpDocType === null) {
$phpDocParameterTypes[$firstParam->var->name] = $this->transformStaticType(TemplateTypeHelper::toArgument($phpDocPropertyType));
}
}

$realReturnType = new VoidType();
$phpDocReturnType = null;
} elseif ($hookName === 'get') {
$realReturnType = $this->getFunctionType($nativePropertyTypeNode, false, false);
$phpDocReturnType = $phpDocPropertyType !== null ? $this->transformStaticType(TemplateTypeHelper::toArgument($phpDocPropertyType)) : null;
} else {
throw new ShouldNotHappenException();
}

$realParameterTypes = $this->getRealParameterTypes($hook);

return $this->enterFunctionLike(
new PhpMethodFromParserNodeReflection(
$this->getClassReflection(),
$hook,
$propertyName,
$this->getFile(),
TemplateTypeMap::createEmpty(),
$realParameterTypes,
$phpDocParameterTypes,
[],
$realReturnType,
$phpDocReturnType,
$throwType,
$deprecatedDescription,
$isDeprecated,
false,
false,
false,
true,
Assertions::createEmpty(),
null,
$phpDocComment,
[],
[],
[],
),
true,
);
}

private function transformStaticType(Type $type): Type
{
return TypeTraverser::map($type, function (Type $type, callable $traverse): Type {
Expand Down
Loading
Loading