Skip to content

Commit 6ff4aa2

Browse files
authored
Merge pull request #11836 from alies-dev/6.x-contrvariant-keyword-backport
[6.x] Ignore inline `covariant`/`contravariant` keywords in generic type parameters (backport)
2 parents 614c7e8 + f6cbb80 commit 6ff4aa2

4 files changed

Lines changed: 65 additions & 0 deletions

File tree

src/Psalm/Internal/Type/ParseTreeCreator.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,15 @@ public function create(): ParseTree
145145
$this->handleIsOrAs($type_token);
146146
break;
147147

148+
case 'covariant':
149+
case 'contravariant':
150+
if ($this->t + 1 < $this->type_token_count
151+
&& $this->type_tokens[$this->t + 1][0] === ' '
152+
) {
153+
$this->t++;
154+
}
155+
break;
156+
148157
default:
149158
$this->handleValue($type_token);
150159
break;

src/Psalm/Internal/Type/TypeTokenizer.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ public static function tokenize(string $string_type, bool $ignore_space = true):
165165
$type_tokens[++$rtc] = ['', ++$i];
166166
$was_char = false;
167167
continue;
168+
} elseif ($was_space
169+
&& in_array($type_tokens[$rtc][0], ['covariant', 'contravariant'], true)
170+
) {
171+
$type_tokens[++$rtc] = ['', $i];
168172
} elseif ($was_char) {
169173
$type_tokens[++$rtc] = ['', $i];
170174
}
@@ -364,6 +368,7 @@ public static function getFullyQualifiedTokens(
364368
$string_type_token[0],
365369
[
366370
'<', '>', '|', '?', ',', '{', '}', ':', '::', '[', ']', '(', ')', '&', '=', '...', 'as', 'is',
371+
'covariant', 'contravariant',
367372
],
368373
true,
369374
)) {

tests/Template/ClassTemplateCovarianceTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,37 @@ function run(Container $container): void{}
532532
533533
run(new Container(new TypeA()));',
534534
],
535+
'inlineCovariantModifierIgnored' => [
536+
'code' => '<?php
537+
/**
538+
* @template-covariant T
539+
* @template TParent
540+
*/
541+
class Container {}
542+
543+
/**
544+
* @template T of object
545+
* @param class-string<T> $class
546+
* @return Container<covariant T, covariant string>
547+
*/
548+
function wrap(string $class): Container
549+
{
550+
/** @var Container<covariant T, covariant string> */
551+
return new Container();
552+
}',
553+
],
554+
'inlineContravariantModifierIgnored' => [
555+
'code' => '<?php
556+
/**
557+
* @template T
558+
*/
559+
class Box {}
560+
561+
/**
562+
* @param Box<contravariant string> $box
563+
*/
564+
function consume(Box $box): void {}',
565+
],
535566
];
536567
}
537568

tests/TypeParseTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,26 @@ public function testGeneric(): void
191191
$this->assertSame('B<int>', (string) Type::parseString('B<int>'));
192192
}
193193

194+
public function testGenericWithInlineCovariant(): void
195+
{
196+
$this->assertSame('B<int>', (string) Type::parseString('B<covariant int>'));
197+
}
198+
199+
public function testGenericWithInlineContravariant(): void
200+
{
201+
$this->assertSame('B<int>', (string) Type::parseString('B<contravariant int>'));
202+
}
203+
204+
public function testGenericWithMultipleInlineVariance(): void
205+
{
206+
$this->assertSame('B<int, string>', (string) Type::parseString('B<covariant int, contravariant string>'));
207+
}
208+
209+
public function testGenericWithInlineVarianceAndUnion(): void
210+
{
211+
$this->assertSame('B<int|string>', (string) Type::parseString('B<covariant int|string>'));
212+
}
213+
194214
public function testIntersection(): void
195215
{
196216
$this->assertSame('I1&I2&I3', (string) Type::parseString('I1&I2&I3'));

0 commit comments

Comments
 (0)