Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ jobs:
extensions: ""
- script: "bin/phpstan analyse e2e/only-files-not-analysed-trait/src/Foo.php e2e/only-files-not-analysed-trait/src/BarTrait.php -c e2e/only-files-not-analysed-trait/no-ignore.neon"
extensions: ""
- script: |
cd e2e/baseline-uninit-prop-trait
../../bin/phpstan analyse --debug --configuration test-no-baseline.neon --generate-baseline test-baseline.neon
../../bin/phpstan analyse --debug --configuration test.neon
- script: |
cd e2e/baseline-uninit-prop-trait
../../bin/phpstan analyse --configuration test-no-baseline.neon --generate-baseline test-baseline.neon
../../bin/phpstan analyse --configuration test.neon

steps:
- name: "Checkout"
Expand Down
8 changes: 2 additions & 6 deletions bin/generate-rule-error-classes.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,13 @@ class RuleError%s implements %s
}
$properties = [];
$interfaces = [];
foreach ($ruleErrorTypes as $typeNumber => [$interface, $propertyName, $nativePropertyType, $phpDocPropertyType]) {
foreach ($ruleErrorTypes as $typeNumber => [$interface, $typeProperties]) {
if (!(($typeCombination & $typeNumber) === $typeNumber)) {
continue;
}

$interfaces[] = '\\' . $interface;
if ($propertyName === null || $nativePropertyType === null || $phpDocPropertyType === null) {
continue;
}

$properties[] = [$propertyName, $nativePropertyType, $phpDocPropertyType];
$properties = array_merge($properties, $typeProperties);
}

$phpClass = sprintf(
Expand Down
18 changes: 18 additions & 0 deletions e2e/baseline-uninit-prop-trait/src/Foo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Readonly\Uninit\Bug;

trait Foo
{
protected readonly int $x;

public function foo(): void
{
echo $this->x;
}

public function init(): void
{
$this->x = rand();
}
}
14 changes: 14 additions & 0 deletions e2e/baseline-uninit-prop-trait/src/HelloWorld.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Readonly\Uninit\Bug;

class HelloWorld
{
use Foo;

public function __construct()
{
$this->init();
$this->foo();
}
}
4 changes: 4 additions & 0 deletions e2e/baseline-uninit-prop-trait/test-no-baseline.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
parameters:
level: 9
paths:
- src
3 changes: 3 additions & 0 deletions e2e/baseline-uninit-prop-trait/test.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
includes:
- test-baseline.neon
- test-no-baseline.neon
2 changes: 1 addition & 1 deletion src/Analyser/RuleErrorTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public function transform(
$ruleError instanceof FileRuleError
&& $ruleError->getFile() !== ''
) {
$fileName = $ruleError->getFile();
$fileName = $ruleError->getFileDescription();
$filePath = $ruleError->getFile();
$traitFilePath = null;
}
Expand Down
3 changes: 2 additions & 1 deletion src/Node/ClassPropertiesNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public function getClassReflection(): ClassReflection
/**
* @param string[] $constructors
* @param ReadWritePropertiesExtension[]|null $extensions
* @return array{array<string, ClassPropertyNode>, array<array{string, int, ClassPropertyNode, string}>, array<array{string, int, ClassPropertyNode}>}
* @return array{array<string, ClassPropertyNode>, array<array{string, int, ClassPropertyNode, string, string}>, array<array{string, int, ClassPropertyNode}>}
*/
public function getUninitializedProperties(
Scope $scope,
Expand Down Expand Up @@ -231,6 +231,7 @@ public function getUninitializedProperties(
$propertyName,
$fetch->getLine(),
$originalProperties[$propertyName],
$usageScope->getFile(),
$usageScope->getFileDescription(),
];
}
Expand Down
2 changes: 2 additions & 0 deletions src/Rules/FileRuleError.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ interface FileRuleError extends RuleError

public function getFile(): string;

public function getFileDescription(): string;

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ public function processNode(Node $node, Scope $scope): array
))->line($propertyNode->getLine())->build();
}

foreach ($prematureAccess as [$propertyName, $line, $propertyNode, $file]) {
foreach ($prematureAccess as [$propertyName, $line, $propertyNode, $file, $fileDescription]) {
if (!$propertyNode->isReadOnlyByPhpDoc() || $propertyNode->isReadOnly()) {
continue;
}
$errors[] = RuleErrorBuilder::message(sprintf(
'Access to an uninitialized @readonly property %s::$%s.',
$classReflection->getDisplayName(),
$propertyName,
))->line($line)->file($file)->build();
))->line($line)->file($file, $fileDescription)->build();
}

foreach ($additionalAssigns as [$propertyName, $line, $propertyNode]) {
Expand Down
4 changes: 2 additions & 2 deletions src/Rules/Properties/MissingReadOnlyPropertyAssignRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ public function processNode(Node $node, Scope $scope): array
))->line($propertyNode->getLine())->build();
}

foreach ($prematureAccess as [$propertyName, $line, $propertyNode, $file]) {
foreach ($prematureAccess as [$propertyName, $line, $propertyNode, $file, $fileDescription]) {
if (!$propertyNode->isReadOnly()) {
continue;
}
$errors[] = RuleErrorBuilder::message(sprintf(
'Access to an uninitialized readonly property %s::$%s.',
$classReflection->getDisplayName(),
$propertyName,
))->line($line)->file($file)->build();
))->line($line)->file($file, $fileDescription)->build();
}

