Skip to content

Commit 08d5b71

Browse files
authored
Merge pull request #9878 from ygottschalk/fix/9820-glob-stub
Glob return type stub
2 parents 64b930e + 8bab567 commit 08d5b71

File tree

4 files changed

+144
-2
lines changed

4 files changed

+144
-2
lines changed

dictionaries/CallMap.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -3330,7 +3330,7 @@
33303330
'gettimeofday' => ['array<string, int>'],
33313331
'gettimeofday\'1' => ['float', 'as_float='=>'true'],
33323332
'gettype' => ['string', 'value'=>'mixed'],
3333-
'glob' => ['list<non-empty-string>|false', 'pattern'=>'string', 'flags='=>'int<1, max>'],
3333+
'glob' => ['false|list{0?:string, ...<non-empty-string>}', 'pattern'=>'string', 'flags='=>'int-mask<GLOB_MARK, GLOB_NOSORT, GLOB_NOCHECK, GLOB_NOESCAPE, GLOB_BRACE, GLOB_ONLYDIR, GLOB_ERR>'],
33343334
'GlobIterator::__construct' => ['void', 'pattern'=>'string', 'flags='=>'int'],
33353335
'GlobIterator::count' => ['int'],
33363336
'GlobIterator::current' => ['FilesystemIterator|SplFileInfo|string'],

dictionaries/CallMap_historical.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -10728,7 +10728,7 @@
1072810728
'gettimeofday' => ['array<string, int>'],
1072910729
'gettimeofday\'1' => ['float', 'as_float='=>'true'],
1073010730
'gettype' => ['string', 'value'=>'mixed'],
10731-
'glob' => ['list<non-empty-string>|false', 'pattern'=>'string', 'flags='=>'int<1, max>'],
10731+
'glob' => ['false|list{0?:string, ...<non-empty-string>}', 'pattern'=>'string', 'flags='=>'int-mask<GLOB_MARK, GLOB_NOSORT, GLOB_NOCHECK, GLOB_NOESCAPE, GLOB_BRACE, GLOB_ONLYDIR, GLOB_ERR>'],
1073210732
'gmdate' => ['string', 'format'=>'string', 'timestamp='=>'int'],
1073310733
'gmmktime' => ['int|false', 'hour='=>'int', 'minute='=>'int', 'second='=>'int', 'month='=>'int', 'day='=>'int', 'year='=>'int'],
1073410734
'gmp_abs' => ['GMP', 'num'=>'GMP|string|int'],

stubs/CoreGenericFunctions.phpstub

+29
Original file line numberDiff line numberDiff line change
@@ -1709,3 +1709,32 @@ function pg_escape_literal($string1, $string2 = null) {}
17091709
* @psalm-flow ($string1, $string2) -> return
17101710
*/
17111711
function pg_escape_string($string1, $string2 = null) {}
1712+
1713+
/**
1714+
* @psalm-template P of string
1715+
* @psalm-template F of int-mask<GLOB_MARK, GLOB_NOSORT, GLOB_NOCHECK, GLOB_NOESCAPE, GLOB_BRACE, GLOB_ONLYDIR, GLOB_ERR>
1716+
* @psalm-param P $pattern
1717+
* @psalm-param F $flags
1718+
* @psalm-return (
1719+
* P is ''
1720+
* ? (F is int-mask<GLOB_MARK, GLOB_NOSORT, GLOB_NOESCAPE, GLOB_BRACE, GLOB_ONLYDIR, GLOB_ERR>
1721+
* ? false|list<never>
1722+
* : (F is int-mask<GLOB_MARK, GLOB_NOSORT, GLOB_NOCHECK, GLOB_NOESCAPE, GLOB_BRACE, GLOB_ERR>
1723+
* ? false|list{0:''}
1724+
* : false|list<never>
1725+
* )
1726+
* )
1727+
* : (F is int-mask<GLOB_MARK, GLOB_NOSORT, GLOB_NOESCAPE, GLOB_BRACE, GLOB_ONLYDIR, GLOB_ERR>
1728+
* ? false|list<non-empty-string>
1729+
* : (F is int-mask<GLOB_MARK, GLOB_NOSORT, GLOB_NOCHECK, GLOB_NOESCAPE, GLOB_BRACE, GLOB_ERR>
1730+
* ? false|list{0:non-empty-string, ...<non-empty-string>}
1731+
* : false|list<non-empty-string>
1732+
* )
1733+
* )
1734+
* )
1735+
* @psalm-ignore-falsable-return
1736+
*/
1737+
function glob (string $pattern, int $flags = 0): array|false {}
1738+
1739+
1740+

