Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor dictionaries #11169

Open
wants to merge 62 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
06ae404
Add support for PHP 8.4
GromNaN Apr 29, 2024
f1e9ba2
E_STRICT deprecated in PHP 8.4
GromNaN Oct 3, 2024
8ef89ff
Fix getReflectionFunction
GromNaN Oct 9, 2024
8900850
Merge branch 'master' into php84
danog Nov 24, 2024
01924c5
Update InternalCallMapHandlerTest.php
danog Nov 24, 2024
5343494
Update psalm-baseline.xml
danog Nov 24, 2024
7c600dd
Update ci.yml
danog Nov 24, 2024
95d5f6f
Update dictionaries for 8.1
danog Nov 25, 2024
ad9b64e
Fix
danog Nov 26, 2024
9af1744
Fixes
danog Nov 26, 2024
1bcef70
Merge remote-tracking branch 'g/php84' into update_dictionaries
danog Nov 26, 2024
66ae90a
bump
danog Nov 26, 2024
ebab021
Regen
danog Nov 26, 2024
192b17f
Add
danog Nov 26, 2024
7abef43
Bump
danog Nov 26, 2024
e2d1e3a
cleanup
danog Nov 26, 2024
ac94583
Add callmap generator
danog Nov 26, 2024
58bdb95
fixes
danog Nov 26, 2024
744844f
Bump
danog Nov 27, 2024
a05ac99
fix
danog Nov 27, 2024
12e5f79
bump
danog Nov 30, 2024
99ff70c
Finalize
danog Nov 30, 2024
8b3a70a
Merge remote-tracking branch 'origin/master' into update_dictionaries
danog Nov 30, 2024
41507e1
bump
danog Nov 30, 2024
68529b5
Merge remote-tracking branch 'origin/master' into update_dictionaries
danog Nov 30, 2024
972f75b
Fix
danog Nov 30, 2024
64f4efc
Pre-lowercase callmaps
danog Dec 1, 2024
1d45aa8
Cleanup
danog Dec 1, 2024
bb092f7
Sort during normalization
danog Dec 1, 2024
8cfb3c1
Autogenerate base callmaps
danog Dec 1, 2024
6b9f365
Refactor
danog Dec 1, 2024
7c5a2ba
bump
danog Dec 1, 2024
a0b5325
bump
danog Dec 1, 2024
2f0ebf5
Cleanup
danog Jan 15, 2025
30651ec
Refactoring
danog Jan 17, 2025
50a13a7
Refactoring
danog Jan 17, 2025
e334b4c
Refactoring
danog Jan 17, 2025
f907a96
Get rid of diffs
danog Jan 17, 2025
8df5013
cleanup
danog Jan 17, 2025
2387f0e
cleanup
danog Jan 17, 2025
9471a59
cleanup
danog Jan 17, 2025
3a59eea
Add remaining versions
danog Jan 17, 2025
cb3b690
Cleanup
danog Jan 17, 2025
6257d39
Cleanup
danog Jan 17, 2025
bd26c03
Cleanup
danog Jan 17, 2025
4752177
Cleanup dictionaries
danog Jan 18, 2025
690f541
CLeanup
danog Jan 18, 2025
600e059
Fixes
danog Jan 18, 2025
ca3949e
Fixes
danog Jan 18, 2025
ca0e233
Fixes
danog Jan 18, 2025
a8beb6c
Fixes
danog Jan 18, 2025
e9e270f
fixes
danog Jan 18, 2025
0d0aedc
cs-fix
danog Jan 18, 2025
1154dcc
Cleanup
danog Jan 18, 2025
0c4ad45
cs-fix
danog Jan 18, 2025
826b0d2
cs-fix
danog Jan 18, 2025
10ef3b6
Fix
danog Jan 18, 2025
d655c39
cs-fix
danog Jan 18, 2025
fe2270d
Restore previous format
danog Jan 18, 2025
27bd178
Fix
danog Jan 18, 2025
d53a45e
Temporarily revert
danog Jan 18, 2025
6cdff1c
cs-fix
danog Jan 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ jobs:
- "8.1"
- "8.2"
- "8.3"
- "8.4"
count: ${{ fromJson(needs.chunk-matrix.outputs.count) }}
chunk: ${{ fromJson(needs.chunk-matrix.outputs.chunks) }}