foreach ($additionalAssigns as [$propertyName, $line, $propertyNode]) {
Expand Down
4 changes: 2 additions & 2 deletions src/Rules/Properties/UninitializedPropertyRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ public function processNode(Node $node, Scope $scope): array
))->line($propertyNode->getLine())->build();
}

foreach ($prematureAccess as [$propertyName, $line, $propertyNode, $file]) {
foreach ($prematureAccess as [$propertyName, $line, $propertyNode, $file, $fileDescription]) {
if ($propertyNode->isReadOnly() || $propertyNode->isReadOnlyByPhpDoc()) {
continue;
}
$errors[] = RuleErrorBuilder::message(sprintf(
'Access to an uninitialized property %s::$%s.',
$classReflection->getDisplayName(),
$propertyName,
))->line($line)->file($file)->build();
))->line($line)->file($file, $fileDescription)->build();
}

return $errors;
Expand Down
78 changes: 55 additions & 23 deletions src/Rules/RuleErrorBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use function class_exists;
use function count;
use function implode;
use function is_file;
use function sprintf;

/** @api */
Expand Down Expand Up @@ -36,52 +37,79 @@ private function __construct(string $message)
}

/**
* @return array<int, array{string, string|null, string|null, string|null}>
* @return array<int, array{string, array<array{string|null, string|null, string|null}>}>
*/
public static function getRuleErrorTypes(): array
{
return [
self::TYPE_MESSAGE => [
RuleError::class,
'message',
'string',
'string',
[
[
'message',
'string',
'string',
],
],
],
self::TYPE_LINE => [
LineRuleError::class,
'line',
'int',
'int',
[
[
'line',
'int',
'int',
],
],
],
self::TYPE_FILE => [
FileRuleError::class,
'file',
'string',
'string',
[
[
'file',
'string',
'string',
],
[
'fileDescription',
'string',
'string',
],
],
],
self::TYPE_TIP => [
TipRuleError::class,
'tip',
'string',
'string',
[
[
'tip',
'string',
'string',
],
],
],
self::TYPE_IDENTIFIER => [
IdentifierRuleError::class,
'identifier',
'string',
'string',
[
[
'identifier',
'string',
'string',
],
],
],
self::TYPE_METADATA => [
MetadataRuleError::class,
'metadata',
'array',
'mixed[]',
[
[
'metadata',
'array',
'mixed[]',
],
],
],
self::TYPE_NON_IGNORABLE => [
NonIgnorableRuleError::class,
null,
null,
null,
[],
],
];
}
Expand All @@ -99,9 +127,13 @@ public function line(int $line): self
return $this;
}

public function file(string $file): self
public function file(string $file, ?string $fileDescription = null): self
{
if (!is_file($file)) {
throw new ShouldNotHappenException(sprintf('File %s does not exist.', $file));
}
$this->properties['file'] = $file;
$this->properties['fileDescription'] = $fileDescription ?? $file;
$this->type |= self::TYPE_FILE;

return $this;
Expand Down
7 changes: 7 additions & 0 deletions src/Rules/RuleErrors/RuleError101.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class RuleError101 implements RuleError, FileRuleError, MetadataRuleError, NonIg

public string $file;

public string $fileDescription;

/** @var mixed[] */
public array $metadata;

Expand All @@ -30,6 +32,11 @@ public function getFile(): string
return $this->file;
}

public function getFileDescription(): string
{
return $this->fileDescription;
}

/**
* @return mixed[]
*/
Expand Down
7 changes: 7 additions & 0 deletions src/Rules/RuleErrors/RuleError103.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class RuleError103 implements RuleError, LineRuleError, FileRuleError, MetadataR

public string $file;

public string $fileDescription;

/** @var mixed[] */
public array $metadata;

Expand All @@ -38,6 +40,11 @@ public function getFile(): string
return $this->file;
}

public function getFileDescription(): string
{
return $this->fileDescription;
}

/**
* @return mixed[]
*/
Expand Down
7 changes: 7 additions & 0 deletions src/Rules/RuleErrors/RuleError109.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class RuleError109 implements RuleError, FileRuleError, TipRuleError, MetadataRu

public string $file;

public string $fileDescription;

public string $tip;

/** @var mixed[] */
Expand All @@ -33,6 +35,11 @@ public function getFile(): string
return $this->file;
}

public function getFileDescription(): string
{
return $this->fileDescription;
}

public function getTip(): string
{
return $this->tip;
Expand Down
7 changes: 7 additions & 0 deletions src/Rules/RuleErrors/RuleError111.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class RuleError111 implements RuleError, LineRuleError, FileRuleError, TipRuleEr

public string $file;

public string $fileDescription;

public string $tip;

/** @var mixed[] */
Expand All @@ -41,6 +43,11 @@ public function getFile(): string
return $this->file;
}

public function getFileDescription(): string
{
return $this->fileDescription;
}

public function getTip(): string
{
return $this->tip;
Expand Down
7 changes: 7 additions & 0 deletions src/Rules/RuleErrors/RuleError117.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class RuleError117 implements RuleError, FileRuleError, IdentifierRuleError, Met

public string $file;

public string $fileDescription;

public string $identifier;

/** @var mixed[] */
Expand All @@ -33,6 +35,11 @@ public function getFile(): string
return $this->file;
}

public function getFileDescription(): string
{
return $this->fileDescription;
}

public function getIdentifier(): string
{
return $this->identifier;
Expand Down
Loading