Skip to content

Commit

Permalink
Use inferred type instead of annotated type where possible.
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrolGenhald committed Jan 22, 2022
1 parent df31465 commit c877ce0
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 23 deletions.
34 changes: 34 additions & 0 deletions src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
use function gettype;
use function implode;
use function in_array;
use function is_float;
use function is_int;
use function is_string;
use function preg_match;
use function preg_replace;
use function strtolower;
Expand Down Expand Up @@ -512,6 +515,37 @@ public static function getTypeFromValue($value): Union
}
}

/**
* Gets the Psalm literal type from a particular value
*
* @param array|scalar|null $value
*
*/
public static function getLiteralTypeFromValue($value): Type\Union
{
if (is_string($value)) {
return Type::getString($value);
}

if (is_int($value)) {
return Type::getInt(false, $value);
}

if (is_float($value)) {
return Type::getFloat($value);
}

if ($value === false) {
return Type::getFalse();
}

if ($value === true) {
return Type::getTrue();
}

return Type::getNull();
}

/**
* @param string[] $suppressed_issues
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ public static function analyze(
$class_visibility,
$statements_analyzer,
[],
$stmt->class->parts[0] === "self"
$stmt->class->parts[0] === "static"
);
} catch (InvalidArgumentException $_) {
return true;
Expand Down
4 changes: 2 additions & 2 deletions src/Psalm/Internal/Codebase/ClassLikes.php
Original file line number Diff line number Diff line change
Expand Up @@ -1633,7 +1633,7 @@ public function getClassConstantType(
int $visibility,
?StatementsAnalyzer $statements_analyzer = null,
array $visited_constant_ids = [],
bool $static_binding = false
bool $late_static_binding = true
): ?Union {
$class_name = strtolower($class_name);

Expand Down Expand Up @@ -1671,7 +1671,7 @@ public function getClassConstantType(
}
}

return $static_binding ? $constant_storage->inferred_type : $constant_storage->type;
return $late_static_binding ? $constant_storage->type : ($constant_storage->inferred_type ?? null);
} elseif (isset($storage->enum_cases[$constant_name])) {
return new Union([new TEnumCase($storage->name, $constant_name)]);
}
Expand Down
1 change: 1 addition & 0 deletions src/Psalm/Internal/Codebase/Reflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ public function registerClass(ReflectionClass $reflected_class): void
foreach ($class_constants as $name => $value) {
$storage->constants[$name] = new ClassConstantStorage(
ClassLikeAnalyzer::getTypeFromValue($value),
ClassLikeAnalyzer::getLiteralTypeFromValue($value),
ClassLikeAnalyzer::VISIBILITY_PUBLIC,
null
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,7 @@ private function visitClassConstDeclaration(

$storage->constants[$const->name->name] = $constant_storage = new ClassConstantStorage(
$const_type,
$inferred_type,
$stmt->isProtected()
? ClassLikeAnalyzer::VISIBILITY_PROTECTED
: ($stmt->isPrivate()
Expand All @@ -1301,7 +1302,6 @@ private function visitClassConstDeclaration(
)
);

$constant_storage->inferred_type = $inferred_type;
$constant_storage->type_location = $type_location;

$constant_storage->stmt_location = new CodeLocation(
Expand Down
3 changes: 2 additions & 1 deletion src/Psalm/Storage/ClassConstantStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,11 @@ class ClassConstantStorage
/**
* @param ClassLikeAnalyzer::VISIBILITY_* $visibility
*/
public function __construct(?Union $type, int $visibility, ?CodeLocation $location)
public function __construct(?Union $type, ?Union $inferred_type, int $visibility, ?CodeLocation $location)
{
$this->visibility = $visibility;
$this->location = $location;
$this->type = $type;
$this->inferred_type = $inferred_type;
}
}
50 changes: 32 additions & 18 deletions tests/ConstantTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,38 @@ class ConstantTest extends TestCase
use InvalidCodeAnalysisTestTrait;
use ValidCodeAnalysisTestTrait;

// TODO: Waiting for https://github.com/vimeo/psalm/issues/7125
// public function testKeyofSelfConstDoesntImplyKeyofStaticConst(): void
// {
// $this->expectException(CodeException::class);
// $this->expectExceptionMessage("PossiblyUndefinedIntArrayOffset");

// $this->testConfig->ensure_array_int_offsets_exist = true;

// $file_path = getcwd() . '/src/somefile.php';

// $this->addFile(
// $file_path,
// '<?php
// class Foo
// {
// /** @var array<int, int> */
// public const CONST = [1, 2, 3];

// /**
// * @param key-of<self::CONST> $key
// */
// public function bar(int $key): int
// {
// return static::CONST[$key];
// }
// }
// '
// );

// $this->analyzeFile($file_path, new Context());
// }

/**
* @return iterable<string,array{code:string,assertions?:array<string,string>,ignored_issues?:list<string>, php_version?: string}>
*/
Expand Down Expand Up @@ -1610,24 +1642,6 @@ public function bar(int $key): string
',
'error_message' => 'UnresolvableConstant',
],
'SKIPPED-keyofSelfConstDoesntImplyKeyofStaticConst' => [
'<?php
class Foo
{
/** @var array<int, int> */
public const CONST = [1, 2, 3];
/**
* @param key-of<self::CONST> $key
*/
public function bar(int $key): int
{
return static::CONST[$key];
}
}
',
'error_message' => 'MixedArrayAccess',
],
];
}
}

0 comments on commit c877ce0

Please sign in to comment.