Expand All @@ -148,7 +149,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: "${{ matrix.php-version }}"
ini-values: zend.assertions=1, assert.exception=1, opcache.enable_cli=1, opcache.jit=function, opcache.jit_buffer_size=512M
ini-values: zend.assertions=1, assert.exception=1
tools: composer:v2
coverage: none
extensions: none, curl, dom, filter, intl, json, libxml, mbstring, opcache, openssl, pcre, phar, reflection, simplexml, spl, tokenizer, xml, xmlwriter
Expand Down
192 changes: 192 additions & 0 deletions bin/gen_callmap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
<?php

declare(strict_types=1);

// Written by SamMousa in /~https://github.com/vimeo/psalm/issues/8101, finalized by @danog

require 'vendor/autoload.php';

use DG\BypassFinals;
use Psalm\Internal\Analyzer\ProjectAnalyzer;
use Psalm\Internal\Codebase\Reflection;
use Psalm\Internal\Provider\FileProvider;
use Psalm\Internal\Provider\Providers;
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
use Psalm\Tests\TestConfig;
use Psalm\Type;

/**
* Returns the correct reflection type for function or method name.
*/
function getReflectionFunction(string $functionName): ?ReflectionFunctionAbstract
{
try {
if (strpos($functionName, '::') !== false) {
return new ReflectionMethod($functionName);
}

/** @var callable-string $functionName */
return new ReflectionFunction($functionName);
} catch (ReflectionException $e) {
return null;
}
}

/**
* @param array<string, string> $entryParameters
*/
function assertEntryParameters(ReflectionFunctionAbstract $function, array &$entryParameters): void
{
assertEntryReturnType($function, $entryParameters[0]);
/**
* Parse the parameter names from the map.
*
* @var array<string, array{byRef: bool, refMode: 'rw'|'w'|'r', variadic: bool, optional: bool, type: string}>
*/
$normalizedEntries = [];

foreach ($entryParameters as $key => &$entry) {
if ($key === 0) {
continue;
}
$normalizedKey = $key;
/**
* @var array{byRef: bool, refMode: 'rw'|'w'|'r', variadic: bool, optional: bool, type: string} $normalizedEntry
*/
$normalizedEntry = [
'variadic' => false,
'byRef' => false,
'optional' => false,
'type' => &$entry,
];
if (strncmp($normalizedKey, '&', 1) === 0) {
$normalizedEntry['byRef'] = true;
$normalizedKey = substr($normalizedKey, 1);
}

if (strncmp($normalizedKey, '...', 3) === 0) {
$normalizedEntry['variadic'] = true;
$normalizedKey = substr($normalizedKey, 3);
}

// Read the reference mode
if ($normalizedEntry['byRef']) {
$parts = explode('_', $normalizedKey, 2);
if (count($parts) === 2) {
if (!($parts[0] === 'rw' || $parts[0] === 'w' || $parts[0] === 'r')) {
throw new InvalidArgumentException('Invalid refMode: '.$parts[0]);
}
$normalizedEntry['refMode'] = $parts[0];
$normalizedKey = $parts[1];
} else {
$normalizedEntry['refMode'] = 'rw';
}
}

// Strip prefixes.
if (substr($normalizedKey, -1, 1) === "=") {
$normalizedEntry['optional'] = true;
$normalizedKey = substr($normalizedKey, 0, -1);
}

$normalizedEntry['name'] = $normalizedKey;
$normalizedEntries[$normalizedKey] = $normalizedEntry;
}

foreach ($function->getParameters() as $parameter) {
if (isset($normalizedEntries[$parameter->getName()])) {
assertParameter($normalizedEntries[$parameter->getName()], $parameter);
}
}
}

