Skip to content

Commit 98e77b9

Browse files
ondrejmirtesphpstan-bot
authored andcommitted
Generalize dynamicConstantNames constants whose type cannot be widened by generalize() to mixed
- NullType::generalize() returns $this because null has no broader type family, so dynamicConstantNames had no effect on null-valued constants - After calling generalize(), check if the result is still a constant value; if so, return MixedType instead - Fix applies to both resolveConstantType (global constants) and resolveClassConstantType (class constants) - Also fixes the analogous case for empty ConstantArrayType ([]), whose generalize() also returns $this
1 parent 03834ec commit 98e77b9

File tree

3 files changed

+31
-2
lines changed

3 files changed

+31
-2
lines changed

src/Analyser/ConstantResolver.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,11 @@ public function resolveConstantType(string $constantName, Type $constantType): T
424424
return $constantType;
425425
}
426426
if (in_array($constantName, $this->dynamicConstantNames, true)) {
427-
return $constantType->generalize(GeneralizePrecision::lessSpecific());
427+
$generalized = $constantType->generalize(GeneralizePrecision::lessSpecific());
428+
if ($generalized->isConstantValue()->yes()) {
429+
return new MixedType();
430+
}
431+
return $generalized;
428432
}
429433
}
430434

@@ -455,7 +459,11 @@ public function resolveClassConstantType(string $className, string $constantName
455459
}
456460

457461
if ($constantType->isConstantValue()->yes()) {
458-
return $constantType->generalize(GeneralizePrecision::lessSpecific());
462+
$generalized = $constantType->generalize(GeneralizePrecision::lessSpecific());
463+
if ($generalized->isConstantValue()->yes()) {
464+
return new MixedType();
465+
}
466+
return $generalized;
459467
}
460468
}
461469

tests/PHPStan/Analyser/data/dynamic-constant.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@
77
define('GLOBAL_PURE_CONSTANT', 123);
88
define('GLOBAL_DYNAMIC_CONSTANT', false);
99
define('GLOBAL_DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES', null);
10+
define('GLOBAL_DYNAMIC_NULL_CONSTANT', null);
1011

1112
class DynamicConstantClass
1213
{
1314
const DYNAMIC_CONSTANT_IN_CLASS = 'abcdef';
1415
const DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES_IN_CLASS = 'xyz';
1516
const PURE_CONSTANT_IN_CLASS = 'abc123def';
17+
const DYNAMIC_NULL_CONSTANT = null;
18+
const DYNAMIC_TRUE_CONSTANT = true;
19+
const DYNAMIC_FALSE_CONSTANT = false;
20+
const DYNAMIC_EMPTY_ARRAY_CONSTANT = [];
1621
}
1722

1823
class NoDynamicConstantClass
@@ -29,5 +34,16 @@ private function rip()
2934
assertType('bool', GLOBAL_DYNAMIC_CONSTANT);
3035
assertType('123', GLOBAL_PURE_CONSTANT);
3136
assertType('string|null', GLOBAL_DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES);
37+
38+
// Bug 9218: dynamicConstantNames with null value
39+
assertType('mixed', DynamicConstantClass::DYNAMIC_NULL_CONSTANT);
40+
assertType('mixed', GLOBAL_DYNAMIC_NULL_CONSTANT);
41+
42+
// Bool constants should generalize properly
43+
assertType('bool', DynamicConstantClass::DYNAMIC_TRUE_CONSTANT);
44+
assertType('bool', DynamicConstantClass::DYNAMIC_FALSE_CONSTANT);
45+
46+
// Empty array constant should generalize to mixed
47+
assertType('mixed', DynamicConstantClass::DYNAMIC_EMPTY_ARRAY_CONSTANT);
3248
}
3349
}

tests/PHPStan/Analyser/dynamic-constants.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ includes:
44
parameters:
55
dynamicConstantNames:
66
- DynamicConstants\DynamicConstantClass::DYNAMIC_CONSTANT_IN_CLASS
7+
- DynamicConstants\DynamicConstantClass::DYNAMIC_NULL_CONSTANT
8+
- DynamicConstants\DynamicConstantClass::DYNAMIC_TRUE_CONSTANT
9+
- DynamicConstants\DynamicConstantClass::DYNAMIC_FALSE_CONSTANT
10+
- DynamicConstants\DynamicConstantClass::DYNAMIC_EMPTY_ARRAY_CONSTANT
711
- GLOBAL_DYNAMIC_CONSTANT
12+
- GLOBAL_DYNAMIC_NULL_CONSTANT
813
DynamicConstants\DynamicConstantClass::DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES_IN_CLASS: 'string|null'
914
GLOBAL_DYNAMIC_CONSTANT_WITH_EXPLICIT_TYPES: 'string|null'

0 commit comments

Comments
 (0)