Skip to content
Permalink
Browse files

Add detection for callable variable use

  • Loading branch information...
muglug committed Apr 24, 2019
1 parent c657a45 commit 3681762a9b972bc28ba62ba456c946e0923c63b0
@@ -2157,7 +2157,8 @@ public static function checkFunctionArgumentType(
foreach ($input_type->getTypes() as $input_type_part) {
if ($input_type_part instanceof Type\Atomic\ObjectLike) {
$potential_method_id = TypeAnalyzer::getCallableMethodIdFromObjectLike(
$input_type_part
$input_type_part,
$codebase
);
if ($potential_method_id) {
@@ -1141,7 +1141,7 @@ public static function isAtomicContainedBy(
return false;
}
} elseif ($input_type_part instanceof ObjectLike) {
$method_id = self::getCallableMethodIdFromObjectLike($input_type_part);
$method_id = self::getCallableMethodIdFromObjectLike($input_type_part, $codebase);
if ($method_id === 'not-callable') {
return false;
@@ -1342,7 +1342,7 @@ public static function getCallableFromAtomic(Codebase $codebase, Type\Atomic $in
}
}
} elseif ($input_type_part instanceof ObjectLike) {
if ($method_id = self::getCallableMethodIdFromObjectLike($input_type_part)) {
if ($method_id = self::getCallableMethodIdFromObjectLike($input_type_part, $codebase)) {
try {
$method_storage = $codebase->methods->getStorage($method_id);
@@ -1361,28 +1361,36 @@ public static function getCallableFromAtomic(Codebase $codebase, Type\Atomic $in
}
/** @return ?string */
public static function getCallableMethodIdFromObjectLike(ObjectLike $input_type_part)
public static function getCallableMethodIdFromObjectLike(ObjectLike $input_type_part, Codebase $codebase)
{
if (!isset($input_type_part->properties[0])
|| !isset($input_type_part->properties[1])
) {
return 'not-callable';
}
if ($input_type_part->properties[1]->hasMixed() || $input_type_part->properties[1]->hasScalar()) {
return null;
}
$lhs = $input_type_part->properties[0];
$rhs = $input_type_part->properties[1];
if (!$input_type_part->properties[1]->hasString()) {
return 'not-callable';
}
if ($rhs->hasMixed()
|| $rhs->hasScalar()
|| !$rhs->isSingleStringLiteral()
) {
if (!$rhs->hasString()) {
return 'not-callable';
}
foreach ($lhs->getTypes() as $lhs_atomic_type) {
if ($lhs_atomic_type instanceof TNamedObject) {
$codebase->analyzer->addMixedMemberName(strtolower($lhs_atomic_type->value) . '::');
}
}
if (!$input_type_part->properties[1]->isSingleStringLiteral()) {
return null;
}
$lhs = $input_type_part->properties[0];
$method_name = $input_type_part->properties[1]->getSingleStringLiteral()->value;
$method_name = $rhs->getSingleStringLiteral()->value;
$class_name = null;
@@ -1397,6 +1405,8 @@ public static function getCallableMethodIdFromObjectLike(ObjectLike $input_type_
}
if (!$class_name) {
$codebase->analyzer->addMixedMemberName(strtolower($method_name));
return null;
}
@@ -11,14 +11,50 @@
ini_set('display_startup_errors', '1');
ini_set('memory_limit', '4096M');
gc_collect_cycles();
gc_disable();
$args = array_slice($argv, 1);
$valid_short_options = ['f:', 'm', 'h', 'r:'];
$valid_long_options = [
'help', 'debug', 'debug-by-line', 'config:', 'file:', 'root:',
'plugin:', 'issues:', 'php-version:', 'dry-run', 'safe-types',
'find-unused-code', 'threads:',
];
// get options from command line
$options = getopt(
'f:mhr:',
[
'help', 'debug', 'debug-by-line', 'config:', 'file:', 'root:',
'plugin:', 'issues:', 'php-version:', 'dry-run', 'safe-types',
'find-unused-code', 'threads:',
]
$options = getopt(implode('', $valid_short_options), $valid_long_options);
array_map(
/**
* @param string $arg
*
* @return void
*/
function ($arg) use ($valid_long_options, $valid_short_options) {
if (substr($arg, 0, 2) === '--' && $arg !== '--') {
$arg_name = preg_replace('/=.*$/', '', substr($arg, 2));
if (!in_array($arg_name, $valid_long_options)
&& !in_array($arg_name . ':', $valid_long_options)
&& !in_array($arg_name . '::', $valid_long_options)
) {
echo 'Unrecognised argument "--' . $arg_name . '"' . PHP_EOL
. 'Type --help to see a list of supported arguments'. PHP_EOL;
exit(1);
}
} elseif (substr($arg, 0, 2) === '-' && $arg !== '-' && $arg !== '--') {
$arg_name = preg_replace('/=.*$/', '', substr($arg, 1));
if (!in_array($arg_name, $valid_short_options) && !in_array($arg_name . ':', $valid_short_options)) {
echo 'Unrecognised argument "-' . $arg_name . '"' . PHP_EOL
. 'Type --help to see a list of supported arguments'. PHP_EOL;
exit(1);
}
}
},
$args
);
if (array_key_exists('help', $options)) {
@@ -73,7 +109,7 @@
--issues=IssueType1,IssueType2
If any issues can be fixed automatically, Psalm will update the codebase
--find-dead-code
--find-unused-code
Include unused code as a candidate for removal
--threads=INT
@@ -1771,6 +1771,62 @@ function foo(A $a, string $var) {
['PossiblyUnusedMethod'],
true,
],
'dontRemovePossiblyUnusedMethodWithVariableCallableCall' => [
'<?php
class A {
public function foo() : void {}
}
function takeCallable(callable $c) : void {}
function foo(A $a, string $var) {
takeCallable([$a, $var]);
}',
'<?php
class A {
public function foo() : void {}
}
function takeCallable(callable $c) : void {}
function foo(A $a, string $var) {
takeCallable([$a, $var]);
}',
'7.1',
['PossiblyUnusedMethod'],
true,
],
'dontRemovePossiblyUnusedMethodWithVariableCallableLhsCall' => [
'<?php
class A {
public function foo() : void {}
public function bar() : void {}
}
function takeCallable(callable $c) : void {}
function foo($a) {
takeCallable([$a, "foo"]);
}
foo(new A);',
'<?php
class A {
public function foo() : void {}
}
function takeCallable(callable $c) : void {}
function foo($a) {
takeCallable([$a, "foo"]);
}
foo(new A);',
'7.1',
['PossiblyUnusedMethod'],
true,
],
'dontRemovePossiblyUnusedMethodWithVariableCallOnParent' => [
'<?php
class A { }

0 comments on commit 3681762

Please sign in to comment.
You can’t perform that action at this time.