Skip to content

Commit 9a3ed85

Browse files
committed
Generic RuleErrorBuilder
1 parent 316339e commit 9a3ed85

File tree

6 files changed

+78
-8
lines changed

6 files changed

+78
-8
lines changed

build/phpstan.neon

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ parameters:
8383
message: '#Fetching class constant class of deprecated class DeprecatedAnnotations\\DeprecatedWithMultipleTags.#'
8484
path: ../tests/PHPStan/Reflection/Annotations/DeprecatedAnnotationsTest.php
8585
-
86-
message: '#^Variable property access on PHPStan\\Rules\\RuleError\.$#'
86+
message: '#^Variable property access on T of PHPStan\\Rules\\RuleError\.$#'
8787
path: ../src/Rules/RuleErrorBuilder.php
8888
-
8989
message: "#^Parameter \\#1 (?:\\$argument|\\$objectOrClass) of class ReflectionClass constructor expects class\\-string\\<PHPStan\\\\ExtensionInstaller\\\\GeneratedConfig\\>\\|PHPStan\\\\ExtensionInstaller\\\\GeneratedConfig, string given\\.$#"

phpstan-baseline.neon

+1-1
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ parameters:
624624
path: src/Rules/PhpDoc/VarTagTypeRuleHelper.php
625625

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

src/Rules/RuleErrorBuilder.php

+43-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
use function implode;
1010
use function sprintf;
1111

12-
/** @api */
12+
/**
13+
* @api
14+
* @template-covariant T of RuleError
15+
*/
1316
class RuleErrorBuilder
1417
{
1518

@@ -86,11 +89,18 @@ public static function getRuleErrorTypes(): array
8689
];
8790
}
8891

92+
/**
93+
* @return self<RuleError>
94+
*/
8995
public static function message(string $message): self
9096
{
9197
return new self($message);
9298
}
9399

100+
/**
101+
* @phpstan-this-out self<T&LineRuleError>
102+
* @return self<T&LineRuleError>
103+
*/
94104
public function line(int $line): self
95105
{
96106
$this->properties['line'] = $line;
@@ -99,6 +109,10 @@ public function line(int $line): self
99109
return $this;
100110
}
101111

112+
/**
113+
* @phpstan-this-out self<T&FileRuleError>
114+
* @return self<T&FileRuleError>
115+
*/
102116
public function file(string $file): self
103117
{
104118
$this->properties['file'] = $file;
@@ -107,6 +121,10 @@ public function file(string $file): self
107121
return $this;
108122
}
109123

124+
/**
125+
* @phpstan-this-out self<T&TipRuleError>
126+
* @return self<T&TipRuleError>
127+
*/
110128
public function tip(string $tip): self
111129
{
112130
$this->tips = [$tip];
@@ -115,6 +133,10 @@ public function tip(string $tip): self
115133
return $this;
116134
}
117135

136+
/**
137+
* @phpstan-this-out self<T&TipRuleError>
138+
* @return self<T&TipRuleError>
139+
*/
118140
public function addTip(string $tip): self
119141
{
120142
$this->tips[] = $tip;
@@ -123,13 +145,19 @@ public function addTip(string $tip): self
123145
return $this;
124146
}
125147

148+
/**
149+
* @phpstan-this-out self<T&TipRuleError>
150+
* @return self<T&TipRuleError>
151+
*/
126152
public function discoveringSymbolsTip(): self
127153
{
128154
return $this->tip('Learn more at https://phpstan.org/user-guide/discovering-symbols');
129155
}
130156

131157
/**
132158
* @param list<string> $reasons
159+
* @phpstan-this-out self<T&TipRuleError>
160+
* @return self<T&TipRuleError>
133161
*/
134162
public function acceptsReasonsTip(array $reasons): self
135163
{
@@ -140,6 +168,10 @@ public function acceptsReasonsTip(array $reasons): self
140168
return $this;
141169
}
142170

