Skip to content

Commit f0d2c7a

Browse files
Fix
1 parent 2b8dae7 commit f0d2c7a

File tree

3 files changed

+36
-19
lines changed

3 files changed

+36
-19
lines changed

src/Rules/PhpDoc/VarTagTypeRuleHelper.php

+33-19
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use PHPStan\Rules\IdentifierRuleError;
1313
use PHPStan\Rules\RuleErrorBuilder;
1414
use PHPStan\Type\ArrayType;
15+
use PHPStan\Type\FileTypeMapper;
1516
use PHPStan\Type\Generic\GenericObjectType;
1617
use PHPStan\Type\MixedType;
1718
use PHPStan\Type\ObjectType;
@@ -28,6 +29,7 @@ final class VarTagTypeRuleHelper
2829

2930
public function __construct(
3031
private TypeNodeResolver $typeNodeResolver,
32+
private FileTypeMapper $fileTypeMapper,
3133
private bool $checkTypeAgainstPhpDocType,
3234
private bool $strictWideningCheck,
3335
)
@@ -82,7 +84,7 @@ public function checkExprType(Scope $scope, Node\Expr $expr, Type $varTagType):
8284
$errors = [];
8385
$exprNativeType = $scope->getNativeType($expr);
8486
$containsPhpStanType = $this->containsPhpStanType($varTagType);
85-
if ($this->shouldVarTagTypeBeReported($expr, $exprNativeType, $varTagType)) {
87+
if ($this->shouldVarTagTypeBeReported($scope, $expr, $exprNativeType, $varTagType)) {
8688
$verbosity = VerbosityLevel::getRecommendedLevelByType($exprNativeType, $varTagType);
8789
$errors[] = RuleErrorBuilder::message(sprintf(
8890
'PHPDoc tag @var with type %s is not subtype of native type %s.',
@@ -92,7 +94,7 @@ public function checkExprType(Scope $scope, Node\Expr $expr, Type $varTagType):
9294
} else {
9395
$exprType = $scope->getType($expr);
9496
if (
95-
$this->shouldVarTagTypeBeReported($expr, $exprType, $varTagType)
97+
$this->shouldVarTagTypeBeReported($scope, $expr, $exprType, $varTagType)
9698
&& ($this->checkTypeAgainstPhpDocType || $containsPhpStanType)
9799
) {
98100
$verbosity = VerbosityLevel::getRecommendedLevelByType($exprType, $varTagType);
@@ -133,22 +135,22 @@ private function containsPhpStanType(Type $type): bool
133135
return false;
134136
}
135137

136-
private function shouldVarTagTypeBeReported(Node\Expr $expr, Type $type, Type $varTagType): bool
138+
private function shouldVarTagTypeBeReported(Scope $scope, Node\Expr $expr, Type $type, Type $varTagType): bool
137139
{
138140
if ($expr instanceof Expr\Array_) {
139141
if ($expr->items === []) {
140142
$type = new ArrayType(new MixedType(), new MixedType());
141143
}
142144

143-
return !$this->isAtLeastMaybeSuperTypeOfVarType($type, $varTagType);
145+
return !$this->isAtLeastMaybeSuperTypeOfVarType($scope, $type, $varTagType);
144146
}
145147

146148
if ($expr instanceof Expr\ConstFetch) {
147-
return !$this->isAtLeastMaybeSuperTypeOfVarType($type, $varTagType);
149+
return !$this->isAtLeastMaybeSuperTypeOfVarType($scope, $type, $varTagType);
148150
}
149151

150152
if ($expr instanceof Node\Scalar) {
151-
return !$this->isAtLeastMaybeSuperTypeOfVarType($type, $varTagType);
153+
return !$this->isAtLeastMaybeSuperTypeOfVarType($scope, $type, $varTagType);
152154
}
153155

154156
if ($expr instanceof Expr\New_) {
@@ -157,64 +159,76 @@ private function shouldVarTagTypeBeReported(Node\Expr $expr, Type $type, Type $v
157159
}
158160
}
159161

160-
return $this->checkType($type, $varTagType);
162+
return $this->checkType($scope, $type, $varTagType);
161163
}
162164

163-
private function checkType(Type $type, Type $varTagType, int $depth = 0): bool
165+
private function checkType(Scope $scope, Type $type, Type $varTagType, int $depth = 0): bool
164166
{
165167
if ($this->strictWideningCheck) {
166-
return !$this->isSuperTypeOfVarType($type, $varTagType);
168+
return !$this->isSuperTypeOfVarType($scope, $type, $varTagType);
167169
}
168170

169171
if ($type->isConstantArray()->yes()) {
170172
if ($type->isIterableAtLeastOnce()->no()) {
171173
$type = new ArrayType(new MixedType(), new MixedType());
172-
return !$this->isAtLeastMaybeSuperTypeOfVarType($type, $varTagType);
174+
return !$this->isAtLeastMaybeSuperTypeOfVarType($scope, $type, $varTagType);
173175
}
174176
}
175177

176178
if ($type->isIterable()->yes() && $varTagType->isIterable()->yes()) {
177-
if (!$this->isAtLeastMaybeSuperTypeOfVarType($type, $varTagType)) {
179+
if (!$this->isAtLeastMaybeSuperTypeOfVarType($scope, $type, $varTagType)) {
178180
return true;
179181
}
180182

181183
$innerType = $type->getIterableValueType();
182184
$innerVarTagType = $varTagType->getIterableValueType();
183185

184186
if ($type->equals($innerType) || $varTagType->equals($innerVarTagType)) {
185-
return !$this->isSuperTypeOfVarType($innerType, $innerVarTagType);
187+
return !$this->isSuperTypeOfVarType($scope, $innerType, $innerVarTagType);
186188
}
187189

188-
return $this->checkType($innerType, $innerVarTagType, $depth + 1);
190+
return $this->checkType($scope, $innerType, $innerVarTagType, $depth + 1);
189191
}
190192

191193
if ($depth === 0 && $type->isConstantValue()->yes()) {
192-
return !$this->isAtLeastMaybeSuperTypeOfVarType($type, $varTagType);
194+
return !$this->isAtLeastMaybeSuperTypeOfVarType($scope, $type, $varTagType);
193195
}
194196

195-
return !$this->isSuperTypeOfVarType($type, $varTagType);
197+
return !$this->isSuperTypeOfVarType($scope, $type, $varTagType);
196198
}
197199

198-
private function isSuperTypeOfVarType(Type $type, Type $varTagType): bool
200+
private function isSuperTypeOfVarType(Scope $scope, Type $type, Type $varTagType): bool
199201
{
200202
if ($type->isSuperTypeOf($varTagType)->yes()) {
201203
return true;
202204
}
203205

204-
$type = $this->typeNodeResolver->resolve($type->toPhpDocNode(), new NameScope(null, []));
206+
$type = $this->typeNodeResolver->resolve($type->toPhpDocNode(), $this->createNameScope($scope));
205207

206208
return $type->isSuperTypeOf($varTagType)->yes();
207209
}
208210

209-
private function isAtLeastMaybeSuperTypeOfVarType(Type $type, Type $varTagType): bool
211+
private function isAtLeastMaybeSuperTypeOfVarType(Scope $scope, Type $type, Type $varTagType): bool
210212
{
211213
if (!$type->isSuperTypeOf($varTagType)->no()) {
212214
return true;
213215
}
214216

215-
$type = $this->typeNodeResolver->resolve($type->toPhpDocNode(), new NameScope(null, []));
217+
$type = $this->typeNodeResolver->resolve($type->toPhpDocNode(), $this->createNameScope($scope));
216218

217219
return !$type->isSuperTypeOf($varTagType)->no();
218220
}
219221

222+
private function createNameScope(Scope $scope): NameScope
223+
{
224+
$function = $scope->getFunction();
225+
226+
return $this->fileTypeMapper->getNameScope(
227+
$scope->getFile(),
228+
$scope->isInClass() ? $scope->getClassReflection()->getName() : null,
229+
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
230+
$function !== null ? $function->getName() : null,
231+
);
232+
}
233+
220234
}

tests/PHPStan/Rules/PhpDoc/VarTagChangedExpressionTypeRuleTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PHPStan\PhpDoc\TypeNodeResolver;
66
use PHPStan\Rules\Rule;
77
use PHPStan\Testing\RuleTestCase;
8+
use PHPStan\Type\FileTypeMapper;
89

910
/**
1011
* @extends RuleTestCase<VarTagChangedExpressionTypeRule>
@@ -16,6 +17,7 @@ protected function getRule(): Rule
1617
{
1718
return new VarTagChangedExpressionTypeRule(new VarTagTypeRuleHelper(
1819
self::getContainer()->getByType(TypeNodeResolver::class),
20+
self::getContainer()->getByType(FileTypeMapper::class),
1921
true,
2022
true,
2123
));

tests/PHPStan/Rules/PhpDoc/WrongVariableNameInVarTagRuleTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ protected function getRule(): Rule
2626
self::getContainer()->getByType(FileTypeMapper::class),
2727
new VarTagTypeRuleHelper(
2828
self::getContainer()->getByType(TypeNodeResolver::class),
29+
self::getContainer()->getByType(FileTypeMapper::class),
2930
$this->checkTypeAgainstPhpDocType,
3031
$this->strictWideningCheck,
3132
),

0 commit comments

Comments
 (0)