6
6
7
7
require 'vendor/autoload.php ' ;
8
8
9
+ require __DIR__ . '/gen_callmap_utils.php ' ;
10
+
9
11
use DG \BypassFinals ;
10
12
use Psalm \Internal \Analyzer \ProjectAnalyzer ;
11
- use Psalm \Internal \Codebase \Reflection ;
12
13
use Psalm \Internal \Provider \FileProvider ;
13
14
use Psalm \Internal \Provider \Providers ;
14
- use Psalm \Internal \Type \Comparator \UnionTypeComparator ;
15
15
use Psalm \Tests \TestConfig ;
16
- use Psalm \Type ;
17
- use Psalm \Type \Atomic \TNull ;
18
-
19
- /**
20
- * Returns the correct reflection type for function or method name.
21
- */
22
- function getReflectionFunction (string $ functionName ): ?ReflectionFunctionAbstract
23
- {
24
- try {
25
- if (strpos ($ functionName , ':: ' ) !== false ) {
26
- if (PHP_VERSION_ID < 8_03_00 ) {
27
- return new ReflectionMethod ($ functionName );
28
- }
29
-
30
- return ReflectionMethod::createFromMethodName ($ functionName );
31
- }
32
-
33
- /** @var callable-string $functionName */
34
- return new ReflectionFunction ($ functionName );
35
- } catch (ReflectionException $ e ) {
36
- return null ;
37
- }
38
- }
39
-
40
- /**
41
- * @param array<string, string> $entryParameters
42
- */
43
- function assertEntryParameters (ReflectionFunctionAbstract $ function , array &$ entryParameters ): void
44
- {
45
- assertEntryReturnType ($ function , $ entryParameters [0 ]);
46
- /**
47
- * Parse the parameter names from the map.
48
- *
49
- * @var array<string, array{byRef: bool, refMode: 'rw'|'w'|'r', variadic: bool, optional: bool, type: string}>
50
- */
51
- $ normalizedEntries = [];
52
-
53
- foreach ($ entryParameters as $ key => &$ entry ) {
54
- if ($ key === 0 ) {
55
- continue ;
56
- }
57
- $ normalizedKey = $ key ;
58
- /**
59
- * @var array{byRef: bool, refMode: 'rw'|'w'|'r', variadic: bool, optional: bool, type: string} $normalizedEntry
60
- */
61
- $ normalizedEntry = [
62
- 'variadic ' => false ,
63
- 'byRef ' => false ,
64
- 'optional ' => false ,
65
- 'type ' => &$ entry ,
66
- ];
67
- if (strncmp ($ normalizedKey , '& ' , 1 ) === 0 ) {
68
- $ normalizedEntry ['byRef ' ] = true ;
69
- $ normalizedKey = substr ($ normalizedKey , 1 );
70
- }
71
-
72
- if (strncmp ($ normalizedKey , '... ' , 3 ) === 0 ) {
73
- $ normalizedEntry ['variadic ' ] = true ;
74
- $ normalizedKey = substr ($ normalizedKey , 3 );
75
- }
76
-
77
- // Read the reference mode
78
- if ($ normalizedEntry ['byRef ' ]) {
79
- $ parts = explode ('_ ' , $ normalizedKey , 2 );
80
- if (count ($ parts ) === 2 ) {
81
- if (!($ parts [0 ] === 'rw ' || $ parts [0 ] === 'w ' || $ parts [0 ] === 'r ' )) {
82
- throw new InvalidArgumentException ('Invalid refMode: ' .$ parts [0 ]);
83
- }
84
- $ normalizedEntry ['refMode ' ] = $ parts [0 ];
85
- $ normalizedKey = $ parts [1 ];
86
- } else {
87
- $ normalizedEntry ['refMode ' ] = 'rw ' ;
88
- }
89
- }
90
-
91
- // Strip prefixes.
92
- if (substr ($ normalizedKey , -1 , 1 ) === "= " ) {
93
- $ normalizedEntry ['optional ' ] = true ;
94
- $ normalizedKey = substr ($ normalizedKey , 0 , -1 );
95
- }
96
-
97
- $ normalizedEntry ['name ' ] = $ normalizedKey ;
98
- $ normalizedEntries [$ normalizedKey ] = $ normalizedEntry ;
99
- }
100
-
101
- foreach ($ function ->getParameters () as $ parameter ) {
102
- if (isset ($ normalizedEntries [$ parameter ->getName ()])) {
103
- assertParameter ($ normalizedEntries [$ parameter ->getName ()], $ parameter );
104
- }
105
- }
106
- }
107
-
108
- /**
109
- * @param array{byRef: bool, name?: string, refMode: 'rw'|'w'|'r', variadic: bool, optional: bool, type: string} $normalizedEntry
110
- */
111
- function assertParameter (array &$ normalizedEntry , ReflectionParameter $ param ): void
112
- {
113
- $ name = $ param ->getName ();
114
-
115
- $ expectedType = $ param ->getType ();
116
-
117
- if (isset ($ expectedType ) && !empty ($ normalizedEntry ['type ' ])) {
118
- $ func = $ param ->getDeclaringFunction ()->getName ();
119
- assertTypeValidity ($ expectedType , $ normalizedEntry ['type ' ], "Param $ func ' {$ name }' " );
120
- }
121
- }
122
-
123
- function assertEntryReturnType (ReflectionFunctionAbstract $ function , string &$ entryReturnType ): void
124
- {
125
- if (version_compare (PHP_VERSION , '8.1.0 ' , '>= ' )) {
126
- $ expectedType = $ function ->hasTentativeReturnType () ? $ function ->getTentativeReturnType () : $ function ->getReturnType ();
127
- } else {
128
- $ expectedType = $ function ->getReturnType ();
129
- }
130
-
131
- if ($ expectedType !== null ) {
132
- assertTypeValidity ($ expectedType , $ entryReturnType , 'Return ' );
133
- }
134
- }
135
-
136
- /**
137
- * Since string equality is too strict, we do some extra checking here
138
- */
139
- function assertTypeValidity (ReflectionType $ reflected , string &$ specified , string $ msgPrefix ): void
140
- {
141
- $ expectedType = Reflection::getPsalmTypeFromReflectionType ($ reflected );
142
- $ callMapType = Type::parseString ($ specified === '' ? 'mixed ' : $ specified );
143
-
144
- $ codebase = ProjectAnalyzer::getInstance ()->getCodebase ();
145
- try {
146
- if (!UnionTypeComparator::isContainedBy ($ codebase , $ callMapType , $ expectedType , false , false , null , false , false ) && !str_contains ($ specified , 'static ' )) {
147
- $ specified = $ expectedType ->getId (true );
148
- $ callMapType = $ expectedType ;
149
- }
150
- } catch (Throwable ) {
151
- }
152
-
153
- if ($ expectedType ->hasMixed ()) {
154
- return ;
155
- }
156
- $ callMapType = $ callMapType ->getBuilder ();
157
- if ($ expectedType ->isNullable () !== $ callMapType ->isNullable ()) {
158
- if ($ expectedType ->isNullable ()) {
159
- $ callMapType ->addType (new TNull ());
160
- } else {
161
- $ callMapType ->removeType ('null ' );
162
- }
163
- }
164
- $ specified = $ callMapType ->getId (true );
165
- // //$this->assertSame($expectedType->hasBool(), $callMapType->hasBool(), "{$msgPrefix} type '{$specified}' missing bool from reflected type '{$reflected}'");
166
- // $this->assertSame($expectedType->hasArray(), $callMapType->hasArray(), "{$msgPrefix} type '{$specified}' missing array from reflected type '{$reflected}'");
167
- // $this->assertSame($expectedType->hasInt(), $callMapType->hasInt(), "{$msgPrefix} type '{$specified}' missing int from reflected type '{$reflected}'");
168
- // $this->assertSame($expectedType->hasFloat(), $callMapType->hasFloat(), "{$msgPrefix} type '{$specified}' missing float from reflected type '{$reflected}'");
169
- }
170
16
171
17
BypassFinals::enable ();
172
18
173
- function writeCallMap (string $ file , array $ callMap ) {
19
+ function writeCallMap (string $ file , array $ callMap ): void
20
+ {
174
21
file_put_contents ($ file , '<?php // phpcs:ignoreFile
175
22
176
23
return ' .var_export ($ callMap , true ).'; ' );
@@ -203,4 +50,4 @@ function writeCallMap(string $file, array $callMap) {
203
50
}
204
51
}
205
52
206
- writeCallMap ($ diffFile , $ diff );
53
+ writeCallMap ($ diffFile , $ diff );
0 commit comments