171+
/**
172+
* @phpstan-this-out self<T&IdentifierRuleError>
173+
* @return self<T&IdentifierRuleError>
174+
*/
143175
public function identifier(string $identifier): self
144176
{
145177
$this->properties['identifier'] = $identifier;
@@ -150,6 +182,8 @@ public function identifier(string $identifier): self
150182

151183
/**
152184
* @param mixed[] $metadata
185+
* @phpstan-this-out self<T&MetadataRuleError>
186+
* @return self<T&MetadataRuleError>
153187
*/
154188
public function metadata(array $metadata): self
155189
{
@@ -159,16 +193,23 @@ public function metadata(array $metadata): self
159193
return $this;
160194
}
161195

196+
/**
197+
* @phpstan-this-out self<T&NonIgnorableRuleError>
198+
* @return self<T&NonIgnorableRuleError>
199+
*/
162200
public function nonIgnorable(): self
163201
{
164202
$this->type |= self::TYPE_NON_IGNORABLE;
165203

166204
return $this;
167205
}
168206

207+
/**
208+
* @return T
209+
*/
169210
public function build(): RuleError
170211
{
171-
/** @var class-string<RuleError> $className */
212+
/** @var class-string<T> $className */
172213
$className = sprintf('PHPStan\\Rules\\RuleErrors\\RuleError%d', $this->type);
173214
if (!class_exists($className)) {
174215
throw new ShouldNotHappenException(sprintf('Class %s does not exist.', $className));

tests/PHPStan/Analyser/NodeScopeResolverTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,7 @@ public function dataFileAsserts(): iterable
12431243
yield from $this->gatherAssertTypes(__DIR__ . '/data/asymmetric-properties.php');
12441244
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9062.php');
12451245
yield from $this->gatherAssertTypes(__DIR__ . '/data/object-shape.php');
1246+
yield from $this->gatherAssertTypes(__DIR__ . '/data/rule-error-builder.php');
12461247
}
12471248

12481249
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace RuleErrorBuilderGenerics;
4+
5+
use PHPStan\Rules\RuleErrorBuilder;
6+
use function PHPStan\Testing\assertType;
7+
8+
class Foo
9+
{
10+
11+
public function doFoo(): void
12+
{
13+
$builder = RuleErrorBuilder::message('test');
14+
assertType('PHPStan\Rules\RuleErrorBuilder<PHPStan\Rules\RuleError>', $builder);
15+
assertType('PHPStan\Rules\RuleError', $builder->build());
16+
17+
$builder->identifier('test');
18+
assertType('PHPStan\Rules\RuleErrorBuilder<PHPStan\Rules\IdentifierRuleError>', $builder);
19+
assertType('PHPStan\Rules\IdentifierRuleError', $builder->build());
20+
21+
assertType('PHPStan\Rules\IdentifierRuleError', RuleErrorBuilder::message('test')->identifier('test')->build());
22+
23+
$builder->tip('test');
24+
assertType('PHPStan\Rules\RuleErrorBuilder<PHPStan\Rules\IdentifierRuleError&PHPStan\Rules\TipRuleError>', $builder);
25+
assertType('PHPStan\Rules\IdentifierRuleError&PHPStan\Rules\TipRuleError', $builder->build());
26+
}
27+
28+
}

tests/PHPStan/Rules/RuleErrorBuilderTest.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public function testMessageAndLineAndBuild(): void
2020
$ruleError = $builder->build();
2121
$this->assertSame('Foo', $ruleError->getMessage());
2222

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

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

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

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

43-
$this->assertInstanceOf(LineRuleError::class, $ruleError);
44-
$this->assertInstanceOf(FileRuleError::class, $ruleError);
43+
$this->assertInstanceOf(LineRuleError::class, $ruleError); // @phpstan-ignore-line
44+
$this->assertInstanceOf(FileRuleError::class, $ruleError); // @phpstan-ignore-line
4545
$this->assertSame(25, $ruleError->getLine());
4646
$this->assertSame('Bar.php', $ruleError->getFile());
4747
}

0 commit comments

Comments
 (0)