/**
* @param array{byRef: bool, name?: string, refMode: 'rw'|'w'|'r', variadic: bool, optional: bool, type: string} $normalizedEntry
*/
function assertParameter(array &$normalizedEntry, ReflectionParameter $param): void
{
$name = $param->getName();

$expectedType = $param->getType();

if (isset($expectedType) && !empty($normalizedEntry['type'])) {
assertTypeValidity($expectedType, $normalizedEntry['type'], "Param '{$name}'");
}
}

function assertEntryReturnType(ReflectionFunctionAbstract $function, string &$entryReturnType): void
{
if (version_compare(PHP_VERSION, '8.1.0', '>=')) {
$expectedType = $function->hasTentativeReturnType() ? $function->getTentativeReturnType() : $function->getReturnType();
} else {
$expectedType = $function->getReturnType();
}

if ($expectedType !== null) {
assertTypeValidity($expectedType, $entryReturnType, 'Return');
}
}

/**
* Since string equality is too strict, we do some extra checking here
*/
function assertTypeValidity(ReflectionType $reflected, string &$specified, string $msgPrefix): void
{
$expectedType = Reflection::getPsalmTypeFromReflectionType($reflected);
$callMapType = Type::parseString($specified === '' ? 'mixed' : $specified);

$codebase = ProjectAnalyzer::getInstance()->getCodebase();
try {
if (!UnionTypeComparator::isContainedBy($codebase, $callMapType, $expectedType, false, false, null, false, false) && !str_contains($specified, 'static')) {
$specified = $expectedType->getId(true);
}
} catch (Throwable) {
}

// Reflection::getPsalmTypeFromReflectionType adds |null to mixed types so skip comparison
/*if (!$expectedType->hasMixed()) {
$this->assertSame($expectedType->isNullable(), $callMapType->isNullable(), "{$msgPrefix} type '{$specified}' missing null from reflected type '{$reflected}'");
//$this->assertSame($expectedType->hasBool(), $callMapType->hasBool(), "{$msgPrefix} type '{$specified}' missing bool from reflected type '{$reflected}'");
$this->assertSame($expectedType->hasArray(), $callMapType->hasArray(), "{$msgPrefix} type '{$specified}' missing array from reflected type '{$reflected}'");
$this->assertSame($expectedType->hasInt(), $callMapType->hasInt(), "{$msgPrefix} type '{$specified}' missing int from reflected type '{$reflected}'");
$this->assertSame($expectedType->hasFloat(), $callMapType->hasFloat(), "{$msgPrefix} type '{$specified}' missing float from reflected type '{$reflected}'");
}*/
}

BypassFinals::enable();

function writeCallMap(string $file, array $callMap) {
file_put_contents($file, '<?php // phpcs:ignoreFile
namespace Phan\Language\Internal;

return '.var_export($callMap, true).';');
}

new ProjectAnalyzer(new TestConfig, new Providers(new FileProvider));
$callMap = require "dictionaries/CallMap.php";
$orig = $callMap;

$codebase = ProjectAnalyzer::getInstance()->getCodebase();

foreach ($callMap as $functionName => &$entry) {
$refl = getReflectionFunction($functionName);
if (!$refl) {
continue;
}
assertEntryParameters($refl, $entry);
} unset($entry);

writeCallMap("dictionaries/CallMap.php", $callMap);

$diffFile = "dictionaries/CallMap_84_delta.php";

$diff = require $diffFile;

foreach ($callMap as $functionName => $entry) {
if ($orig[$functionName] !== $entry) {
$diff['changed'][$functionName]['old'] = $orig[$functionName];
$diff['changed'][$functionName]['new'] = $entry;
}
}

writeCallMap($diffFile, $diff);
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"composer/xdebug-handler": "^2.0 || ^3.0",
"dnoegel/php-xdg-base-dir": "^0.1.1",
"felixfbecker/advanced-json-rpc": "^3.1",
"felixfbecker/language-server-protocol": "^1.5.2",
"felixfbecker/language-server-protocol": "^1.5.3",
"fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0",
"netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
"nikic/php-parser": "^5.0.0",
Expand Down
Loading
Loading