Skip to content

Commit

Permalink
Suggest functions from newer target_php_version
Browse files Browse the repository at this point in the history
Fixes phan#4230
  • Loading branch information
TysonAndre committed Nov 29, 2020
1 parent edc8fe4 commit 268272a
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 1 deletion.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ New features (Analysis):
+ Update real parameter names to match php 8.0's parameter names for php's own internal methods (including variadics and those with multiple signatures). (#4263)
+ Raise the severity of some php 8.0 incompatibility issues to critical.
+ Fix handling of references after renaming variadic reference parameters of `fscanf`/`scanf`/`mb_convert_variables`
+ Mention if PhanUndeclaredFunction is potentially caused by the target php version being too old. (#4230)

Nov 27 2020, Phan 3.2.6
-----------------------
Expand Down
47 changes: 47 additions & 0 deletions src/Phan/CodeBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -2107,6 +2107,53 @@ public function suggestSimilarGlobalFunctionInOtherNamespace(
}, $namespaces_for_function);
}

private static function phpVersionIdToString(int $php_version_id): string
{
return \sprintf('%d.%d', $php_version_id / 10000, ($php_version_id / 100) % 100);
}

/**
* @unused-param $context
* @return list<string> 0 or more namespaced function names found in this code base for newer php versions
*/
public function suggestSimilarGlobalFunctionInNewerVersion(
string $namespace,
string $function_name,
Context $context,
bool $suggest_in_global_namespace
): array {
if (!$suggest_in_global_namespace && $namespace !== '\\') {
return [];
}
$target_php_version_config = Config::get_closest_target_php_version_id();
$target_php_version = (int)\floor(\min($target_php_version_config, \PHP_VERSION_ID) / 100) * 100;
$targets = [50600, 70000, 70100, 70200, 70300, 70400, 80000];
$function_name_lower = strtolower($function_name);
foreach ($targets as $i => $target) {
// If $target_php_version is 7.1 only check for functions added in 7.2 or newer that weren't in the previous version.
// Don't suggest functions added in 7.1
$next_target = $targets[$i + 1] ?? 0;
if (!$next_target || $next_target <= $target_php_version) {
continue;
}
$signature_map = UnionType::internalFunctionSignatureMap($next_target);
if (isset($signature_map[$function_name_lower])) {
$old_signature_map = UnionType::internalFunctionSignatureMap($target);
if (!isset($old_signature_map[$function_name_lower])) {
$details = \sprintf('target_php_version=%s run with PHP %s', self::phpVersionIdToString($target_php_version_config), self::phpVersionIdToString(\PHP_VERSION_ID));
$suggestion = \sprintf(
'(to use the function %s() added in PHP %s in a project with %s without a polyfill parsed by Phan)',
$function_name,
self::phpVersionIdToString($next_target),
$details
);
return [$suggestion];
}
}
}
return [];
}

/**
* @internal
*/
Expand Down
6 changes: 5 additions & 1 deletion src/Phan/IssueFixSuggester.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ public static function suggestSimilarGlobalFunction(
$suggested_fqsens = \array_merge(
$code_base->suggestSimilarGlobalFunctionInOtherNamespace($namespace, $name, $context),
$code_base->suggestSimilarGlobalFunctionInSameNamespace($namespace, $name, $context, $suggest_in_global_namespace),
$code_base->suggestSimilarNewInAnyNamespace($namespace, $name, $context, $suggest_in_global_namespace)
$code_base->suggestSimilarNewInAnyNamespace($namespace, $name, $context, $suggest_in_global_namespace),
$code_base->suggestSimilarGlobalFunctionInNewerVersion($namespace, $name, $context, $suggest_in_global_namespace)
);
if (count($suggested_fqsens) === 0) {
return null;
Expand All @@ -133,6 +134,9 @@ public static function suggestSimilarGlobalFunction(
if ($fqsen instanceof FullyQualifiedClassName) {
return "new $fqsen()";
}
if (is_string($fqsen) && strpos($fqsen, 'added in PHP') !== false) {
return $fqsen;
}
return $fqsen . '()';
};
$suggestion_text = $prefix . ' ' . \implode(' or ', \array_map($generate_type_representation, $suggested_fqsens));
Expand Down
2 changes: 2 additions & 0 deletions tests/files/expected/0914_newer_function.php.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
%s:6 PhanUndeclaredFunction Call to undeclared function \str_contains() (Did you mean (to use the function str_contains() added in PHP 8.0 in a project with target_php_version=7.1 run with PHP 7.%s without a polyfill parsed by Phan))
%s:15 PhanUndeclaredFunction Call to undeclared function \str_contains() (Did you mean (to use the function str_contains() added in PHP 8.0 in a project with target_php_version=7.1 run with PHP 7.%s without a polyfill parsed by Phan))
Empty file.
19 changes: 19 additions & 0 deletions tests/files/src/0914_newer_function.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
// When this test case is run
namespace {

function contains_x(string $x) {
return str_contains($x, 'x');
}
var_export(contains_x('xyz'));

}

namespace NS914 {

function contains_x(string $x) {
return str_contains($x, 'x');
}
var_export(contains_x('xyz'));

}

0 comments on commit 268272a

Please sign in to comment.