tests/CoreStubsTest.php

+113
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,119 @@ function after_str_ends_with()
293293
echo str_contains($d, "psalm");
294294
',
295295
];
296+
yield 'glob return types' => [
297+
'code' => <<<'PHP'
298+
<?php
299+
/** @var int-mask<GLOB_NOCHECK> */
300+
$maybeNocheckFlag = 0;
301+
/** @var int-mask<GLOB_ONLYDIR> */
302+
$maybeOnlydirFlag = 0;
303+
304+
/** @var string */
305+
$string = '';
306+
307+
$emptyPatternNoFlags = glob( '' );
308+
$emptyPatternWithoutNocheckFlag1 = glob( '', GLOB_MARK );
309+
$emptyPatternWithoutNocheckFlag2 = glob( '' , GLOB_NOSORT | GLOB_NOESCAPE);
310+
$emptyPatternWithoutNocheckFlag3 = glob( '' , GLOB_MARK | GLOB_NOSORT | GLOB_NOESCAPE | GLOB_BRACE | GLOB_ONLYDIR | GLOB_ERR);
311+
$emptyPatternWithNocheckFlag1 = glob( '' , GLOB_NOCHECK);
312+
$emptyPatternWithNocheckFlag2 = glob( '' , GLOB_NOCHECK | GLOB_MARK);
313+
$emptyPatternWithNocheckFlag3 = glob( '' , GLOB_NOCHECK | GLOB_MARK | GLOB_NOSORT | GLOB_NOESCAPE | GLOB_BRACE | GLOB_ERR);
314+
$emptyPatternWithNocheckAndOnlydirFlag1 = glob( '' , GLOB_NOCHECK | GLOB_ONLYDIR);
315+
$emptyPatternWithNocheckAndOnlydirFlag2 = glob( '' , GLOB_NOCHECK | GLOB_ONLYDIR | GLOB_MARK);
316+
$emptyPatternWithNocheckAndOnlydirFlag3 = glob( '' , GLOB_NOCHECK | GLOB_ONLYDIR | GLOB_MARK | GLOB_NOSORT | GLOB_NOESCAPE | GLOB_BRACE | GLOB_ERR);
317+
$emptyPatternWithNocheckFlagAndMaybeOnlydir = glob( '' , GLOB_NOCHECK | $maybeOnlydirFlag);
318+
$emptyPatternMaybeWithNocheckFlag = glob( '' , $maybeNocheckFlag);
319+
$emptyPatternMaybeWithNocheckFlagAndOnlydir = glob( '' , $maybeNocheckFlag | GLOB_ONLYDIR);
320+
$emptyPatternMaybeWithNocheckFlagAndMaybeOnlydir = glob( '' , $maybeNocheckFlag | $maybeOnlydirFlag);
321+
322+
$nonEmptyPatternNoFlags = glob( 'pattern' );
323+
$nonEmptyPatternWithoutNocheckFlag1 = glob( 'pattern', GLOB_MARK );
324+
$nonEmptyPatternWithoutNocheckFlag2 = glob( 'pattern' , GLOB_NOSORT | GLOB_NOESCAPE);
325+
$nonEmptyPatternWithoutNocheckFlag3 = glob( 'pattern' , GLOB_MARK | GLOB_NOSORT | GLOB_NOESCAPE | GLOB_BRACE | GLOB_ONLYDIR | GLOB_ERR);
326+
$nonEmptyPatternWithNocheckFlag1 = glob( 'pattern' , GLOB_NOCHECK);
327+
$nonEmptyPatternWithNocheckFlag2 = glob( 'pattern' , GLOB_NOCHECK | GLOB_MARK);
328+
$nonEmptyPatternWithNocheckFlag3 = glob( 'pattern' , GLOB_NOCHECK | GLOB_MARK | GLOB_NOSORT | GLOB_NOESCAPE | GLOB_BRACE | GLOB_ERR);
329+
$nonEmptyPatternWithNocheckAndOnlydirFlag1 = glob( 'pattern' , GLOB_NOCHECK | GLOB_ONLYDIR);
330+
$nonEmptyPatternWithNocheckAndOnlydirFlag2 = glob( 'pattern' , GLOB_NOCHECK | GLOB_ONLYDIR | GLOB_MARK);
331+
$nonEmptyPatternWithNocheckAndOnlydirFlag3 = glob( 'pattern' , GLOB_NOCHECK | GLOB_ONLYDIR | GLOB_MARK | GLOB_NOSORT | GLOB_NOESCAPE | GLOB_BRACE | GLOB_ERR);
332+
$nonEmptyPatternWithNocheckFlagAndMaybeOnlydir = glob( 'pattern' , GLOB_NOCHECK | $maybeOnlydirFlag);
333+
$nonEmptyPatternMaybeWithNocheckFlag = glob( 'pattern' , $maybeNocheckFlag);
334+
$nonEmptyPatternMaybeWithNocheckFlagAndOnlydir = glob( 'pattern' , $maybeNocheckFlag | GLOB_ONLYDIR);
335+
$nonEmptyPatternMaybeWithNocheckFlagAndMaybeOnlydir = glob( 'pattern' , $maybeNocheckFlag | $maybeOnlydirFlag);
336+
337+
$stringPatternNoFlags = glob( $string );
338+
$stringPatternWithoutNocheckFlag1 = glob( $string, GLOB_MARK );
339+
$stringPatternWithoutNocheckFlag2 = glob( $string , GLOB_NOSORT | GLOB_NOESCAPE);
340+
$stringPatternWithoutNocheckFlag3 = glob( $string , GLOB_MARK | GLOB_NOSORT | GLOB_NOESCAPE | GLOB_BRACE | GLOB_ONLYDIR | GLOB_ERR);
341+
$stringPatternWithNocheckFlag1 = glob( $string , GLOB_NOCHECK);
342+
$stringPatternWithNocheckFlag2 = glob( $string , GLOB_NOCHECK | GLOB_MARK);
343+
$stringPatternWithNocheckFlag3 = glob( $string , GLOB_NOCHECK | GLOB_MARK | GLOB_NOSORT | GLOB_NOESCAPE | GLOB_BRACE | GLOB_ERR);
344+
$stringPatternWithNocheckAndOnlydirFlag1 = glob( $string , GLOB_NOCHECK | GLOB_ONLYDIR);
345+
$stringPatternWithNocheckAndOnlydirFlag2 = glob( $string , GLOB_NOCHECK | GLOB_ONLYDIR | GLOB_MARK);
346+
$stringPatternWithNocheckAndOnlydirFlag3 = glob( $string , GLOB_NOCHECK | GLOB_ONLYDIR | GLOB_MARK | GLOB_NOSORT | GLOB_NOESCAPE | GLOB_BRACE | GLOB_ERR);
347+
$stringPatternWithNocheckFlagAndMaybeOnlydir = glob( $string , GLOB_NOCHECK | $maybeOnlydirFlag);
348+
$stringPatternMaybeWithNocheckFlag = glob( $string , $maybeNocheckFlag);
349+
$stringPatternMaybeWithNocheckFlagAndOnlydir = glob( $string , $maybeNocheckFlag | GLOB_ONLYDIR);
350+
$stringPatternMaybeWithNocheckFlagAndMaybeOnlydir = glob( $string , $maybeNocheckFlag | $maybeOnlydirFlag);
351+
PHP,
352+
'assertions' => [
353+
'$emptyPatternNoFlags===' => 'false|list<never>',
354+
'$emptyPatternWithoutNocheckFlag1===' => 'false|list<never>',
355+
'$emptyPatternWithoutNocheckFlag2===' => 'false|list<never>',
356+
'$emptyPatternWithoutNocheckFlag3===' => 'false|list<never>',
357+
'$emptyPatternWithNocheckFlag1===' => 'false|list{\'\'}',
358+
'$emptyPatternWithNocheckFlag2===' => 'false|list{\'\'}',
359+
'$emptyPatternWithNocheckFlag3===' => 'false|list{\'\'}',
360+
'$emptyPatternWithNocheckAndOnlydirFlag1===' => 'false|list<never>',
361+
'$emptyPatternWithNocheckAndOnlydirFlag2===' => 'false|list<never>',
362+
'$emptyPatternWithNocheckAndOnlydirFlag3===' => 'false|list<never>',
363+
'$emptyPatternWithNocheckFlagAndMaybeOnlydir===' => 'false|list{0?: \'\', ...<never>}',
364+
'$emptyPatternMaybeWithNocheckFlag===' => 'false|list{0?: \'\', ...<never>}',
365+
'$emptyPatternMaybeWithNocheckFlagAndOnlydir===' => 'false|list<never>',
366+
'$emptyPatternMaybeWithNocheckFlagAndMaybeOnlydir===' => 'false|list{0?: \'\', ...<never>}',
367+
368+
'$nonEmptyPatternNoFlags===' => 'false|list<non-empty-string>',
369+
'$nonEmptyPatternWithoutNocheckFlag1===' => 'false|list<non-empty-string>',
370+
'$nonEmptyPatternWithoutNocheckFlag2===' => 'false|list<non-empty-string>',
371+
'$nonEmptyPatternWithoutNocheckFlag3===' => 'false|list<non-empty-string>',
372+
'$nonEmptyPatternWithNocheckFlag1===' => 'false|non-empty-list<non-empty-string>',
373+
'$nonEmptyPatternWithNocheckFlag2===' => 'false|non-empty-list<non-empty-string>',
374+
'$nonEmptyPatternWithNocheckFlag3===' => 'false|non-empty-list<non-empty-string>',
375+
'$nonEmptyPatternWithNocheckAndOnlydirFlag1===' => 'false|list<non-empty-string>',
376+
'$nonEmptyPatternWithNocheckAndOnlydirFlag2===' => 'false|list<non-empty-string>',
377+
'$nonEmptyPatternWithNocheckAndOnlydirFlag3===' => 'false|list<non-empty-string>',
378+
'$nonEmptyPatternWithNocheckFlagAndMaybeOnlydir===' => 'false|list<non-empty-string>',
379+
'$nonEmptyPatternMaybeWithNocheckFlag===' => 'false|list<non-empty-string>',
380+
'$nonEmptyPatternMaybeWithNocheckFlagAndOnlydir===' => 'false|list<non-empty-string>',
381+
'$nonEmptyPatternMaybeWithNocheckFlagAndMaybeOnlydir===' => 'false|list<non-empty-string>',
382+
383+
'$stringPatternNoFlags===' => 'false|list<non-empty-string>',
384+
'$stringPatternWithoutNocheckFlag1===' => 'false|list<non-empty-string>',
385+
'$stringPatternWithoutNocheckFlag2===' => 'false|list<non-empty-string>',
386+
'$stringPatternWithoutNocheckFlag3===' => 'false|list<non-empty-string>',
387+
'$stringPatternWithNocheckFlag1===' => 'false|list{string, ...<non-empty-string>}',
388+
'$stringPatternWithNocheckFlag2===' => 'false|list{string, ...<non-empty-string>}',
389+
'$stringPatternWithNocheckFlag3===' => 'false|list{string, ...<non-empty-string>}',
390+
'$stringPatternWithNocheckAndOnlydirFlag1===' => 'false|list<non-empty-string>',
391+
'$stringPatternWithNocheckAndOnlydirFlag2===' => 'false|list<non-empty-string>',
392+
'$stringPatternWithNocheckAndOnlydirFlag3===' => 'false|list<non-empty-string>',
393+
'$stringPatternWithNocheckFlagAndMaybeOnlydir===' => 'false|list{0?: string, ...<non-empty-string>}',
394+
'$stringPatternMaybeWithNocheckFlag===' => 'false|list{0?: string, ...<non-empty-string>}',
395+
'$stringPatternMaybeWithNocheckFlagAndOnlydir===' => 'false|list<non-empty-string>',
396+
'$stringPatternMaybeWithNocheckFlagAndMaybeOnlydir===' => 'false|list{0?: string, ...<non-empty-string>}',
397+
],
398+
];
399+
yield 'glob return ignores false' => [
400+
'code' => <<<'PHP'
401+
<?php
402+
/**
403+
* @param list $list
404+
*/
405+
function takesList(array $list): void {}
406+
takesList(glob( '' ));
407+
PHP,
408+
];
296409
}
297410

298411
public function providerInvalidCodeParse(): iterable

0 commit comments

Comments
 (0)