2018-06-17 02:01:33 +02:00
< ? php
2021-12-15 04:58:32 +01:00
2018-06-17 02:01:33 +02:00
namespace Psalm\Tests ;
use Psalm\Config ;
use Psalm\Context ;
2021-12-03 20:29:06 +01:00
use Psalm\Exception\CodeException ;
2021-12-03 20:11:20 +01:00
use Psalm\Internal\Analyzer\ProjectAnalyzer ;
2021-07-02 01:10:21 +02:00
use Psalm\Internal\Provider\FakeFileProvider ;
2021-12-03 20:11:20 +01:00
use Psalm\Internal\Provider\Providers ;
2020-08-23 16:32:07 +02:00
use Psalm\Internal\RuntimeCaches ;
2021-12-04 21:55:53 +01:00
use Psalm\Tests\Internal\Provider\FakeParserCacheProvider ;
2021-06-08 04:55:21 +02:00
use function preg_quote ;
2019-06-26 22:52:29 +02:00
use function strpos ;
2021-06-08 04:55:21 +02:00
2021-03-17 06:10:42 +01:00
use const DIRECTORY_SEPARATOR ;
2018-06-17 02:01:33 +02:00
class UnusedVariableTest extends TestCase
{
2021-12-04 03:37:19 +01:00
/** @var ProjectAnalyzer */
2018-11-11 18:01:14 +01:00
protected $project_analyzer ;
2018-06-17 02:01:33 +02:00
2021-12-05 18:51:26 +01:00
public function setUp () : void
2018-06-17 02:01:33 +02:00
{
2020-08-23 16:32:07 +02:00
RuntimeCaches :: clearAll ();
2018-06-17 02:01:33 +02:00
2021-07-02 01:10:21 +02:00
$this -> file_provider = new FakeFileProvider ();
2018-06-17 02:01:33 +02:00
2021-12-03 20:11:20 +01:00
$this -> project_analyzer = new ProjectAnalyzer (
2018-06-17 02:01:33 +02:00
new TestConfig (),
2021-12-03 20:11:20 +01:00
new Providers (
2018-09-28 22:18:45 +02:00
$this -> file_provider ,
2021-12-04 21:55:53 +01:00
new FakeParserCacheProvider ()
2018-09-28 22:18:45 +02:00
)
2018-06-17 02:01:33 +02:00
);
2021-11-27 01:06:33 +01:00
$this -> project_analyzer -> setPhpVersion ( '7.4' , 'tests' );
2019-03-05 21:45:09 +01:00
$this -> project_analyzer -> getCodebase () -> reportUnusedVariables ();
2018-06-17 02:01:33 +02:00
}
/**
2018-11-06 03:57:36 +01:00
* @ dataProvider providerValidCodeParse
2018-06-17 02:01:33 +02:00
*
* @ param string $code
* @ param array < string > $error_levels
*
*/
2020-09-12 17:24:05 +02:00
public function testValidCode ( $code , array $error_levels = []) : void
2018-06-17 02:01:33 +02:00
{
2018-07-13 23:44:50 +02:00
$test_name = $this -> getTestName ();
2019-02-07 18:25:57 +01:00
if ( strpos ( $test_name , 'SKIPPED-' ) !== false ) {
2018-06-17 02:01:33 +02:00
$this -> markTestSkipped ( 'Skipped due to a bug.' );
}
$file_path = self :: $src_dir_path . 'somefile.php' ;
$this -> addFile (
$file_path ,
$code
);
foreach ( $error_levels as $error_level ) {
2018-11-11 18:01:14 +01:00
$this -> project_analyzer -> getCodebase () -> config -> setCustomErrorLevel ( $error_level , Config :: REPORT_SUPPRESS );
2018-06-17 02:01:33 +02:00
}
2020-03-28 21:30:56 +01:00
$this -> analyzeFile ( $file_path , new Context ());
2018-06-17 02:01:33 +02:00
}
/**
2018-11-06 03:57:36 +01:00
* @ dataProvider providerInvalidCodeParse
2018-06-17 02:01:33 +02:00
*
* @ param string $code
* @ param string $error_message
* @ param array < string > $error_levels
*
*/
2020-09-12 17:24:05 +02:00
public function testInvalidCode ( $code , $error_message , $error_levels = []) : void
2018-06-17 02:01:33 +02:00
{
2018-07-13 23:44:50 +02:00
if ( strpos ( $this -> getTestName (), 'SKIPPED-' ) !== false ) {
2018-06-17 02:01:33 +02:00
$this -> markTestSkipped ();
}
2021-12-03 20:29:06 +01:00
$this -> expectException ( CodeException :: class );
2022-01-19 19:29:16 +01:00
$this -> expectExceptionMessageMatches ( '/\b' . preg_quote ( $error_message , '/' ) . '\b/' );
2018-06-17 02:01:33 +02:00
$file_path = self :: $src_dir_path . 'somefile.php' ;
foreach ( $error_levels as $error_level ) {
2018-11-11 18:01:14 +01:00
$this -> project_analyzer -> getCodebase () -> config -> setCustomErrorLevel ( $error_level , Config :: REPORT_SUPPRESS );
2018-06-17 02:01:33 +02:00
}
$this -> addFile (
$file_path ,
$code
);
2020-03-28 21:30:56 +01:00
$this -> analyzeFile ( $file_path , new Context ());
2018-06-17 02:01:33 +02:00
}
/**
2019-06-15 23:57:40 +02:00
* @ return array < string , array { string , error_levels ? : string []} >
2018-06-17 02:01:33 +02:00
*/
2020-09-12 17:24:05 +02:00
public function providerValidCodeParse () : array
2018-06-17 02:01:33 +02:00
{
return [
'arrayOffset' => [
' < ? php
$a = 0 ;
$arr = [ " hello " ];
echo $arr [ $a ]; ' ,
],
'unset' => [
' < ? php
$a = 0 ;
$arr = [ " hello " ];
unset ( $arr [ $a ]); ' ,
],
'usedVariables' => [
' < ? php
/** @return string */
function foo () {
$a = 5 ;
$b = [];
$c [] = " hello " ;
2019-01-02 12:58:49 +01:00
class Foo {
public function __construct ( string $_i ) {}
}
2018-06-17 02:01:33 +02:00
$d = " Foo " ;
$e = " arg " ;
$f = new $d ( $e );
return $a . implode ( " , " , $b ) . $c [ 0 ] . get_class ( $f );
} ' ,
'error_levels' => [
'PossiblyUndefinedVariable' ,
'MixedArrayAccess' ,
'MixedOperand' ,
'MixedAssignment' ,
2018-11-12 18:03:55 +01:00
'InvalidStringClass' ,
2018-06-17 02:01:33 +02:00
],
],
'varDefinedInIfWithReference' => [
' < ? php
$a = 5 ;
if ( rand ( 0 , 1 )) {
$b = " hello " ;
} else {
$b = " goodbye " ;
}
echo $a . $b ; ' ,
],
'varRedefinedInIfWithReference' => [
' < ? php
2020-11-25 18:04:48 +01:00
$a = ( string ) 5 ;
2018-06-17 02:01:33 +02:00
if ( rand ( 0 , 1 )) {
2020-11-25 18:04:48 +01:00
$a = ( string ) 6 ;
2018-06-17 02:01:33 +02:00
}
echo $a ; ' ,
],
'byrefInForeachLoopWithReference' => [
' < ? php
$a = [ 1 , 2 , 3 ];
foreach ( $a as & $b ) {
$b = $b + 1 ;
}
echo $a [ 0 ]; ' ,
],
2018-06-17 03:14:19 +02:00
'foreachVarSetInValue' => [
' < ? php
/** @param string[] $arr */
function foo ( array $arr ) : void {
$a = null ;
foreach ( $arr as $a ) { }
if ( $a ) {}
} ' ,
],
2018-06-17 02:01:33 +02:00
'definedInSecondBranchOfCondition' => [
' < ? php
if ( rand ( 0 , 1 ) && $a = rand ( 0 , 1 )) {
echo $a ;
} ' ,
],
'booleanOr' => [
' < ? php
function foo ( int $a , int $b ) : bool {
return $a || $b ;
} ' ,
],
'paramUsedInIf' => [
' < ? php
function foo ( string $a ) : void {
if ( rand ( 0 , 1 )) {
echo $a ;
}
} ' ,
],
'dummyByRefVar' => [
' < ? php
function foo ( string & $a = null , string $b = null ) : void {
if ( $a ) {
echo $a ;
}
if ( $b ) {
echo $b ;
}
}
function bar () : void {
foo ( $dummy_byref_var , " hello " );
}
bar (); ' ,
],
'foreachReassigned' => [
' < ? php
2020-09-25 06:14:27 +02:00
/**
* @ param list < int > $arr
*/
function foo ( array $arr ) : void {
$a = false ;
2018-06-17 02:01:33 +02:00
2020-09-25 06:14:27 +02:00
foreach ( $arr as $b ) {
$a = true ;
echo $b ;
}
2018-06-17 02:01:33 +02:00
2020-09-25 06:14:27 +02:00
echo $a ;
} ' ,
2018-06-17 02:01:33 +02:00
],
'doWhileReassigned' => [
' < ? php
$a = 5 ;
do {
echo $a ;
$a = $a - rand ( - 3 , 3 );
} while ( $a > 3 ); ' ,
],
'loopTypeChangedInIfAndContinueWithReference' => [
' < ? php
$a = false ;
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = true ;
continue ;
}
$a = false ;
}
echo $a ; ' ,
],
'loopReassignedInIfAndContinueWithReferenceAfter' => [
' < ? php
$a = 5 ;
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = 7 ;
continue ;
}
$a = 3 ;
}
echo $a ; ' ,
],
'loopReassignedInIfAndContinueWithReferenceBeforeAndAfter' => [
' < ? php
$a = 5 ;
2018-11-11 01:05:51 +01:00
echo $a ;
2018-06-17 02:01:33 +02:00
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = 7 ;
continue ;
}
$a = 3 ;
}
echo $a ; ' ,
],
'loopReassigned' => [
' < ? php
$a = false ;
while ( rand ( 0 , 1 )) {
$a = true ;
}
echo $a ; ' ,
],
'ifVarReassignedInBranchWithUse' => [
' < ? php
$a = true ;
if ( rand ( 0 , 1 )) {
$a = false ;
}
if ( $a ) {
echo " cool " ;
} ' ,
],
'elseVarReassignedInBranchAndReference' => [
' < ? php
$a = false ;
if ( rand ( 0 , 1 )) {
// do nothing
} else {
$a = true ;
//echo $a;
}
if ( $a ) {
echo " cool " ;
} ' ,
],
'switchVarReassignedInBranch' => [
' < ? php
$a = false ;
switch ( rand ( 0 , 2 )) {
case 0 :
$a = true ;
}
if ( $a ) {
echo " cool " ;
} ' ,
],
'switchVarDefinedInAllBranches' => [
' < ? php
switch ( rand ( 0 , 2 )) {
case 0 :
$a = true ;
break ;
default :
$a = false ;
}
if ( $a ) {
echo " cool " ;
} ' ,
],
2018-11-11 01:05:51 +01:00
'switchVarConditionalAssignmentWithReference' => [
2018-06-17 03:54:44 +02:00
' < ? php
switch ( rand ( 0 , 4 )) {
case 0 :
if ( rand ( 0 , 1 )) {
$a = 0 ;
break ;
}
default :
$a = 1 ;
}
2019-03-23 19:27:54 +01:00
echo $a ; ' ,
2018-06-17 03:54:44 +02:00
],
2018-06-17 02:01:33 +02:00
'throwWithMessageCall' => [
' < ? php
function dangerous () : void {
throw new \Exception ( " bad " );
}
function callDangerous () : void {
try {
dangerous ();
} catch ( Exception $e ) {
echo $e -> getMessage ();
}
} ' ,
],
'throwWithMessageCallAndAssignmentAndReference' => [
' < ? php
function dangerous () : string {
if ( rand ( 0 , 1 )) {
throw new \Exception ( " bad " );
}
return " hello " ;
}
function callDangerous () : void {
$s = null ;
try {
$s = dangerous ();
} catch ( Exception $e ) {
echo $e -> getMessage ();
}
if ( $s ) {}
} ' ,
],
'throwWithMessageCallAndAssignmentInCatchAndReference' => [
' < ? php
function dangerous () : string {
if ( rand ( 0 , 1 )) {
throw new \Exception ( " bad " );
}
return " hello " ;
}
function callDangerous () : void {
$s = null ;
try {
dangerous ();
} catch ( Exception $e ) {
echo $e -> getMessage ();
$s = " hello " ;
}
if ( $s ) {}
} ' ,
],
'throwWithMessageCallAndAssignmentInTryAndCatchAndReference' => [
' < ? php
function dangerous () : string {
if ( rand ( 0 , 1 )) {
throw new \Exception ( " bad " );
}
return " hello " ;
}
function callDangerous () : void {
try {
$s = dangerous ();
} catch ( Exception $e ) {
echo $e -> getMessage ();
$s = " hello " ;
}
if ( $s ) {}
} ' ,
],
'throwWithMessageCallAndNestedAssignmentInTryAndCatchAndReference' => [
' < ? php
function dangerous () : string {
if ( rand ( 0 , 1 )) {
throw new \Exception ( " bad " );
}
return " hello " ;
}
function callDangerous () : void {
$s = null ;
if ( rand ( 0 , 1 )) {
$s = " hello " ;
} else {
try {
$t = dangerous ();
} catch ( Exception $e ) {
echo $e -> getMessage ();
$t = " hello " ;
}
if ( $t ) {
$s = $t ;
}
}
if ( $s ) {}
} ' ,
],
2018-06-17 03:14:19 +02:00
'throwWithReturnInOneCatch' => [
' < ? php
class E1 extends Exception {}
function dangerous () : void {
if ( rand ( 0 , 1 )) {
throw new \Exception ( " bad " );
}
}
function callDangerous () : void {
try {
dangerous ();
$s = true ;
} catch ( E1 $e ) {
echo $e -> getMessage ();
$s = false ;
} catch ( Exception $e ) {
return ;
}
if ( $s ) {}
} ' ,
],
2018-06-17 02:01:33 +02:00
'loopWithIfRedefinition' => [
' < ? php
$i = false ;
foreach ([ 1 , 2 , 3 ] as $a ) {
if ( rand ( 0 , 1 )) {
$i = true ;
}
echo $a ;
}
if ( $i ) {} ' ,
],
2020-03-06 03:10:29 +01:00
'unknownMethodCallWithVar' => [
' < ? php
/** @psalm-suppress MixedMethodCall */
function passesByRef ( object $a ) : void {
2020-03-06 03:24:08 +01:00
/** @psalm-suppress PossiblyUndefinedVariable */
2020-03-06 03:10:29 +01:00
$a -> passedByRef ( $b );
} ' ,
],
2018-06-17 02:01:33 +02:00
'usedMethodCallVariable' => [
' < ? php
function reindex ( array $arr , string $methodName ) : array {
$ret = [];
foreach ( $arr as $element ) {
$ret [ $element -> $methodName ()] = true ;
}
return $ret ;
} ' ,
'error_levels' => [
'MixedAssignment' ,
'MixedMethodCall' ,
'MixedArrayOffset' ,
],
],
'globalVariableUsage' => [
' < ? php
$a = " hello " ;
function example () : void {
global $a ;
echo $a ;
2020-04-19 19:15:04 +02:00
$a = " hello " ;
2018-06-17 02:01:33 +02:00
}
example (); ' ,
],
'staticVar' => [
' < ? php
function use_static () : void {
static $token ;
if ( ! $token ) {
$token = rand ( 1 , 10 );
}
echo " token is $token\n " ;
} ' ,
],
2019-03-20 04:26:46 +01:00
'staticVarUsedLater' => [
' < ? php
function use_static () : int {
static $x = null ;
if ( $x ) {
return ( int ) $x ;
}
$x = rand ( 0 , 1 );
return - 1 ;
} ' ,
],
2018-06-17 02:01:33 +02:00
'tryCatchWithUseInIf' => [
' < ? php
function example_string () : string {
if ( rand ( 0 , 1 ) > 0 ) {
return " value " ;
}
throw new Exception ( " fail " );
}
function main () : void {
try {
$s = example_string ();
if ( ! $s ) {
echo " Failed to get string \n " ;
}
} catch ( Exception $e ) {
$s = " fallback " ;
}
printf ( " s is %s \n " , $s );
} ' ,
],
'loopTypeChangedInIfAndBreakWithReference' => [
' < ? php
2019-05-13 05:31:36 +02:00
$a = 1 ;
2018-06-17 02:01:33 +02:00
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
2019-05-13 05:31:36 +02:00
$a = 2 ;
2018-06-17 02:01:33 +02:00
break ;
}
2019-05-13 05:31:36 +02:00
$a = 3 ;
2018-06-17 02:01:33 +02:00
}
echo $a ; ' ,
],
'loopReassignedInIfAndBreakWithReferenceAfter' => [
' < ? php
$a = 5 ;
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = 7 ;
break ;
}
$a = 3 ;
}
echo $a ; ' ,
],
'loopSetIfNullWithBreakAndReference' => [
' < ? php
$a = null ;
while ( rand ( 0 , 1 )) {
if ( $a !== null ) {
$a = 4 ;
break ;
}
$a = 5 ;
}
echo $a ; ' ,
],
'loopSetIfNullWithContinueAndReference' => [
' < ? php
$a = null ;
while ( rand ( 0 , 1 )) {
if ( $a !== null ) {
$a = 4 ;
continue ;
}
$a = 5 ;
}
echo $a ; ' ,
],
'loopAssignmentAfterReferenceSimple' => [
' < ? php
$a = 0 ;
while ( rand ( 0 , 1 )) {
echo $a ;
$a = 1 ;
} ' ,
],
'loopAssignmentAfterReferenceWithContinue' => [
' < ? php
$a = 0 ;
while ( rand ( 0 , 1 )) {
echo $a ;
$a = 1 ;
continue ;
} ' ,
],
'loopAssignmentAfterReferenceWithConditionalAssignmentWithContinue' => [
' < ? php
$a = 0 ;
while ( rand ( 0 , 1 )) {
echo $a ;
if ( rand ( 0 , 1 )) {
$a = 1 ;
}
continue ;
} ' ,
],
'loopAssignmentAfterReferenceWithContinueInIf' => [
' < ? php
$a = 0 ;
while ( rand ( 0 , 1 )) {
echo $a ;
if ( rand ( 0 , 1 )) {
$a = 1 ;
continue ;
}
} ' ,
],
'loopAssignmentAfterReferenceWithContinueInSwitch' => [
' < ? php
$a = 0 ;
while ( rand ( 0 , 1 )) {
switch ( rand ( 0 , 1 )) {
case 0 :
$a = 1 ;
break ;
}
}
echo $a ; ' ,
],
2018-11-27 23:45:07 +01:00
'loopAssignmentAfterReferenceWithContinueInSwitch2' => [
2018-06-17 02:01:33 +02:00
' < ? php
$a = 0 ;
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
switch ( rand ( 0 , 1 )) {
case 0 :
$a = 1 ;
break ;
}
}
}
echo $a ; ' ,
],
'listVarAssignmentInIf' => [
' < ? php
$a = " a " ;
$b = " b " ;
if ( rand ( 0 , 1 )) {
list ( $a , $b ) = explode ( " . " , " c.d " );
}
echo $a ;
2019-03-23 19:27:54 +01:00
echo $b ; ' ,
2018-06-17 02:01:33 +02:00
],
2020-09-25 06:14:27 +02:00
'arrayVarAssignmentInFunctionAndReturned' => [
' < ? php
/**
* @ param array { string } $arr
*/
function far ( array $arr ) : string {
[ $a ] = $arr ;
return $a ;
} ' ,
],
'arrayUnpackInForeach' => [
' < ? php
/**
* @ param list < array { string , string } > $arr
*/
function far ( array $arr ) : void {
foreach ( $arr as [ $a , $b ]) {
echo $a ;
echo $b ;
}
} ' ,
],
'arrayAssignmentInFunctionCoerced' => [
' < ? php
class A {
public int $a = 0 ;
public int $b = 1 ;
function setPhpVersion ( string $version ) : void {
[ $a , $b ] = explode ( " . " , $version );
$this -> a = ( int ) $a ;
$this -> b = ( int ) $b ;
}
}
'
],
2018-06-17 02:01:33 +02:00
'varCheckAfterNestedAssignmentAndBreak' => [
' < ? php
$a = false ;
if ( rand ( 0 , 1 )) {
while ( rand ( 0 , 1 )) {
$a = true ;
break ;
}
}
if ( $a ) {} ' ,
],
'varCheckAfterNestedAssignmentAndBreakInIf' => [
' < ? php
$a = false ;
if ( rand ( 0 , 1 )) {
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = true ;
break ;
}
}
}
if ( $a ) {} ' ,
],
'breakInSwitchStatementIf' => [
' < ? php
$a = 0 ;
while ( rand ( 0 , 1 )) {
switch ( rand ( 0 , 1 )) {
default :
echo $a ;
if ( rand ( 0 , 1 )) {
$a = 5 ;
break ;
}
}
} ' ,
],
'breakInSwitchStatementIfWithSecondCase' => [
' < ? php
$a = 0 ;
while ( rand ( 0 , 1 )) {
switch ( rand ( 0 , 1 )) {
case 0 :
$a = 1 ;
break ;
default :
echo $a ;
if ( rand ( 0 , 1 )) {
$a = 5 ;
break ;
}
}
} ' ,
],
2018-06-17 05:40:25 +02:00
'echoVarWithAdditionOp' => [
' < ? php
$a = 5 ;
2020-09-25 06:14:27 +02:00
while ( rand ( 0 , 1 )) {
echo ( $a += 1 );
} ' ,
2018-06-17 05:40:25 +02:00
],
'echoVarWithIncrement' => [
' < ? php
function foo ( int $i ) : void {
echo $i ;
}
2020-09-25 06:14:27 +02:00
2018-06-17 05:40:25 +02:00
$a = 5 ;
2020-09-25 06:14:27 +02:00
while ( rand ( 0 , 1 )) {
foo ( ++ $a );
} ' ,
2018-06-17 05:40:25 +02:00
],
2018-06-26 01:38:15 +02:00
'afterMethodExistsCheck' => [
' < ? php
class A {
/**
* @ param array < string , string > $options
*/
public function __construct ( array $options ) {
$this -> setOptions ( $options );
}
/**
* @ param array < string , string > $options
*/
protected function setOptions ( array $options ) : void
{
foreach ( $options as $key => $value ) {
$normalized = ucfirst ( $key );
$method = " set " . $normalized ;
if ( method_exists ( $this , $method )) {
$this -> $method ( $value );
}
}
}
}
new A ([ " bar " => " bat " ]); ' ,
],
2018-06-27 16:07:31 +02:00
'instanceofVarUse' => [
' < ? php
interface Foo { }
2020-03-15 19:43:05 +01:00
function returnFoo () : Foo {
return new class implements Foo { };
}
2018-06-27 16:07:31 +02:00
$interface = Foo :: class ;
if ( returnFoo () instanceof $interface ) {
exit ;
} ' ,
],
2018-06-30 17:08:51 +02:00
'usedVariableInDoWhile' => [
' < ? php
$i = 5 ;
do {
echo " hello " ;
} while ( -- $i > 0 );
echo $i ; ' ,
],
2018-10-04 22:42:40 +02:00
'callableReferencesItself' => [
' < ? php
/** @psalm-suppress UnusedParam */
function foo ( callable $c ) : void {}
$listener = function () use ( & $listener ) : void {
/** @psalm-suppress MixedArgument */
foo ( $listener );
};
foo ( $listener ); ' ,
],
2019-02-24 15:58:11 +01:00
'newVariableConstructor' => [
' < ? php
/**
* @ param class - string < ArrayObject > $type
*/
function bar ( string $type ) : ArrayObject {
$data = [[ " foo " ], [ " bar " ]];
2020-08-06 16:18:55 +02:00
/** @psalm-suppress UnsafeInstantiation */
2019-02-24 15:58:11 +01:00
return new $type ( $data [ 0 ]);
} ' ,
],
2019-03-10 21:36:35 +01:00
'byRefVariableUsedInAddition' => [
' < ? php
$i = 0 ;
$a = function () use ( & $i ) : void {
$i = 1 ;
};
2020-09-28 00:12:53 +02:00
$a ();
2021-03-21 01:53:51 +01:00
/** @psalm-suppress MixedArgument */
2020-09-28 00:12:53 +02:00
echo $i ; ' ,
2019-03-10 21:36:35 +01:00
],
2020-09-25 06:14:27 +02:00
'regularVariableClosureUseInAddition' => [
' < ? php
$i = 0 ;
$a = function () use ( $i ) : int {
return $i + 1 ;
};
$a (); ' ,
],
2019-03-20 04:16:00 +01:00
'superGlobalInFunction' => [
' < ? php
function example1 () : void {
$_SESSION = [];
}
function example2 () : int {
return ( int ) $_SESSION [ " str " ];
} ' ,
],
2019-03-28 03:13:06 +01:00
'usedInArray' => [
' < ? php
/**
* @ psalm - suppress MixedMethodCall
* @ psalm - suppress MissingParamType
*/
function foo ( $a ) : void {
2019-05-02 21:33:47 +02:00
$b = " b " ;
$a -> bar ([ $b ]);
} ' ,
],
'paramUsedInsideLoop' => [
' < ? php
function foo ( int $counter ) : void {
foreach ([ 1 , 2 , 3 ] as $_ ) {
echo ( $counter = $counter + 1 );
echo rand ( 0 , 1 ) ? 1 : 0 ;
}
2019-03-28 03:13:06 +01:00
} ' ,
],
2019-05-13 02:49:37 +02:00
'useParamInsideIfLoop' => [
' < ? php
function foo () : void {
$a = 1 ;
if ( rand ( 0 , 1 )) {
while ( rand ( 0 , 1 )) {
$a = 2 ;
}
}
echo $a ;
} ' ,
],
2019-05-20 17:54:21 +02:00
'useVariableInsideTry' => [
' < ? php
$foo = false ;
try {
if ( rand ( 0 , 1 )) {
throw new \Exception ( " bad " );
}
$foo = rand ( 0 , 1 );
if ( $foo ) {}
} catch ( Exception $e ) {}
if ( $foo ) {} ' ,
],
'useTryAssignedVariableInsideFinally' => [
' < ? php
$var = " " ;
try {
if ( rand ( 0 , 1 )) {
throw new \Exception ();
}
$var = " hello " ;
} finally {
if ( $var !== " " ) {
echo $var ;
}
2019-07-05 22:24:00 +02:00
} ' ,
2019-05-20 17:54:21 +02:00
],
2019-05-20 18:01:18 +02:00
'useTryAssignedVariableInFinallyWhenCatchExits' => [
' < ? php
/**
* @ return resource
*/
function getStream () {
throw new \Exception ();
}
$stream = null ;
try {
$stream = getStream ();
\file_put_contents ( " ./foobar " , $stream );
} catch ( \Exception $e ) {
throw new \Exception ( " Something went wrong " );
} finally {
2019-11-12 00:38:33 +01:00
if ( $stream ) {
\fclose ( $stream );
}
2019-05-20 18:01:18 +02:00
} ' ,
],
2019-07-04 23:35:33 +02:00
'varUsedInloop' => [
' < ? php
class A {
public static function getA () : ? A {
return rand ( 0 , 1 ) ? new A : null ;
}
}
function foo ( ? A $a ) : void {
while ( $a ) {
echo get_class ( $a );
$a = A :: getA ();
}
2019-07-05 22:24:00 +02:00
} ' ,
2019-07-04 23:35:33 +02:00
],
2019-07-24 22:53:14 +02:00
'varPassedByRef' => [
' < ? php
2020-09-28 00:12:53 +02:00
function foo ( array $returned ) : array {
$ancillary = & $returned ;
$ancillary [ " foo " ] = 5 ;
return $returned ;
2019-07-24 22:53:14 +02:00
} ' ,
],
2019-07-25 15:45:11 +02:00
'usedAsMethodName' => [
' < ? php
class A {
public static function foo () : void {}
}
function foo () : void {
$method = " foo " ;
A :: $method ();
} ' ,
],
2019-08-09 18:55:33 +02:00
'usedAsStaticPropertyName' => [
' < ? php
class A {
private static bool $something = false ;
public function foo () : void {
$var = " something " ;
2020-03-16 03:23:31 +01:00
if ( rand ( 0 , 1 )) {
2019-08-09 18:55:33 +02:00
static :: ${$var} = true ;
}
}
} '
],
2019-08-12 22:01:24 +02:00
'setInLoopThatsAlwaysEntered' => [
' < ? php
/**
* @ param non - empty - array < int > $a
*/
function getLastNum ( array $a ) : int {
foreach ( $a as $num ) {
$last = $num ;
}
return $last ;
} '
],
2019-08-13 20:07:45 +02:00
'usedStrtolowerInArray' => [
' < ? php
/**
* @ param array < string , int > $row
*/
function foo ( array $row , string $s ) : array {
$row [ " a " . strtolower ( $s )] += 1 ;
return $row ;
} ' ,
],
2019-08-13 20:53:31 +02:00
'pureWithReflectionMethodSetValue' => [
' < ? php
function foo ( object $mock ) : void {
$m = new \ReflectionProperty ( $mock , " bar " );
$m -> setValue ([ get_class ( $mock ) => " hello " ]);
} '
],
2019-08-15 15:43:43 +02:00
'defineBeforeAssignmentInConditional' => [
2019-08-14 23:15:35 +02:00
' < ? php
$i = null ;
if ( rand ( 0 , 1 ) || ( $i = rand ( 0 , 1 ))) {
echo $i ;
} ' ,
],
2019-08-15 15:43:43 +02:00
'definedInFirstAssignmentInConditional' => [
2019-08-14 23:15:35 +02:00
' < ? php
if (( $b = rand ( 0 , 1 )) || rand ( 0 , 1 )) {
echo $b ;
} ' ,
],
2019-08-15 15:51:40 +02:00
'noUnusedVariableWhenUndefinedMethod' => [
' < ? php
class A {}
function foo ( A $a ) : void {
$i = 0 ;
/** @psalm-suppress UndefinedMethod */
$a -> bar ( $i );
} ' ,
],
2019-08-26 17:41:15 +02:00
'noUnusedVariableAfterRedeclaredInCatch' => [
' < ? php
$path = " " ;
echo $path ;
try {
// do nothing
} catch ( \Exception $exception ) {
$path = " hello " ;
}
echo $path ; '
],
2019-08-27 04:54:54 +02:00
'assignedInElseif' => [
' < ? php
function bar () : int {
if ( rand ( 0 , 1 ) === 0 ) {
$foo = 0 ;
} elseif ( $foo = rand ( 0 , 10 )) {
return 5 ;
}
return $foo ;
} ' ,
],
2019-09-19 17:59:43 +02:00
'refineForeachVarType' => [
' < ? php
function foo () : array {
return [ " hello " ];
}
/** @var string $s */
foreach ( foo () as $s ) {
echo $s ;
} ' ,
],
2019-10-20 20:18:30 +02:00
'doWhileReassignedInConditional' => [
' < ? php
$index = 0 ;
do {
echo $index ;
} while (( $index = $index + 1 ) < 10 ); '
],
2019-11-09 16:39:21 +01:00
'tryCatchInsaneRepro' => [
' < ? php
function maybeThrows () : string {
return " hello " ;
}
function b ( bool $a ) : void {
if ( ! $a ) {
return ;
}
$b = " " ;
try {
$b = maybeThrows ();
echo $b ;
} catch ( \Exception $e ) {}
echo $b ;
} '
],
'tryCatchInsaneReproNoFirstBoolCheck' => [
' < ? php
function maybeThrows () : string {
return " hello " ;
}
function b () : void {
$b = " " ;
try {
$b = maybeThrows ();
echo $b ;
} catch ( \Exception $e ) {}
echo $b ;
} '
],
2019-11-09 17:01:48 +01:00
'tryWithWhile' => [
' < ? php
function foo () : void {
$done = false ;
while ( ! $done ) {
try {
$done = true ;
} catch ( \Exception $e ) {
}
}
} ' ,
],
'tryWithWhileWithoutTry' => [
' < ? php
function foo () : void {
$done = false ;
while ( ! $done ) {
$done = true ;
}
} ' ,
],
2019-11-11 22:52:55 +01:00
'usedInCatchAndTryWithReturnInTry' => [
' < ? php
function foo () : ? string {
$a = null ;
try {
$a = " hello " ;
echo $a ;
} catch ( Exception $e ) {
return $a ;
}
return $a ;
}
function dangerous () : string {
if ( rand ( 0 , 1 )) {
throw new \Exception ( " bad " );
}
return " hello " ;
} ' ,
],
2020-09-30 18:28:13 +02:00
'useTryAndCatchAssignedVariableInsideFinally' => [
2019-11-12 00:38:33 +01:00
' < ? php
function foo () : void {
try {
// do something dangerous
$a = 5 ;
} catch ( Exception $e ) {
$a = 4 ;
throw new Exception ( " bad " );
} finally {
2020-10-20 15:07:10 +02:00
/** @psalm-suppress PossiblyUndefinedVariable */
2019-11-12 00:38:33 +01:00
echo $a ;
}
} '
],
2019-11-12 06:52:10 +01:00
'usedVarInCatchAndAfter' => [
' < ? php
function foo () : void {
if ( rand ( 0 , 1 )) {
throw new \Exception ( " bad " );
}
}
$a = null ;
try {
foo ();
$a = " hello " ;
} catch ( \Exception $e ) {
echo $a ;
}
echo $a ; '
],
2019-11-13 19:38:34 +01:00
'unusedForeach' => [
' < ? php
/**
* @ param array < int , string > $test
*/
function foo ( array $test ) : void {
foreach ( $test as $key => $_testValue ) {
echo $key ;
}
} '
],
'usedAfterMixedVariableAssignment' => [
' < ? php
function foo ( array $arr ) : array {
$c = " c " ;
/** @psalm-suppress MixedArrayAssignment */
$arr [ " a " ][ " b " ][ $c ] = 1 ;
return $arr ;
} ' ,
2019-11-13 20:32:27 +01:00
],
'binaryOpIncrementInElse' => [
' < ? php
function foo ( int $i , string $alias ) : void {
echo $alias ? : $i ++ ;
echo $i ;
} '
],
'binaryOpIncrementInCond' => [
' < ? php
function foo ( int $i , string $alias ) : void {
echo $i ++ ? : $alias ;
echo $i ;
} '
],
'binaryOpIncrementInIf' => [
' < ? php
function foo ( int $i , string $alias ) : void {
echo rand ( 0 , 1 ) ? $i ++ : $alias ;
echo $i ;
} '
],
2019-11-18 22:14:21 +01:00
'usedInNewCall' => [
' < ? php
/**
* @ psalm - suppress MixedMethodCall
* @ psalm - suppress MixedArgument
2020-09-21 03:25:35 +02:00
* @ psalm - suppress PossiblyNullArgument
2020-09-30 18:28:13 +02:00
* @ param mixed $mixed
* @ param mixed | null $mixed_or_null
2019-11-18 22:14:21 +01:00
*/
2020-09-30 18:28:13 +02:00
function foo ( $mixed , $mixed_or_null ) : void {
$mixed -> foo ( new Exception ( $mixed_or_null ));
2019-11-18 22:14:21 +01:00
} ' ,
],
2019-12-01 16:19:36 +01:00
'validMixedAnnotation' => [
' < ? php
function keys () : array {
return [ " foo " , " bar " ];
}
/** @var mixed $k */
foreach ( keys () as $k ) {
echo gettype ( $k );
} '
],
2020-09-30 18:28:13 +02:00
'byRefVariableAfterAssignmentToArray' => [
' < ? php
$a = [ 1 , 2 , 3 ];
$b = & $a [ 1 ];
$b = 5 ;
print_r ( $a ); '
],
'byRefVariableAfterAssignmentToProperty' => [
2019-12-05 19:37:03 +01:00
' < ? php
class A {
public string $value = " " ;
public function writeByRef ( string $value ) : void {
$update =& $this -> value ;
$update = $value ;
}
} '
],
2019-12-06 20:47:05 +01:00
'createdAndUsedInCondition' => [
' < ? php
class A {
public function foo () : bool {
return true ;
}
}
function getA () : ? A {
return rand ( 0 , 1 ) ? new A () : null ;
}
if ( rand ( 0 , 1 )) {
if ( ! ( $a = getA ()) || $a -> foo ()) {}
return ;
}
if ( ! ( $a = getA ()) || $a -> foo ()) {} '
],
2019-12-18 17:37:48 +01:00
'usedInUndefinedFunction' => [
' < ? php
/**
* @ psalm - suppress MixedInferredReturnType
* @ psalm - suppress MixedReturnStatement
*/
function test () : string {
$s = " a " ;
/** @psalm-suppress UndefinedFunction */
return undefined_function ( $s );
} '
],
2020-01-02 20:07:02 +01:00
'useVariableVariable' => [
' < ? php
$variables = [ " a " => " b " , " c " => " d " ];
foreach ( $variables as $name => $value ) {
${$name} = $value ;
} '
],
2020-01-03 16:33:32 +01:00
'usedLoopVariable' => [
' < ? php
$a = 0 ;
while ( rand ( 0 , 1 )) {
if ( $a < 20 ) {
$a = $a + 1 ;
echo " hello " ;
continue ;
}
echo " goodbye " ;
break ;
} '
],
2020-01-04 17:16:53 +01:00
'usedForVariable' => [
' < ? php
$a = 0 ;
for ( $i = 0 ; $i < 1000 ; $i ++ ) {
if ( rand ( 0 , 1 )) {
2020-09-25 06:14:27 +02:00
$a = $a + 1 ;
2020-01-04 17:16:53 +01:00
continue ;
}
break ;
}
echo $a ; '
],
2020-09-30 18:28:13 +02:00
'usedForVariableMinusString' => [
' < ? php
function foo ( string $limit ) : void {
/**
* @ psalm - suppress InvalidOperand
*/
for ( $i = $limit ; $i > 0 ; $i -- ) {
echo $i . " \n " ;
}
} '
],
'usedForVariablePlusString' => [
' < ? php
function foo ( string $limit ) : void {
/**
* @ psalm - suppress InvalidOperand
*/
for ( $i = $limit ; $i < 50 ; $i ++ ) {
echo $i . " \n " ;
}
} '
],
2020-01-27 18:17:12 +01:00
'breakInForeachInsideSwitch' => [
' < ? php
function foo ( string $b ) : void {
switch ( $b ){
case " foo " :
$a = null ;
foreach ([ 1 , 2 , 3 ] as $f ){
if ( $f == 2 ) {
$a = $f ;
break ;
}
}
echo $a ;
}
} '
],
2020-09-28 00:12:53 +02:00
'passedByRefSimpleUndefinedBefore' => [
' < ? php
takes_ref ( $a );
function takes_ref ( ? array & $p ) : void {
$p = [ 0 ];
} '
],
'passedByRefSimpleDefinedBefore' => [
2020-09-25 06:14:27 +02:00
' < ? php
2020-09-28 00:12:53 +02:00
$a = [];
2020-09-25 06:14:27 +02:00
takes_ref ( $a );
function takes_ref ( ? array & $p ) : void {
$p = [ 0 ];
} '
],
2020-09-30 18:28:13 +02:00
'passedByRefSimpleDefinedBeforeWithExtract' => [
' < ? php
function foo ( array $arr ) : void {
while ( rand ( 0 , 1 )) {
/** @psalm-suppress MixedArgument */
extract ( $arr );
$a = [];
takes_ref ( $a );
}
}
/** @param mixed $p */
function takes_ref ( & $p ) : void {} '
],
2020-01-30 03:46:30 +01:00
'passedByRefArrayOffset' => [
' < ? php
$a = [
" a " => [ 1 ],
" b " => [ 2 ]
];
foreach ([ " a " ] as $e ){
takes_ref ( $a [ $e ]);
}
2021-06-22 01:55:27 +02:00
/** @param array<string|int> $p */
2020-01-30 03:46:30 +01:00
function takes_ref ( array & $p ) : void {
echo implode ( " , " , $p );
} '
],
2020-03-13 16:41:17 +01:00
'doWhileWithBreak' => [
' < ? php
function foo () : void {
$f = false ;
do {
if ( rand ( 0 , 1 )) {
$f = true ;
break ;
}
} while ( rand ( 0 , 1 ));
if ( $f ) {}
} '
],
2020-09-25 06:14:27 +02:00
'usedParamInWhileAddition' => [
' < ? php
function foo ( int $index ) : void {
while ( $index ++ <= 100 ) {
//
}
} '
],
2020-03-14 01:21:49 +01:00
'usedParamInWhileDirectly' => [
' < ? php
function foo ( int $index ) : void {
while ( 100 >= $index = nextNumber ( $index )) {
// ...
}
}
function nextNumber ( int $eee ) : int {
return $eee + 1 ;
} '
],
'usedParamInWhileIndirectly' => [
' < ? php
function foo ( int $i ) : void {
$index = $i ;
while ( 100 >= $index = nextNumber ( $index )) {
// ...
}
}
function nextNumber ( int $i ) : int {
return $i + 1 ;
} '
],
2020-07-31 18:44:01 +02:00
'doArrayIncrement' => [
' < ? php
/**
* @ param list < int > $keys
* @ param int $key
*/
function error2 ( array $keys , int $key ) : int
{
if ( $key === 1 ) {}
do {
$nextKey = $keys [ ++ $key ] ? ? null ;
} while ( $nextKey === null );
return $nextKey ;
} '
],
2020-09-25 06:14:27 +02:00
'variableUsedIndirectly' => [
' < ? php
$a = 0 ;
while ( rand ( 0 , 1 )){
$b = $a + 1 ;
echo $b ;
$a = $b ;
} ' ,
],
'arrayMapClosureWithParamType' => [
' < ? php
$a = [ 1 , 2 , 3 ];
$b = array_map (
function ( int $i ) {
return $i * 3 ;
},
$a
);
foreach ( $b as $c ) {
echo $c ;
} ' ,
],
'arrayMapClosureWithoutParamType' => [
' < ? php
$a = [ 1 , 2 , 3 ];
$b = array_map (
function ( $i ) {
return $i * 3 ;
},
$a
);
foreach ( $b as $c ) {
echo $c ;
} ' ,
],
'unusedArrayAdditionWithArrayChecked' => [
' < ? php
$a = [];
while ( rand ( 0 , 1 )) {
$a [] = 1 ;
}
if ( $a ) {} ' ,
],
'usedArrayRecursiveAddition' => [
' < ? php
$a = [];
while ( rand ( 0 , 1 )) {
$a [] = $a ;
}
print_r ( $a ); ' ,
],
'usedImmutableProperty' => [
' < ? php
/**
* @ psalm - immutable
*/
class Clause {
/**
* @ var array < int , int >
*/
public $b = [];
}
function foo ( Clause $c , int $var ) : void {
$new_b = $c -> b ;
if ( isset ( $c -> b [ 0 ])) {
$new_b [ $var ] = 0 ;
}
if ( $new_b ) {}
} ' ,
],
'arrayAssignOpAdditionInsideLoop' => [
' < ? php
/**
* @ param array < string , string > $arr0
* @ param array < string , string > $arr1
* @ param array < string , string > $arr2
* @ return void
*/
function parp ( array $arr0 , array $arr1 , array $arr2 ) {
$arr3 = $arr0 ;
foreach ( $arr1 as $a ) {
echo $a ;
$arr3 += $arr2 ;
}
if ( $arr3 ) {}
} ' ,
],
'arrayAdditionInsideLoop' => [
' < ? php
/**
* @ param array < string , string > $arr0
* @ param array < string , string > $arr1
* @ param array < string , string > $arr2
* @ return void
*/
function parp ( array $arr0 , array $arr1 , array $arr2 ) {
$arr3 = $arr0 ;
foreach ( $arr1 as $a ) {
echo $a ;
$arr3 = $arr3 + $arr2 ;
}
if ( $arr3 ) {}
} ' ,
],
'checkValueBeforeAdding' => [
' < ? php
class T {
public bool $b = false ;
}
function foo (
? T $t
) : void {
if ( ! $t ) {
$t = new T ();
} elseif ( rand ( 0 , 1 )) {
//
}
if ( $t -> b ) {}
} '
],
'loopOverUnknown' => [
' < ? php
/** @psalm-suppress MixedAssignment */
function foo ( Traversable $t ) : void {
foreach ( $t as $u ) {
if ( $u instanceof stdClass ) {}
}
} '
],
'loopWithRequire' => [
' < ? php
/**
* @ psalm - suppress UnresolvableInclude
*/
function foo ( string $delta_file ) : void {
while ( rand ( 0 , 1 )) {
/**
* @ var array < string , mixed >
*/
$diff_call_map = require ( $delta_file );
foreach ( $diff_call_map as $key => $_ ) {
$cased_key = strtolower ( $key );
echo $cased_key ;
}
}
} ' ,
],
'loopAgain' => [
' < ? php
/** @param non-empty-list<string> $lines */
function parse ( array $lines ) : array {
$last = 0 ;
foreach ( $lines as $k => $line ) {
if ( rand ( 0 , 1 )) {
$last = $k ;
} elseif ( rand ( 0 , 1 )) {
$last = 0 ;
} elseif ( $last !== 0 ) {
$lines [ $last ] .= $line ;
}
}
return $lines ;
} '
],
'necessaryVarAnnotation' => [
' < ? php
function foo ( array $arr ) : void {
/** @var int $key */
foreach ( $arr as $key => $_ ) {
echo $key ;
}
} '
],
'continuingEducation' => [
' < ? php
function breakUpPathIntoParts () : void {
$b = false ;
while ( rand ( 0 , 1 )) {
if ( $b ) {
if ( rand ( 0 , 1 )) {
$b = 0 ;
}
echo " hello " ;
continue ;
}
$b = true ;
}
} '
],
'usedInBinaryOp' => [
' < ? php
function foo ( int $a , int $b ) : int {
$a |= $b ;
return $a ;
} '
],
2020-09-25 16:20:22 +02:00
'reassignedInFinally' => [
' < ? php
function getRows ( int $s ) : void {
try {}
finally {
$s = $s + 3 ;
}
echo $s ;
} '
],
2020-09-28 00:12:53 +02:00
'divAssignOp' => [
' < ? php
function hslToRgb ( float $hue ) : float {
$hue /= 360 ;
return $hue ;
} '
],
'concatAssignOp' => [
' < ? php
function hslToRgb ( string $hue ) : string {
$hue .= " hello " ;
return $hue ;
} '
],
'possiblyUndefinedVariableUsed' => [
' < ? php
function foo ( string $a ) : void {
if ( $a === " a " ) {
$hue = " hello " ;
} elseif ( $a === " b " ) {
$hue = " goodbye " ;
}
2021-03-21 01:53:51 +01:00
/**
* @ psalm - suppress PossiblyUndefinedVariable
* @ psalm - suppress MixedArgument
*/
2020-09-28 00:12:53 +02:00
echo $hue ;
} '
],
'possiblyUndefinedVariableUsedInUnknownMethod' => [
' < ? php
function foo ( string $a , object $b ) : void {
if ( $a === " a " ) {
$hue = " hello " ;
} elseif ( $a === " b " ) {
$hue = " goodbye " ;
}
/**
* @ psalm - suppress PossiblyUndefinedVariable
* @ psalm - suppress MixedMethodCall
*/
$b -> foo ( $hue );
} '
],
'usedAsArrayKey' => [
' < ? php
function hslToRgb ( string $hue , string $lightness ) : array {
$arr = [ $hue => $lightness ];
return $arr ;
} '
],
'assignToGlobalVar' => [
' < ? php
/** @psalm-suppress MixedAssignment */
function foo ( array $args ) : void {
foreach ( $args as $key => $value ) {
$_GET [ $key ] = $value ;
}
} '
],
'assignToArrayTwice' => [
' < ? php
function foo ( string $c ) : void {
$arr = [ $c ];
$arr [] = 1 ;
foreach ( $arr as $e ) {
echo $e ;
}
} '
],
'classPropertyThing' => [
' < ? php
function foo () : string {
$notice = " i " ;
$notice .= " j " ;
$notice .= " k " ;
$notice .= " l " ;
$notice .= " m " ;
$notice .= " n " ;
$notice .= " o " ;
$notice .= " p " ;
$notice .= " q " ;
$notice .= " r " ;
$notice .= " s " ;
return $notice ;
} '
],
'usedInIsset' => [
' < ? php
function foo ( int $i ) : void {
if ( $i === 0 ) {
$j = " hello " ;
} elseif ( $i === 1 ) {
$j = " goodbye " ;
}
if ( isset ( $j )) {
2021-03-21 01:53:51 +01:00
/** @psalm-suppress MixedArgument */
2020-09-28 00:12:53 +02:00
echo $j ;
}
} '
],
'byRefNestedArrayParam' => [
' < ? php
function foo ( array & $arr ) : void {
$b = 5 ;
$arr [ 0 ] = $b ;
} '
],
'byRefNestedArrayInForeach' => [
' < ? php
function foo ( array $arr ) : array {
/**
* @ psalm - suppress MixedAssignment
* @ psalm - suppress MixedArrayAssignment
*/
foreach ( $arr as & $element ) {
$b = 5 ;
$element [ 0 ] = $b ;
}
return $arr ;
} '
],
'instantArrayAssignment' => [
' < ? php
function foo ( string $b ) : array {
/** @psalm-suppress PossiblyUndefinedVariable */
$arr [ " foo " ] = $b ;
return $arr ;
} ' ,
],
2020-09-30 18:28:13 +02:00
'explodeSource' => [
' < ? php
$start = microtime ();
$start = explode ( " " , $start );
/**
* @ psalm - suppress InvalidOperand
*/
$start = $start [ 1 ] + $start [ 0 ];
echo $start ; '
],
2020-09-28 00:12:53 +02:00
'csvByRefForeach' => [
' < ? php
function foo ( string $value ) : array {
$arr = str_getcsv ( $value );
foreach ( $arr as & $element ) {
$element = $element ? : " foo " ;
}
return $arr ;
} '
],
2020-09-30 18:28:13 +02:00
'memoryFree' => [
' < ? php
function verifyLoad ( string $free ) : void {
$free = explode ( " \n " , $free );
$parts_mem = preg_split ( " / \ s+/ " , $free [ 1 ]);
$free_mem = $parts_mem [ 3 ];
$total_mem = $parts_mem [ 1 ];
/** @psalm-suppress InvalidOperand */
$used_mem = ( $total_mem - $free_mem ) / $total_mem ;
echo $used_mem ;
} '
],
2020-09-28 00:12:53 +02:00
'returnNotBool' => [
' < ? php
function verifyLoad ( bool $b ) : bool {
$c = ! $b ;
return $c ;
} '
],
'sourcemaps' => [
' < ? php
/**
* @ psalm - suppress MixedAssignment
2021-03-21 01:53:51 +01:00
* @ psalm - suppress MixedArgument
2020-09-28 00:12:53 +02:00
* @ param iterable < mixed , int > $keys
*/
function foo ( iterable $keys , int $colno ) : void {
$i = 0 ;
$key = 0 ;
$index = 0 ;
foreach ( $keys as $index => $key ) {
if ( $key === $colno ) {
$i = $index ;
break ;
} elseif ( $key > $colno ) {
$i = $index ;
break ;
}
}
echo $i ;
echo $index ;
echo $key ;
} '
],
'whileLoopVarUpdatedInWhileLoop' => [
' < ? php
/** @param non-empty-list<int> $arr */
function foo ( array $arr ) : void {
while ( $a = array_pop ( $arr )) {
if ( $a === 4 ) {
$arr = array_merge ( $arr , [ " a " , " b " , " c " ]);
continue ;
}
echo " here " ;
}
} '
],
'usedThroughParamByRef' => [
' < ? php
$arr = [];
$populator = function ( array & $arr ) : void {
$arr [] = 5 ;
};
$populator ( $arr );
print_r ( $arr ); '
],
'maybeUndefinedCheckedWithEmpty' => [
' < ? php
function foo ( array $arr ) : void {
if ( rand ( 0 , 1 )) {
$maybe_undefined = $arr ;
}
if ( empty ( $maybe_undefined )) {
$maybe_undefined = [ 0 ];
}
print_r ( $maybe_undefined );
} '
],
'maybeUndefinedCheckedWithEmptyOrRand' => [
' < ? php
function foo ( array $arr ) : void {
if ( rand ( 0 , 1 )) {
$maybe_undefined = $arr ;
}
if ( empty ( $maybe_undefined ) || rand ( 0 , 1 )) {
$maybe_undefined = [ 0 ];
}
print_r ( $maybe_undefined );
} '
],
'maybeUndefinedCheckedWithNotIsset' => [
' < ? php
function foo ( array $arr ) : void {
if ( rand ( 0 , 1 )) {
$maybe_undefined = $arr ;
}
if ( ! isset ( $maybe_undefined )) {
$maybe_undefined = [ 0 ];
}
print_r ( $maybe_undefined );
} '
],
'maybeUndefinedCheckedWithImplicitIsset' => [
' < ? php
function foo ( array $arr ) : void {
if ( rand ( 0 , 1 )) {
$maybe_undefined = $arr ;
}
/** @psalm-suppress MixedAssignment */
$maybe_undefined = $maybe_undefined ? ? [ 0 ];
print_r ( $maybe_undefined );
} '
],
2020-09-30 18:28:13 +02:00
'usedInGlobalAfterAssignOp' => [
' < ? php
$total = 0 ;
$foo = & $total ;
$total = 5 ;
echo $foo ; '
],
'takesByRefThing' => [
' < ? php
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$c = 5 ;
}
takesByRef ( $c );
echo $c ;
}
/**
2020-11-27 23:05:26 +01:00
* @ psalm - param - out int $c
2020-09-30 18:28:13 +02:00
*/
function takesByRef ( ? int & $c ) : void {
$c = 7 ;
} '
],
'clips' => [
' < ? php declare ( strict_types = 1 );
function foo ( array $clips ) : void {
/** @psalm-suppress MixedAssignment */
foreach ( $clips as & $clip ) {
/** @psalm-suppress MixedArgument */
if ( ! empty ( $clip )) {
$legs = explode ( " / " , $clip );
$clip_id = $clip = $legs [ 1 ];
if (( is_numeric ( $clip_id ) || $clip = ( new \Exception ( $clip_id )))) {}
print_r ( $clips );
}
}
} '
],
'validator' => [
' < ? php
/**
* @ param bool $b
*/
function validate ( $b , string $source ) : void {
/**
* @ psalm - suppress DocblockTypeContradiction
* @ psalm - suppress MixedAssignment
*/
if ( ! is_bool ( $b )) {
$source = $b ;
}
print_r ( $source );
} '
],
2020-09-30 19:08:01 +02:00
'implicitSpread' => [
' < ? php
function validate ( bool $b , bool $c ) : void {
$d = [ $b , $c ];
print_r ( ... $d );
} '
],
2021-01-20 23:42:11 +01:00
'explicitSpread' => [
' < ? php
function f () : array {
$s = [ 1 , 2 , 3 ];
$b = [ " a " , " b " , " c " ];
$r = [ ... $s , ... $b ];
return $r ;
} '
],
2020-09-30 19:08:01 +02:00
'funcGetArgs' => [
' < ? php
function validate ( bool $b , bool $c ) : void {
2020-10-15 00:51:15 +02:00
/** @psalm-suppress MixedArgument */
2020-09-30 19:08:01 +02:00
print_r ( ... func_get_args ());
} '
],
2020-10-15 15:57:37 +02:00
'nullCoalesce' => [
' < ? php
function foo ( ? bool $b , int $c ) : void {
$b ? ? = $c ;
echo $b ;
} '
],
2020-10-20 15:32:08 +02:00
'arrowFunctionImplicitlyUsedVar' => [
' < ? php
function test ( Exception $e ) : callable {
return fn () => $e -> getMessage ();
} '
],
2020-10-20 16:59:09 +02:00
'useImmutableGetIteratorInForeach' => [
' < ? php
/**
* @ psalm - immutable
*/
class A implements IteratorAggregate
{
/**
* @ return Iterator < int >
*/
public function getIterator () {
yield from [ 1 , 2 , 3 ];
}
}
$a = new A ();
foreach ( $a as $v ) {
echo $v ;
} '
],
2020-10-28 19:06:05 +01:00
'castToBoolAndDouble' => [
' < ? php
function string_to_bool ( string $a ) : bool {
$b = ( bool ) $a ;
return $b ;
}
function string_to_float ( string $a ) : float {
$b = ( float ) $a ;
return $b ;
} '
],
2020-11-13 18:50:01 +01:00
'allowUseByRef' => [
' < ? php
/**
* @ psalm - suppress MixedReturnStatement
* @ psalm - suppress MixedInferredReturnType
*/
function foo ( array $data ) : array {
$output = [];
array_map (
function ( array $row ) use ( & $output ) {
$output = $row ;
},
$data
);
return $output ;
} '
],
2020-11-13 19:37:27 +01:00
'allowedUseByRefArrayAssignment' => [
' < ? php
$output_rows = [];
$a = function () use ( & $output_rows ) : void {
$output_row = 5 ;
/** @psalm-suppress MixedArrayAssignment */
$output_rows [] = $output_row ;
};
$a ();
print_r ( $output_rows ); '
],
2020-11-17 21:30:46 +01:00
'usedInAssignOpToByRef' => [
' < ? php
function foo ( int & $d ) : void {
$l = 4 ;
$d += $l ;
} ' ,
],
2020-12-03 19:09:08 +01:00
'mixedArrayAccessMighBeObject' => [
' < ? php
function takesResults ( array $arr ) : void {
/**
* @ psalm - suppress MixedAssignment
*/
foreach ( $arr as $item ) {
/**
* @ psalm - suppress MixedArrayAccess
* @ psalm - suppress MixedArrayAssignment
*/
$item [ 0 ] = $item [ 1 ];
}
} '
],
2021-02-07 04:07:01 +01:00
'usedThrow' => [
' < ? php
function f ( Exception $e ) : void {
throw $e ;
}
' ,
],
'usedThrowInReturnedCallable' => [
' < ? php
function createFailingFunction ( RuntimeException $exception ) : Closure
{
return static function () use ( $exception ) : void {
throw $exception ;
};
}
' ,
2021-03-11 06:08:32 +01:00
],
'usedInIntCastInAssignment' => [
' < ? php
/** @return mixed */
function f () {
$a = random_int ( 0 , 10 ) >= 5 ? true : false ;
$b = ( int ) $a ;
return $b ;
}
'
2021-03-16 18:41:43 +01:00
],
'promotedPropertiesAreNeverMarkedAsUnusedParams' => [
' < ? php
class Container {
private function __construct (
public float $value
) {}
public static function fromValue ( float $value ) : self {
return new self ( $value );
}
2021-03-17 06:10:42 +01:00
} '
2021-03-16 18:41:43 +01:00
],
2021-04-18 22:16:54 +02:00
'noUnusedVariableDefinedInBranchOfIf' => [
' < ? php
abstract class Foo {
abstract function validate () : bool | string ;
abstract function save () : bool | string ;
function bar () : int {
if (( $result = $this -> validate ()) && ( $result = $this -> save ())) {
return 0 ;
} elseif ( is_string ( $result )) {
return 1 ;
} else {
return 2 ;
}
}
} '
],
2021-04-25 20:49:43 +02:00
'concatWithUnknownProperty' => [
' < ? php
/** @param array<string> $key */
function foo ( object $a , string $k ) : string {
$sortA = " " ;
/** @psalm-suppress MixedOperand */
$sortA .= $a -> $k ;
return $sortA ;
} '
],
2021-04-25 21:11:23 +02:00
'varDocblockVariableIsUsedByRef' => [
' < ? php
2021-06-22 01:55:27 +02:00
/** @param array<string|int> $arr */
2021-04-25 21:11:23 +02:00
function foo ( array $arr ) : string {
/** @var string $val */
foreach ( $arr as & $val ) {
$val = urlencode ( $val );
}
return implode ( " / " , $arr );
} '
],
2021-07-12 23:05:33 +02:00
'initVariableInOffset' => [
' < ? php
$a = [
$b = " b " => $b ,
];
foreach ( $a as $key => $value ) {
echo $key . " " . $value ;
} ' ,
],
2021-07-12 19:09:20 +02:00
'intAndBitwiseNotOperator' => [
' < ? php
function foo () : int
{
$bitmask = 0x1 ;
$bytes = 2 ;
$ret = $bytes | ~ $bitmask ;
return $ret ;
} '
],
'stringAndBitwiseAndOperator' => [
' < ? php
function randomBits () : string
{
$bitmask = \chr ( 0xFF >> 1 );
$randomBytes = random_bytes ( 1 );
$randomBytes [ 0 ] = $randomBytes [ 0 ] & $bitmask ;
return $randomBytes ;
} '
],
2021-07-17 22:00:54 +02:00
'globalChangeValue' => [
' < ? php
function setProxySettingsFromEnv () : void {
global $a ;
$a = false ;
} '
],
2022-02-13 22:02:46 +01:00
'usedInCatchIsAlwaysUsedInTry' => [
' < ? php
$step = 0 ;
try {
$step = 1 ;
$step = 2 ;
} catch ( Throwable $_ ) {
echo $step ;
}
' ,
],
'usedInFinallyIsAlwaysUsedInTry' => [
' < ? php
$step = 0 ;
try {
$step = 1 ;
$step = 2 ;
} finally {
echo $step ;
}
' ,
],
2022-02-13 22:30:06 +01:00
'usedInFinallyIsAlwaysUsedInTryWithNestedTry' => [
' < ? php
$step = 0 ;
try {
try {
$step = 1 ;
} finally {
}
$step = 2 ;
$step = 3 ;
} finally {
echo $step ;
}
' ,
],
2018-06-17 02:01:33 +02:00
];
}
/**
2019-02-23 22:22:39 +01:00
* @ return array < string , array { string , error_message : string } >
2018-06-17 02:01:33 +02:00
*/
2020-09-12 17:24:05 +02:00
public function providerInvalidCodeParse () : array
2018-06-17 02:01:33 +02:00
{
return [
'simpleUnusedVariable' => [
' < ? php
$a = 5 ;
$b = [];
echo $a ; ' ,
'error_message' => 'UnusedVariable' ,
],
2018-06-17 05:40:25 +02:00
'unusedVarWithAdditionOp' => [
' < ? php
$a = 5 ;
$a += 1 ; ' ,
'error_message' => 'UnusedVariable' ,
],
'unusedVarWithConditionalAdditionOp' => [
' < ? php
$a = 5 ;
if ( rand ( 0 , 1 )) {
$a += 1 ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'unusedVarWithConditionalAddition' => [
' < ? php
$a = 5 ;
if ( rand ( 0 , 1 )) {
$a = $a + 1 ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'unusedVarWithIncrement' => [
' < ? php
$a = 5 ;
$a ++ ; ' ,
'error_message' => 'UnusedVariable' ,
],
'unusedVarWithConditionalIncrement' => [
' < ? php
$a = 5 ;
if ( rand ( 0 , 1 )) {
$a ++ ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
2018-06-17 02:01:33 +02:00
'ifInBothBranchesWithoutReference' => [
' < ? php
$a = 5 ;
if ( rand ( 0 , 1 )) {
$b = " hello " ;
} else {
$b = " goodbye " ;
}
echo $a ; ' ,
'error_message' => 'UnusedVariable' ,
],
'varInNestedAssignmentWithoutReference' => [
' < ? php
if ( rand ( 0 , 1 )) {
$a = " foo " ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'varInSecondNestedAssignmentWithoutReference' => [
' < ? php
if ( rand ( 0 , 1 )) {
$a = " foo " ;
echo $a ;
}
if ( rand ( 0 , 1 )) {
$a = " foo " ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'varReassignedInBothBranchesOfIf' => [
' < ? php
$a = " foo " ;
if ( rand ( 0 , 1 )) {
$a = " bar " ;
} else {
$a = " bat " ;
}
echo $a ; ' ,
'error_message' => 'UnusedVariable' ,
],
'varReassignedInNestedBranchesOfIf' => [
' < ? php
$a = " foo " ;
if ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = " bar " ;
} else {
$a = " bat " ;
}
} else {
$a = " bang " ;
}
echo $a ; ' ,
'error_message' => 'UnusedVariable' ,
],
'ifVarReassignedInBranchWithNoUse' => [
' < ? php
$a = true ;
if ( rand ( 0 , 1 )) {
$a = false ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'elseVarReassignedInBranchAndNoReference' => [
' < ? php
$a = true ;
if ( rand ( 0 , 1 )) {
// do nothing
} else {
$a = false ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'switchVarReassignedInBranch' => [
' < ? php
$a = false ;
switch ( rand ( 0 , 2 )) {
case 0 :
$a = true ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'switchVarReassignedInBranchWithDefault' => [
' < ? php
$a = false ;
switch ( rand ( 0 , 2 )) {
case 0 :
$a = true ;
break ;
default :
$a = false ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'switchVarReassignedInAllBranches' => [
' < ? php
$a = false ;
switch ( rand ( 0 , 2 )) {
case 0 :
$a = true ;
break ;
default :
$a = false ;
}
if ( $a ) {
echo " cool " ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'unusedListVar' => [
' < ? php
list ( $a , $b ) = explode ( " " , " hello world " );
echo $a ; ' ,
'error_message' => 'UnusedVariable' ,
],
'unusedPreForVar' => [
' < ? php
$i = 0 ;
for ( $i = 0 ; $i < 10 ; $i ++ ) {
echo $i ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'unusedIfInReturnBlock' => [
' < ? php
$i = rand ( 0 , 1 );
foreach ([ 1 , 2 , 3 ] as $a ) {
if ( $a % 2 ) {
$i = 7 ;
return ;
}
}
if ( $i ) {} ' ,
'error_message' => 'UnusedVariable' ,
],
'unusedIfVarInBranch' => [
' < ? php
if ( rand ( 0 , 1 )) {
} elseif ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = " foo " ;
} else {
$a = " bar " ;
echo $a ;
}
} ' ,
'error_message' => 'UnusedVariable' ,
],
'throwWithMessageCallAndAssignmentAndNoReference' => [
' < ? php
function dangerous () : string {
if ( rand ( 0 , 1 )) {
throw new \Exception ( " bad " );
}
return " hello " ;
}
function callDangerous () : void {
$s = null ;
try {
$s = dangerous ();
} catch ( Exception $e ) {
echo $e -> getMessage ();
}
} ' ,
'error_message' => 'UnusedVariable' ,
],
'throwWithMessageCallAndAssignmentInCatchAndNoReference' => [
' < ? php
function dangerous () : string {
if ( rand ( 0 , 1 )) {
throw new \Exception ( " bad " );
}
return " hello " ;
}
function callDangerous () : void {
$s = null ;
try {
dangerous ();
} catch ( Exception $e ) {
echo $e -> getMessage ();
$s = " hello " ;
}
} ' ,
'error_message' => 'UnusedVariable' ,
],
'throwWithMessageCallAndNestedAssignmentInTryAndCatchAndNoReference' => [
' < ? php
function dangerous () : string {
if ( rand ( 0 , 1 )) {
throw new \Exception ( " bad " );
}
return " hello " ;
}
function callDangerous () : void {
$s = null ;
if ( rand ( 0 , 1 )) {
$s = " hello " ;
} else {
try {
$t = dangerous ();
} catch ( Exception $e ) {
echo $e -> getMessage ();
$t = " hello " ;
}
if ( $t ) {
$s = $t ;
}
}
2018-06-17 03:14:19 +02:00
} ' ,
'error_message' => 'UnusedVariable' ,
],
'throwWithReturnInOneCatchAndNoReference' => [
' < ? php
class E1 extends Exception {}
function dangerous () : void {
if ( rand ( 0 , 1 )) {
throw new \Exception ( " bad " );
}
}
function callDangerous () : void {
try {
dangerous ();
$s = true ;
} catch ( E1 $e ) {
echo $e -> getMessage ();
$s = false ;
} catch ( Exception $e ) {
return ;
}
2018-06-17 02:01:33 +02:00
} ' ,
'error_message' => 'UnusedVariable' ,
],
'loopTypeChangedInIfWithoutReference' => [
' < ? php
$a = false ;
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = true ;
}
} ' ,
'error_message' => 'UnusedVariable' ,
],
'loopTypeChangedInIfAndContinueWithoutReference' => [
' < ? php
$a = false ;
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = true ;
continue ;
}
$a = false ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'loopReassignedInIfAndContinueWithoutReferenceAfter' => [
' < ? php
$a = 5 ;
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = 7 ;
continue ;
}
$a = 3 ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'loopReassignedInIfAndContinueWithoutReference' => [
' < ? php
$a = 3 ;
2018-11-11 01:05:51 +01:00
echo $a ;
2018-06-17 02:01:33 +02:00
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = 5 ;
continue ;
}
$a = 3 ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'unusedConditionalCode' => [
' < ? php
$a = 5 ;
if ( rand ( 0 , 1 )) {
$a = $a + 5 ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'varDefinedInIfWithoutReference' => [
' < ? php
$a = 5 ;
if ( rand ( 0 , 1 )) {
$b = " hello " ;
} else {
$b = " goodbye " ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'SKIPPED-byrefInForeachLoopWithoutReference' => [
' < ? php
$a = [ 1 , 2 , 3 ];
foreach ( $a as & $b ) {
$b = $b + 1 ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'loopSetIfNullWithBreakWithoutReference' => [
' < ? php
$a = null ;
while ( rand ( 0 , 1 )) {
if ( $a !== null ) {
$a = 4 ;
break ;
}
$a = 5 ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
2018-11-27 23:45:07 +01:00
'loopSetIfNullWithBreakWithoutReference2' => [
2018-06-17 02:01:33 +02:00
' < ? php
$a = null ;
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = 4 ;
break ;
}
$a = 5 ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'loopSetIfNullWithContinueWithoutReference' => [
' < ? php
$a = null ;
while ( rand ( 0 , 1 )) {
if ( rand ( 0 , 1 )) {
$a = 4 ;
continue ;
}
$a = 5 ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'loopAssignmentAfterReferenceWithBreak' => [
' < ? php
$a = 0 ;
while ( rand ( 0 , 1 )) {
echo $a ;
$a = 1 ;
break ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'loopAssignmentAfterReferenceWithBreakInIf' => [
' < ? php
$a = 0 ;
while ( rand ( 0 , 1 )) {
echo $a ;
if ( rand ( 0 , 1 )) {
$a = 1 ;
break ;
}
} ' ,
'error_message' => 'UnusedVariable' ,
],
2018-06-17 03:54:44 +02:00
'switchVarConditionalAssignmentWithoutReference' => [
' < ? php
switch ( rand ( 0 , 4 )) {
case 0 :
if ( rand ( 0 , 1 )) {
$a = 0 ;
break ;
}
default :
$a = 1 ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
2018-06-17 06:12:26 +02:00
'switchInIf' => [
' < ? php
$a = 0 ;
if ( rand ( 0 , 1 )) {
switch ( rand ( 0 , 4 )) {
case 0 :
$a = 3 ;
break ;
default :
$a = 3 ;
}
} else {
$a = 6 ;
}
echo $a ; ' ,
'error_message' => 'UnusedVariable' ,
],
2018-06-17 06:52:32 +02:00
'reusedKeyVar' => [
' < ? php
$key = " a " ;
echo $key ;
$arr = [ " foo " => " foo.foo " ];
foreach ( $arr as $key => $v ) {
list ( $key ) = explode ( " . " , $v );
echo $key ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
2019-05-02 23:21:02 +02:00
'detectUnusedVarBeforeTryInsideForeach' => [
' < ? php
function foo () : void {
$unused = 1 ;
while ( rand ( 0 , 1 )) {
try {} catch ( \Exception $e ) {}
}
} ' ,
'error_message' => 'UnusedVariable' ,
],
2019-05-13 02:49:37 +02:00
'detectUnusedVariableInsideIfLoop' => [
' < ? php
function foo () : void {
$a = 1 ;
if ( rand ( 0 , 1 )) {
while ( rand ( 0 , 1 )) {
$a = 2 ;
}
}
} ' ,
'error_message' => 'UnusedVariable' ,
],
'detectUnusedVariableInsideIfElseLoop' => [
' < ? php
function foo () : void {
$a = 1 ;
if ( rand ( 0 , 1 )) {
} else {
while ( rand ( 0 , 1 )) {
$a = 2 ;
}
}
} ' ,
'error_message' => 'UnusedVariable' ,
],
'detectUnusedVariableInsideIfElseifLoop' => [
' < ? php
function foo () : void {
$a = 1 ;
if ( rand ( 0 , 1 )) {
} elseif ( rand ( 0 , 1 )) {
while ( rand ( 0 , 1 )) {
$a = 2 ;
}
}
} ' ,
'error_message' => 'UnusedVariable' ,
],
'detectUnusedVariableInsideIfLoopWithEchoInside' => [
' < ? php
function foo () : void {
$a = 1 ;
if ( rand ( 0 , 1 )) {
while ( rand ( 0 , 1 )) {
$a = 2 ;
echo $a ;
}
}
} ' ,
'error_message' => 'UnusedVariable' ,
],
2019-05-13 05:31:36 +02:00
'detectUnusedVariableInsideLoopAfterAssignment' => [
' < ? php
function foo () : void {
foreach ([ 1 , 2 , 3 ] as $i ) {
$i = $i ;
}
} ' ,
2021-06-17 06:40:24 +02:00
'error_message' => 'UnusedForeachValue' ,
2019-05-13 05:31:36 +02:00
],
'detectUnusedVariableInsideLoopAfterAssignmentWithAddition' => [
' < ? php
function foo () : void {
foreach ([ 1 , 2 , 3 ] as $i ) {
$i = $i + 1 ;
}
} ' ,
2021-06-17 06:40:24 +02:00
'error_message' => 'UnusedForeachValue' ,
2019-05-13 05:31:36 +02:00
],
'detectUnusedVariableInsideLoopCalledInFunction' => [
' < ? php
function foo ( int $s ) : int {
return $s ;
}
function bar () : void {
foreach ([ 1 , 2 , 3 ] as $i ) {
$i = foo ( $i );
}
} ' ,
'error_message' => 'UnusedVariable' ,
],
2019-05-21 00:25:11 +02:00
'detectUnusedVariableReassignedInIfFollowedByTryInsideForLoop' => [
' < ? php
$user_id = 0 ;
$user = null ;
if ( rand ( 0 , 1 )) {
$user_id = rand ( 0 , 1 );
$user = $user_id ;
}
if ( $user ) {
$a = 0 ;
for ( $i = 1 ; $i <= 10 ; $i ++ ) {
$a += $i ;
try {} catch ( \Exception $e ) {}
}
echo $i ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'detectUnusedVariableReassignedInIfFollowedByTryInsideForeachLoop' => [
' < ? php
$user_id = 0 ;
$user = null ;
if ( rand ( 0 , 1 )) {
$user_id = rand ( 0 , 1 );
$user = $user_id ;
}
if ( $user ) {
$a = 0 ;
foreach ([ 1 , 2 , 3 ] as $i ) {
$a += $i ;
try {} catch ( \Exception $e ) {}
}
echo $i ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
2019-07-04 22:38:31 +02:00
'detectUselessArrayAssignment' => [
' < ? php
function foo () : void {
$a = [];
$a [ 0 ] = 1 ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
2019-07-04 23:35:33 +02:00
'detectUnusedSecondAssignmentBeforeTry' => [
' < ? php
$a = [ 1 , 2 , 3 ];
echo ( $a [ 0 ]);
$a = [ 4 , 5 , 6 ];
try {
// something
} catch ( \Throwable $t ) {
// something else
} ' ,
'error_message' => 'UnusedVariable' ,
],
2019-07-05 04:25:49 +02:00
'detectRedundancyAfterLoopWithContinue' => [
' < ? php
$gap = null ;
foreach ([ 1 , 2 , 3 ] as $_ ) {
if ( rand ( 0 , 1 )) {
continue ;
}
$gap = " asa " ;
throw new \Exception ( $gap );
} ' ,
'error_message' => 'UnusedVariable' ,
],
2019-08-12 22:01:24 +02:00
'setInLoopThatsAlwaysEnteredButNotReferenced' => [
' < ? php
/**
* @ param non - empty - array < int > $a
*/
function getLastNum ( array $a ) : int {
foreach ( $a as $num ) {
$last = $num ;
}
return 4 ;
} ' ,
2021-06-17 06:40:24 +02:00
'error_message' => 'UnusedForeachValue' ,
],
'conditionalForeachWithUnusedValue' => [
' < ? php
if ( rand ( 0 , 1 ) > 0 ) {
foreach ([ 1 , 2 , 3 ] as $val ) {}
}
' ,
'error_message' => 'UnusedForeachValue' ,
],
'doubleForeachWithInnerUnusedValue' => [
' < ? php
/**
* @ param non - empty - list < list < int >> $arr
* @ return list < int >
*/
function f ( array $arr ) : array {
foreach ( $arr as $elt ) {
foreach ( $elt as $subelt ) {}
}
return $elt ;
}
' ,
'error_message' => 'UnusedForeachValue'
2019-08-12 22:01:24 +02:00
],
2019-08-15 15:43:43 +02:00
'defineInBothBranchesOfConditional' => [
' < ? php
$i = null ;
if (( $i = rand ( 0 , 5 )) || ( $i = rand ( 0 , 3 ))) {
echo $i ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
2019-09-19 17:59:43 +02:00
'knownVarType' => [
' < ? php
function foo () : string {
return " hello " ;
}
/** @var string */
$a = foo ();
echo $a ; ' ,
'error_message' => 'UnnecessaryVarAnnotation' ,
],
'knownVarTypeWithName' => [
' < ? php
function foo () : string {
return " hello " ;
}
/** @var string $a */
$a = foo ();
echo $a ; ' ,
'error_message' => 'UnnecessaryVarAnnotation' ,
],
'knownForeachVarType' => [
' < ? php
/** @return string[] */
function foo () : array {
return [ " hello " ];
}
/** @var string $s */
foreach ( foo () as $s ) {
echo $s ;
} ' ,
'error_message' => 'UnnecessaryVarAnnotation' ,
],
2020-09-12 23:03:11 +02:00
'arrowFunctionUnusedVariable' => [
' < ? php
function f ( callable $c ) : void {
$c ( 22 );
}
f (
fn ( int $p )
=>
++ $p
); ' ,
'error_message' => 'UnusedVariable' ,
],
'arrowFunctionUnusedParam' => [
' < ? php
function f ( callable $c ) : void {
$c ( 22 );
}
f (
fn ( int $p )
=>
0
); ' ,
'error_message' => 'UnusedClosureParam' ,
],
2020-09-14 16:58:36 +02:00
'unusedFunctionParamWithDefault' => [
' < ? php
function foo ( bool $b = false ) : void {} ' ,
'error_message' => 'UnusedParam' ,
],
2020-09-25 06:14:27 +02:00
'arrayMapClosureWithParamTypeNoUse' => [
' < ? php
$a = [ 1 , 2 , 3 ];
$b = array_map (
function ( int $i ) {
return rand ( 0 , 5 );
},
$a
);
foreach ( $b as $c ) {
echo $c ;
} ' ,
'error_message' => 'UnusedClosureParam' ,
],
2020-09-30 18:28:13 +02:00
'noUseOfInstantArrayAssignment' => [
' < ? php
function foo () : void {
/** @psalm-suppress PossiblyUndefinedVariable */
$arr [ " foo " ] = 1 ;
} ' ,
'error_message' => 'UnusedVariable' ,
],
'expectsNonNullAndPassedPossiblyNull' => [
' < ? php
/**
* @ param mixed | null $mixed_or_null
*/
function foo ( $mixed_or_null ) : Exception {
/**
* @ psalm - suppress MixedArgument
*/
return new Exception ( $mixed_or_null );
} ' ,
'error_message' => 'PossiblyNullArgument'
],
2020-11-13 19:13:29 +01:00
'useArrayAssignmentNeverUsed' => [
' < ? php
$data = [];
return function () use ( $data ) {
$data [] = 1 ;
}; ' ,
'error_message' => 'UnusedVariable' ,
],
2021-03-17 06:10:42 +01:00
'warnAboutOriginalBadArray' => [
' < ? php
function takesArray ( array $arr ) : void {
foreach ( $arr as $a ) {}
} ' ,
2021-03-21 02:45:38 +01:00
'error_message' => 'MixedAssignment - src' . DIRECTORY_SEPARATOR . 'somefile.php:3:42 - Unable to determine the type that $a is being assigned to. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:2:47'
2021-03-17 06:10:42 +01:00
],
2021-03-18 00:37:21 +01:00
'warnAboutOriginalBadFunctionCall' => [
' < ? php
function makeArray () : array {
return [ " hello " ];
}
$arr = makeArray ();
foreach ( $arr as $a ) {
echo $a ;
} ' ,
2021-03-21 02:45:38 +01:00
'error_message' => 'MixedAssignment - src' . DIRECTORY_SEPARATOR . 'somefile.php:8:38 - Unable to determine the type that $a is being assigned to. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:2:44'
2021-03-18 00:37:21 +01:00
],
'warnAboutOriginalBadStaticCall' => [
' < ? php
class A {
public static function makeArray () : array {
return [ " hello " ];
}
}
$arr = A :: makeArray ();
foreach ( $arr as $a ) {
echo $a ;
} ' ,
2021-03-21 02:45:38 +01:00
'error_message' => 'MixedAssignment - src' . DIRECTORY_SEPARATOR . 'somefile.php:10:38 - Unable to determine the type that $a is being assigned to. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:3:62'
2021-03-18 00:37:21 +01:00
],
'warnAboutOriginalBadInstanceCall' => [
' < ? php
class A {
public function makeArray () : array {
return [ " hello " ];
}
}
$arr = ( new A ) -> makeArray ();
foreach ( $arr as $a ) {
echo $a ;
} ' ,
2021-03-21 02:45:38 +01:00
'error_message' => 'MixedAssignment - src' . DIRECTORY_SEPARATOR . 'somefile.php:10:38 - Unable to determine the type that $a is being assigned to. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:3:55'
2021-03-18 00:37:21 +01:00
],
2021-03-18 20:19:29 +01:00
'warnAboutDocblockReturnType' => [
' < ? php
/** @return array[] */
function makeArray () : array {
return [[ " hello " ]];
}
$arr = makeArray ();
foreach ( $arr as $some_arr ) {
foreach ( $some_arr as $a ) {
echo $a ;
}
} ' ,
2021-03-21 02:45:38 +01:00
'error_message' => 'MixedAssignment - src' . DIRECTORY_SEPARATOR . 'somefile.php:10:47 - Unable to determine the type that $a is being assigned to. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:2:33'
],
'warnAboutMixedArgument' => [
' < ? php
function makeArray () : array {
return [ " hello " ];
}
$arr = makeArray ();
/** @psalm-suppress MixedAssignment */
foreach ( $arr as $a ) {
echo $a ;
} ' ,
'error_message' => 'MixedArgument - src' . DIRECTORY_SEPARATOR . 'somefile.php:10:30 - Argument 1 of echo cannot be mixed, expecting string. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:2:44'
],
'warnAboutMixedMethodCall' => [
' < ? php
function makeArray () : array {
return [ " hello " ];
}
$arr = makeArray ();
/** @psalm-suppress MixedAssignment */
foreach ( $arr as $a ) {
$a -> foo ();
} ' ,
'error_message' => 'MixedMethodCall - src' . DIRECTORY_SEPARATOR . 'somefile.php:10:29 - Cannot determine the type of $a when calling method foo. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:2:44'
],
'warnAboutMixedReturnStatement' => [
' < ? php
function makeArray () : array {
return [ " hello " ];
}
function foo () : string {
$arr = makeArray ();
/** @psalm-suppress MixedAssignment */
foreach ( $arr as $a ) {
return $a ;
}
return " " ;
} ' ,
'error_message' => 'MixedReturnStatement - src' . DIRECTORY_SEPARATOR . 'somefile.php:11:36 - Could not infer a return type. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:2:44'
2021-03-18 20:19:29 +01:00
],
2021-03-24 20:32:56 +01:00
'warnAboutIterableKeySource' => [
' < ? php
function foo ( iterable $arr ) : void {
foreach ( $arr as $key => $_ ) {}
} ' ,
'error_message' => 'MixedAssignment - src' . DIRECTORY_SEPARATOR . 'somefile.php:3:42 - Unable to determine the type that $key is being assigned to. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:2:43'
],
2021-03-24 21:42:22 +01:00
'warnAboutMixedKeySource' => [
' < ? php
/** @param mixed $arr */
function foo ( $arr ) : void {
foreach ( $arr as $key => $_ ) {}
} ' ,
'error_message' => 'MixedAssignment - src' . DIRECTORY_SEPARATOR . 'somefile.php:4:42 - Unable to determine the type that $key is being assigned to. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:3:34'
],
2021-03-28 17:32:38 +02:00
'warnAboutMixedArgumentTypeCoercionSource' => [
' < ? php
/** @param array<string> $arr */
function takesArrayOfString ( array $arr ) : void {
foreach ( $arr as $a ) {
echo $a ;
}
}
/** @param mixed $a */
function takesArray ( $a ) : void {
$arr = [ $a ];
takesArrayOfString ( $arr );
} ' ,
'error_message' => 'MixedArgumentTypeCoercion - src' . DIRECTORY_SEPARATOR . 'somefile.php:12:44 - Argument 1 of takesArrayOfString expects array<array-key, string>, parent type array{mixed} provided. Consider improving the type at src' . DIRECTORY_SEPARATOR . 'somefile.php:10:41'
],
2022-02-13 22:30:06 +01:00
'warnAboutUnusedVariableInTryReassignedInCatch' => [
' < ? php
$step = 0 ;
try {
$step = 1 ;
$step = 2 ;
} catch ( Throwable $_ ) {
$step = 3 ;
echo $step ;
}
' ,
'error_message' => 'UnusedVariable' ,
],
'warnAboutUnusedVariableInTryReassignedInFinally' => [
' < ? php
$step = 0 ;
try {
$step = 1 ;
$step = 2 ;
} finally {
$step = 3 ;
echo $step ;
}
' ,
'error_message' => 'UnusedVariable' ,
],
'SKIPPED-warnAboutVariableUsedInNestedTryNotUsedInOuterTry' => [
' < ? php
$step = 0 ;
try {
$step = 1 ; // Unused
$step = 2 ;
try {
$step = 3 ;
$step = 4 ;
} finally {
echo $step ;
}
} finally {
}
' ,
'error_message' => 'UnusedVariable' ,
],
2018-06-17 02:01:33 +02:00
];
}
}