Skip to content

Commit

Permalink
Detect global user functions (e.g. polyfills) (#210)
Browse files Browse the repository at this point in the history
  • Loading branch information
janedbal authored Dec 30, 2024
1 parent 6d1ad56 commit 160c1c6
Show file tree
Hide file tree
Showing 7 changed files with 32 additions and 23 deletions.
8 changes: 2 additions & 6 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,6 @@ jobs:
repo: mimmi20/ua-device-type
-
repo: mimmi20/ua-normalizer
-
repo: numero2/contao-marketing-suite
cdaArgs: --disable-ext-analysis --config=depcheck.php
-
repo: numero2/contao-opengraph3
cdaArgs: --config=depcheck.php
Expand All @@ -176,6 +173,7 @@ jobs:
-
repo: phpstan/phpstan-src
cdaArgs: --disable-ext-analysis --config=build/composer-dependency-analyser.php
php: 8.4
-
repo: qossmic/deptrac-src
-
Expand Down Expand Up @@ -208,8 +206,6 @@ jobs:
repo: shipmonk-rnd/phpstan-rules
-
repo: symplify/config-transformer
-
repo: teamneusta/pimcore-testing-framework
-
repo: wallabag/wallabag
cdaArgs: --disable-ext-analysis
Expand All @@ -236,7 +232,7 @@ jobs:
name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.3
php-version: ${{ matrix.php || '8.3' }}
ini-file: development

-
Expand Down
3 changes: 3 additions & 0 deletions scripts/refresh-e2e.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ function outputYaml(array $items): void
$result[] = [
'repo' => 'phpstan/phpstan-src',
'cdaArgs' => '--config=build/composer-dependency-analyser.php',
'php' => '8.4',
];
$result[] = [
'repo' => 'qossmic/deptrac-src',
Expand Down Expand Up @@ -109,6 +110,8 @@ function outputYaml(array $items): void
|| $item['repo'] === 'symplify/phpstan-rules'
|| $item['repo'] === 'contao-thememanager/core'
|| $item['repo'] === 'oveleon/contao-recommendation-bundle'
|| $item['repo'] === 'numero2/contao-marketing-suite' // since 1.8.2 (shadow symfony/contracts via trigger_deprecation)
|| $item['repo'] === 'teamneusta/pimcore-testing-framework' // since 1.8.2 (shadow symfony/contracts via trigger_deprecation)
) {
unset($result[$index]); // failing builds
}
Expand Down
11 changes: 6 additions & 5 deletions src/Analyser.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class Analyser
*
* @var array<string, SymbolKind::*>
*/
private $extensionSymbolKinds = [];
private $knownSymbolKinds = [];

/**
* @param array<string, ClassLoader> $classLoaders vendorDir => ClassLoader (e.g. result of \Composer\Autoload\ClassLoader::getRegisteredLoaders())
Expand Down Expand Up @@ -392,7 +392,7 @@ private function getUsedSymbolsInFile(string $filePath): array
}

return (new UsedSymbolExtractor($code))->parseUsedSymbols(
$this->extensionSymbolKinds
$this->knownSymbolKinds
);
}

Expand Down Expand Up @@ -553,7 +553,7 @@ private function initExistingSymbols(Configuration $config): void
$this->ignoredSymbols[$constantName] = true;
} else {
$this->extensionSymbols[SymbolKind::CONSTANT][$constantName] = $extensionName;
$this->extensionSymbolKinds[strtolower($constantName)] = SymbolKind::CONSTANT;
$this->knownSymbolKinds[strtolower($constantName)] = SymbolKind::CONSTANT;
}
}
}
Expand All @@ -567,6 +567,7 @@ private function initExistingSymbols(Configuration $config): void
if ($reflectionFunction->getExtension() === null) {
if (is_string($functionFilePath)) {
$this->definedFunctions[$functionName] = Path::normalize($functionFilePath);
$this->knownSymbolKinds[$functionName] = SymbolKind::FUNCTION;
}
} else {
$extensionName = $this->getNormalizedExtensionName($reflectionFunction->getExtension()->name);
Expand All @@ -575,7 +576,7 @@ private function initExistingSymbols(Configuration $config): void
$this->ignoredSymbols[$functionName] = true;
} else {
$this->extensionSymbols[SymbolKind::FUNCTION][$functionName] = $extensionName;
$this->extensionSymbolKinds[$functionName] = SymbolKind::FUNCTION;
$this->knownSymbolKinds[$functionName] = SymbolKind::FUNCTION;
}
}
}
Expand All @@ -598,7 +599,7 @@ private function initExistingSymbols(Configuration $config): void
$this->ignoredSymbols[$classLikeName] = true;
} else {
$this->extensionSymbols[SymbolKind::CLASSLIKE][$classLikeName] = $extensionName;
$this->extensionSymbolKinds[strtolower($classLikeName)] = SymbolKind::CLASSLIKE;
$this->knownSymbolKinds[strtolower($classLikeName)] = SymbolKind::CLASSLIKE;
}
}
}
Expand Down
18 changes: 9 additions & 9 deletions src/UsedSymbolExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ public function __construct(string $code)
* It does not produce any local names in current namespace
* - this results in very limited functionality in files without namespace
*
* @param array<string, SymbolKind::*> $extensionSymbols
* @param array<string, SymbolKind::*> $knownSymbols
* @return array<SymbolKind::*, array<string, list<int>>>
* @license Inspired by https://github.com/doctrine/annotations/blob/2.0.0/lib/Doctrine/Common/Annotations/TokenParser.php
*/
public function parseUsedSymbols(
array $extensionSymbols
array $knownSymbols
): array
{
$usedSymbols = [];
Expand Down Expand Up @@ -124,7 +124,7 @@ public function parseUsedSymbols(
case PHP_VERSION_ID >= 80000 ? T_NAME_FULLY_QUALIFIED : -1:
$symbolName = $this->normalizeBackslash($token[1]);
$lowerSymbolName = strtolower($symbolName);
$kind = $extensionSymbols[$lowerSymbolName] ?? $this->getFqnSymbolKind($this->pointer - 2, $this->pointer, $inAttributeSquareLevel !== null);
$kind = $knownSymbols[$lowerSymbolName] ?? $this->getFqnSymbolKind($this->pointer - 2, $this->pointer, $inAttributeSquareLevel !== null);
$usedSymbols[$kind][$symbolName][] = $token[2];
break;

Expand All @@ -140,7 +140,7 @@ public function parseUsedSymbols(
}

$lowerSymbolName = strtolower($symbolName);
$kind = $extensionSymbols[$lowerSymbolName] ?? $this->getFqnSymbolKind($this->pointer - 2, $this->pointer, $inAttributeSquareLevel !== null);
$kind = $knownSymbols[$lowerSymbolName] ?? $this->getFqnSymbolKind($this->pointer - 2, $this->pointer, $inAttributeSquareLevel !== null);
$usedSymbols[$kind][$symbolName][] = $token[2];

break;
Expand All @@ -160,9 +160,9 @@ public function parseUsedSymbols(
$kind = $useStatementKinds[$name];
$usedSymbols[$kind][$symbolName][] = $token[2];

} elseif (isset($extensionSymbols[$lowerName])) {
} elseif (isset($knownSymbols[$lowerName])) {
$symbolName = $name;
$kind = $extensionSymbols[$lowerName];
$kind = $knownSymbols[$lowerName];

if (!$inGlobalScope && $kind === SymbolKind::CLASSLIKE) {
break; // cannot use class-like symbols in non-global scope when not imported
Expand Down Expand Up @@ -192,7 +192,7 @@ public function parseUsedSymbols(
$lowerSymbolName = strtolower($symbolName);

if ($symbolName !== '') { // e.g. \array (NS separator followed by not-a-name)
$kind = $extensionSymbols[$lowerSymbolName] ?? $this->getFqnSymbolKind($pointerBeforeName, $this->pointer - 1, false);
$kind = $knownSymbols[$lowerSymbolName] ?? $this->getFqnSymbolKind($pointerBeforeName, $this->pointer - 1, false);
$usedSymbols[$kind][$symbolName][] = $token[2];
}

Expand All @@ -213,9 +213,9 @@ public function parseUsedSymbols(
$kind = $useStatementKinds[$name];
$usedSymbols[$kind][$symbolName][] = $token[2];

} elseif (isset($extensionSymbols[$lowerName])) {
} elseif (isset($knownSymbols[$lowerName])) {
$symbolName = $name;
$kind = $extensionSymbols[$lowerName];
$kind = $knownSymbols[$lowerName];

if (!$inGlobalScope && $kind === SymbolKind::CLASSLIKE) {
break; // cannot use class-like symbols in non-global scope when not imported
Expand Down
11 changes: 10 additions & 1 deletion tests/UsedSymbolExtractorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ public function provideVariants(): iterable
'PHPUnit\Framework\Error' => [14],
'LogicException' => [15, 20],
],
SymbolKind::FUNCTION => [
'user_defined_function' => [38],
],
],
[
'user_defined_function' => SymbolKind::FUNCTION,
],
];

Expand Down Expand Up @@ -113,7 +119,6 @@ public function provideVariants(): iterable
];

yield 'global namespace' => [

__DIR__ . '/data/not-autoloaded/used-symbols/global-namespace.php',
[
SymbolKind::CLASSLIKE => [
Expand All @@ -122,8 +127,12 @@ public function provideVariants(): iterable
],
SymbolKind::FUNCTION => [
'PHPUnit\Framework\assertSame' => [7],
'user_defined_function' => [12],
],
],
[
strtolower('user_defined_function') => SymbolKind::FUNCTION,
],
];

yield 'curly braces' => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
class Foo {
public function someFunction(string $foo): void
{

user_defined_function();
}
}
2 changes: 1 addition & 1 deletion tests/data/not-autoloaded/used-symbols/various-usages.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Foo {

public function foo(Error $error): void // not PHPUnit\Framework\Error anymore
{

user_defined_function();
}

}

0 comments on commit 160c1c6

Please sign in to comment.