Skip to content

Commit

Permalink
Generic RuleErrorBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Apr 13, 2023
1 parent 316339e commit 9a3ed85
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 8 deletions.
2 changes: 1 addition & 1 deletion build/phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ parameters:
message: '#Fetching class constant class of deprecated class DeprecatedAnnotations\\DeprecatedWithMultipleTags.#'
path: ../tests/PHPStan/Reflection/Annotations/DeprecatedAnnotationsTest.php
-
message: '#^Variable property access on PHPStan\\Rules\\RuleError\.$#'
message: '#^Variable property access on T of PHPStan\\Rules\\RuleError\.$#'
path: ../src/Rules/RuleErrorBuilder.php
-
message: "#^Parameter \\#1 (?:\\$argument|\\$objectOrClass) of class ReflectionClass constructor expects class\\-string\\<PHPStan\\\\ExtensionInstaller\\\\GeneratedConfig\\>\\|PHPStan\\\\ExtensionInstaller\\\\GeneratedConfig, string given\\.$#"
Expand Down
2 changes: 1 addition & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ parameters:
path: src/Rules/PhpDoc/VarTagTypeRuleHelper.php

-
message: "#^Access to an undefined property PHPStan\\\\Rules\\\\RuleError\\:\\:\\$tip\\.$#"
message: "#^Access to an undefined property T of PHPStan\\\\Rules\\\\RuleError\\:\\:\\$tip\\.$#"
count: 2
path: src/Rules/RuleErrorBuilder.php

Expand Down
45 changes: 43 additions & 2 deletions src/Rules/RuleErrorBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
use function implode;
use function sprintf;

/** @api */
/**
* @api
* @template-covariant T of RuleError
*/
class RuleErrorBuilder
{

Expand Down Expand Up @@ -86,11 +89,18 @@ public static function getRuleErrorTypes(): array
];
}

/**
* @return self<RuleError>
*/
public static function message(string $message): self
{
return new self($message);
}

/**
* @phpstan-this-out self<T&LineRuleError>
* @return self<T&LineRuleError>
*/
public function line(int $line): self
{
$this->properties['line'] = $line;
Expand All @@ -99,6 +109,10 @@ public function line(int $line): self
return $this;
}

/**
* @phpstan-this-out self<T&FileRuleError>
* @return self<T&FileRuleError>
*/
public function file(string $file): self
{
$this->properties['file'] = $file;
Expand All @@ -107,6 +121,10 @@ public function file(string $file): self
return $this;
}

/**
* @phpstan-this-out self<T&TipRuleError>
* @return self<T&TipRuleError>
*/
public function tip(string $tip): self
{
$this->tips = [$tip];
Expand All @@ -115,6 +133,10 @@ public function tip(string $tip): self
return $this;
}

/**
* @phpstan-this-out self<T&TipRuleError>
* @return self<T&TipRuleError>
*/
public function addTip(string $tip): self
{
$this->tips[] = $tip;
Expand All @@ -123,13 +145,19 @@ public function addTip(string $tip): self
return $this;
}

/**
* @phpstan-this-out self<T&TipRuleError>
* @return self<T&TipRuleError>
*/
public function discoveringSymbolsTip(): self
{
return $this->tip('Learn more at https://phpstan.org/user-guide/discovering-symbols');
}

/**
* @param list<string> $reasons
* @phpstan-this-out self<T&TipRuleError>
* @return self<T&TipRuleError>
*/
public function acceptsReasonsTip(array $reasons): self
{
Expand All @@ -140,6 +168,10 @@ public function acceptsReasonsTip(array $reasons): self
return $this;
}

/**
* @phpstan-this-out self<T&IdentifierRuleError>
* @return self<T&IdentifierRuleError>
*/
public function identifier(string $identifier): self
{
$this->properties['identifier'] = $identifier;
Expand All @@ -150,6 +182,8 @@ public function identifier(string $identifier): self

/**
* @param mixed[] $metadata
* @phpstan-this-out self<T&MetadataRuleError>
* @return self<T&MetadataRuleError>
*/
public function metadata(array $metadata): self
{
Expand All @@ -159,16 +193,23 @@ public function metadata(array $metadata): self
return $this;
}

/**
* @phpstan-this-out self<T&NonIgnorableRuleError>
* @return self<T&NonIgnorableRuleError>
*/
public function nonIgnorable(): self
{
$this->type |= self::TYPE_NON_IGNORABLE;

return $this;
}

/**
* @return T
*/
public function build(): RuleError
{
/** @var class-string<RuleError> $className */
/** @var class-string<T> $className */
$className = sprintf('PHPStan\\Rules\\RuleErrors\\RuleError%d', $this->type);
if (!class_exists($className)) {
throw new ShouldNotHappenException(sprintf('Class %s does not exist.', $className));
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/asymmetric-properties.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9062.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/object-shape.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/rule-error-builder.php');
}

/**
Expand Down
28 changes: 28 additions & 0 deletions tests/PHPStan/Analyser/data/rule-error-builder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace RuleErrorBuilderGenerics;

use PHPStan\Rules\RuleErrorBuilder;
use function PHPStan\Testing\assertType;

class Foo
{

public function doFoo(): void
{
$builder = RuleErrorBuilder::message('test');
assertType('PHPStan\Rules\RuleErrorBuilder<PHPStan\Rules\RuleError>', $builder);
assertType('PHPStan\Rules\RuleError', $builder->build());

$builder->identifier('test');
assertType('PHPStan\Rules\RuleErrorBuilder<PHPStan\Rules\IdentifierRuleError>', $builder);
assertType('PHPStan\Rules\IdentifierRuleError', $builder->build());

assertType('PHPStan\Rules\IdentifierRuleError', RuleErrorBuilder::message('test')->identifier('test')->build());

$builder->tip('test');
assertType('PHPStan\Rules\RuleErrorBuilder<PHPStan\Rules\IdentifierRuleError&PHPStan\Rules\TipRuleError>', $builder);
assertType('PHPStan\Rules\IdentifierRuleError&PHPStan\Rules\TipRuleError', $builder->build());
}

}
8 changes: 4 additions & 4 deletions tests/PHPStan/Rules/RuleErrorBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function testMessageAndLineAndBuild(): void
$ruleError = $builder->build();
$this->assertSame('Foo', $ruleError->getMessage());

$this->assertInstanceOf(LineRuleError::class, $ruleError);
$this->assertInstanceOf(LineRuleError::class, $ruleError); // @phpstan-ignore-line
$this->assertSame(25, $ruleError->getLine());
}

Expand All @@ -30,7 +30,7 @@ public function testMessageAndFileAndBuild(): void
$ruleError = $builder->build();
$this->assertSame('Foo', $ruleError->getMessage());

$this->assertInstanceOf(FileRuleError::class, $ruleError);
$this->assertInstanceOf(FileRuleError::class, $ruleError); // @phpstan-ignore-line
$this->assertSame('Bar.php', $ruleError->getFile());
}

Expand All @@ -40,8 +40,8 @@ public function testMessageAndLineAndFileAndBuild(): void
$ruleError = $builder->build();
$this->assertSame('Foo', $ruleError->getMessage());

$this->assertInstanceOf(LineRuleError::class, $ruleError);
$this->assertInstanceOf(FileRuleError::class, $ruleError);
$this->assertInstanceOf(LineRuleError::class, $ruleError); // @phpstan-ignore-line
$this->assertInstanceOf(FileRuleError::class, $ruleError); // @phpstan-ignore-line
$this->assertSame(25, $ruleError->getLine());
$this->assertSame('Bar.php', $ruleError->getFile());
}
Expand Down

0 comments on commit 9a3ed85

Please sign in to comment.