packages/deriver/tests/Integration/NativeOverloadContractTest.php
1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Integration;
6
7use Deriver\Deriver\Contributions;
8use Deriver\Deriver\DeriverConfig;
9use Deriver\Deriver\Evaluator\Call\Evaluators;
10use Deriver\Deriver\Evaluator\Call\Registrations;
11use Deriver\Deriver\Evaluator\EvaluatorConfig;
12use Deriver\Deriver\Evaluator\Evidence\Frontier;
13use Deriver\Deriver\Evaluator\Execution\Completion\Quality\Coverage;
14use Deriver\Deriver\Evaluator\Execution\EvaluatorConfig as ExecutionEvaluatorConfig;
15use Deriver\Deriver\Evaluator\Request\ReturnQuery;
16use Deriver\Deriver\Evaluator\TargetProfile;
17use Deriver\Deriver\Evaluator\Term;
18use Deriver\Deriver\Evaluator\TermKind;
19use Deriver\Deriver\Execution\QueryExecution;
20use Deriver\Deriver\Lowerer\Declaration\Index;
21use Deriver\Deriver\MicroEvaluatorConversion;
22use Deriver\Deriver\Parser\Node\File;
23use Deriver\Deriver\Parser\Parser;
24use Deriver\Deriver\Parser\ParserConfig;
25use Deriver\Deriver\Providers\ProviderExpansion;
26use Deriver\Deriver\Resolver\Resolver;
27use Deriver\Deriver\SnapshotIdentity;
28use Deriver\Deriver\Sources\Path;
29use Deriver\Deriver\Sources\Source;
30use Deriver\Deriver\Sources\Sources;
31use PHPUnit\Framework\Attributes\CoversNothing;
32use PHPUnit\Framework\Attributes\Small;
33use PHPUnit\Framework\TestCase;
34
35#[CoversNothing]
36#[Small]
37final class NativeOverloadContractTest extends TestCase
38{
39 public function testInternalNullCoercionRetainsTheTargetDeprecation(): void
40 {
41 $source = '<?php function target(){return [strlen(null), in_array(1,[1],null)];}';
42 $symbol = 'target';
43 $input = new Sources([new Source('fixture.php', $source)]);
44 $configuration = new DeriverConfig();
45 $contributions = new Contributions();
46 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
47 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
48 $contributions1 = $providers->contributions;
49 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
50 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
51 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
52 $snapshotId = $identity->id;
53 $input1 = $providers->input;
54 $parser = new Parser(new ParserConfig($target->version));
55 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
56 $program = new Index($snapshotId, $trees);
57 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
58 self::assertSame([0, true], $result->normalOutcomes[0]->values['return']->native());
59 self::assertSame([], $result->exceptionalOutcomes);
60 self::assertSame(['PHP_WARNING', 'PHP_WARNING'], array_column($result->frontiers, 'code'));
61 }
62 public function testUnsupportedCallableOverloadsRetainNormalValuesAndReferenceEffects(): void
63 {
64 $source = '<?php function target(){$name="old"; $result=is_callable("strlen",true,$name);return [$result,$name];}';
65 $symbol = 'target';
66 $input = new Sources([new Source('fixture.php', $source)]);
67 $configuration = new DeriverConfig();
68 $contributions = new Contributions();
69 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
70 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
71 $contributions1 = $providers->contributions;
72 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
73 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
74 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
75 $snapshotId = $identity->id;
76 $input1 = $providers->input;
77 $parser = new Parser(new ParserConfig($target->version));
78 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
79 $program = new Index($snapshotId, $trees);
80 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
81 self::assertSame(TermKind::Opaque, $result->normalOutcomes[0]->values['return']->operands[0]->kind);
82 self::assertSame(TermKind::Opaque, $result->normalOutcomes[0]->values['return']->operands[1]->kind);
83 self::assertContains('UNSUPPORTED_EVALUATOR_CASE', array_column($result->frontiers, 'code'));
84 }
85 public function testWeakObjectCoercionReportsItsEffectsAndThrowableBoundary(): void
86 {
87 $source = '<?php class B{function __toString():string{return "value";}}function pass(string $value){return $value;}function target(){return pass(new B);}';
88 $symbol = 'target';
89 $input = new Sources([new Source('fixture.php', $source)]);
90 $configuration = new DeriverConfig();
91 $contributions = new Contributions();
92 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
93 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
94 $contributions1 = $providers->contributions;
95 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
96 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
97 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
98 $snapshotId = $identity->id;
99 $input1 = $providers->input;
100 $parser = new Parser(new ParserConfig($target->version));
101 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
102 $program = new Index($snapshotId, $trees);
103 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
104 self::assertNotEmpty($result->normalOutcomes);
105 self::assertContains('UNSUPPORTED_LANGUAGE_FEATURE', array_column($result->frontiers, 'code'));
106 self::assertSame(Coverage::Unavailable, $result->assessment->coverage);
107 self::assertContains('Throwable', array_map(static fn ($outcome) => $outcome->exception->literal, $result->exceptionalOutcomes));
108 }
109 public function testAllocationWithADestructorCannotLeaveAnExactKnownReturn(): void
110 {
111 $source = '<?php class B{function __destruct(){}}function target(){new B;return 4;}';
112 $symbol = 'target';
113 $input = new Sources([new Source('fixture.php', $source)]);
114 $configuration = new DeriverConfig();
115 $contributions = new Contributions();
116 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
117 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
118 $contributions1 = $providers->contributions;
119 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
120 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
121 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
122 $snapshotId = $identity->id;
123 $input1 = $providers->input;
124 $parser = new Parser(new ParserConfig($target->version));
125 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
126 $program = new Index($snapshotId, $trees);
127 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
128 self::assertContains('destructor-lifetime', array_column($result->frontiers, 'operation'));
129 self::assertSame(Coverage::Unavailable, $result->assessment->coverage);
130 self::assertNotSame(TermKind::Constant, $result->normalOutcomes[0]->values['return']->kind);
131 }
132 public function testUnknownCallsCannotRestoreAnUnseenStaticDefault(): void
133 {
134 $source = '<?php class B{public static int $value=4;}function target(){unknown();return B::$value;}';
135 $symbol = 'target';
136 $input = new Sources([new Source('fixture.php', $source)]);
137 $configuration = new DeriverConfig();
138 $contributions = new Contributions();
139 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
140 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
141 $contributions1 = $providers->contributions;
142 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
143 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
144 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
145 $snapshotId = $identity->id;
146 $input1 = $providers->input;
147 $parser = new Parser(new ParserConfig($target->version));
148 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
149 $program = new Index($snapshotId, $trees);
150 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
151 self::assertNotSame(TermKind::Constant, $result->normalOutcomes[0]->values['return']->kind);
152 self::assertNotEmpty($result->exceptionalOutcomes);
153 }
154 public function testUnknownFilterModesKeepTheirCallbackEffectsUnresolved(): void
155 {
156 $source = '<?php function target(int $mode){return array_filter([1], fn($x)=>true, $mode);}';
157 $symbol = 'target';
158 $input = new Sources([new Source('fixture.php', $source)]);
159 $configuration = new DeriverConfig();
160 $contributions = new Contributions();
161 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
162 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
163 $contributions1 = $providers->contributions;
164 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
165 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
166 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
167 $snapshotId = $identity->id;
168 $input1 = $providers->input;
169 $parser = new Parser(new ParserConfig($target->version));
170 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
171 $program = new Index($snapshotId, $trees);
172 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
173 self::assertContains('UNSUPPORTED_EVALUATOR_CASE', array_column($result->frontiers, 'code'));
174 self::assertSame(TermKind::Opaque, $result->normalOutcomes[0]->values['return']->kind);
175 self::assertNotEmpty($result->exceptionalOutcomes);
176 }
177 public function testImplicitConversionsInsideStandardModelsRetainPossibleThrowables(): void
178 {
179 $source = '<?php class B{function __toString():string{throw new RuntimeException;}}function target(){return implode(",",[new B]);}';
180 $symbol = 'target';
181 $input = new Sources([new Source('fixture.php', $source)]);
182 $configuration = new DeriverConfig();
183 $contributions = new Contributions();
184 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
185 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
186 $contributions1 = $providers->contributions;
187 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
188 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
189 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
190 $snapshotId = $identity->id;
191 $input1 = $providers->input;
192 $parser = new Parser(new ParserConfig($target->version));
193 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
194 $program = new Index($snapshotId, $trees);
195 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
196 self::assertSame([], $result->frontiers);
197 self::assertSame([], $result->normalOutcomes);
198 self::assertNotEmpty($result->exceptionalOutcomes);
199 }
200 public function testFormattingCannotTreatAnUnconstrainedConversionAsPure(): void
201 {
202 $source = '<?php function target($value){return sprintf("%s",$value);}';
203 $symbol = 'target';
204 $input = new Sources([new Source('fixture.php', $source)]);
205 $configuration = new DeriverConfig();
206 $contributions = new Contributions();
207 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
208 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
209 $contributions1 = $providers->contributions;
210 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
211 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
212 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
213 $snapshotId = $identity->id;
214 $input1 = $providers->input;
215 $parser = new Parser(new ParserConfig($target->version));
216 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
217 $program = new Index($snapshotId, $trees);
218 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
219 self::assertContains('UNSUPPORTED_EVALUATOR_CASE', array_column($result->frontiers, 'code'));
220 self::assertNotEmpty($result->exceptionalOutcomes);
221 }
222 public function testUnknownSeparatorRetainsTheEmptySeparatorException(): void
223 {
224 $source = '<?php function target(string $separator){return explode($separator,"a,b");}';
225 $symbol = 'target';
226 $input = new Sources([new Source('fixture.php', $source)]);
227 $configuration = new DeriverConfig();
228 $contributions = new Contributions();
229 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
230 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
231 $contributions1 = $providers->contributions;
232 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
233 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
234 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
235 $snapshotId = $identity->id;
236 $input1 = $providers->input;
237 $parser = new Parser(new ParserConfig($target->version));
238 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
239 $program = new Index($snapshotId, $trees);
240 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
241 self::assertContains('UNSUPPORTED_EVALUATOR_CASE', array_column($result->frontiers, 'code'));
242 self::assertNotEmpty($result->normalOutcomes);
243 self::assertNotEmpty($result->exceptionalOutcomes);
244 }
245 public function testUnknownJoinElementsCannotSuppressStringConversionEffects(): void
246 {
247 $source = '<?php function target(array $values){return implode(",",$values);}';
248 $symbol = 'target';
249 $input = new Sources([new Source('fixture.php', $source)]);
250 $configuration = new DeriverConfig();
251 $contributions = new Contributions();
252 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
253 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
254 $contributions1 = $providers->contributions;
255 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
256 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
257 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
258 $snapshotId = $identity->id;
259 $input1 = $providers->input;
260 $parser = new Parser(new ParserConfig($target->version));
261 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
262 $program = new Index($snapshotId, $trees);
263 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
264 self::assertContains('UNSUPPORTED_EVALUATOR_CASE', array_column($result->frontiers, 'code'));
265 self::assertSame(TermKind::Opaque, $result->normalOutcomes[0]->values['return']->kind);
266 self::assertNotEmpty($result->exceptionalOutcomes);
267 }
268 public function testMalformedTrimRangesDoNotEmitWarningsFromTheAnalyzer(): void
269 {
270 $source = '<?php function target(){return trim("abc","z..a");}';
271 $symbol = 'target';
272 $input = new Sources([new Source('fixture.php', $source)]);
273 $configuration = new DeriverConfig();
274 $contributions = new Contributions();
275 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
276 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
277 $contributions1 = $providers->contributions;
278 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
279 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
280 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
281 $snapshotId = $identity->id;
282 $input1 = $providers->input;
283 $parser = new Parser(new ParserConfig($target->version));
284 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
285 $program = new Index($snapshotId, $trees);
286 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
287 self::assertContains('UNSUPPORTED_EVALUATOR_CASE', array_column($result->frontiers, 'code'));
288 self::assertNotEmpty($result->normalOutcomes);
289 self::assertNotEmpty($result->exceptionalOutcomes);
290 }
291 public function testUnknownMergeCannotKeepAnOverwrittenFieldConstant(): void
292 {
293 $source = '<?php function target(array $input){return array_merge(["a"=>1],$input)["a"];}';
294 $symbol = 'target';
295 $input = new Sources([new Source('fixture.php', $source)]);
296 $configuration = new DeriverConfig();
297 $contributions = new Contributions();
298 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
299 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
300 $contributions1 = $providers->contributions;
301 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
302 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
303 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
304 $snapshotId = $identity->id;
305 $input1 = $providers->input;
306 $parser = new Parser(new ParserConfig($target->version));
307 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
308 $program = new Index($snapshotId, $trees);
309 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
310 self::assertNotSame(TermKind::Constant, $result->normalOutcomes[0]->values['return']->kind);
311 }
312 public function testUnknownUnpackRetainsItsPrefixAndSubsequentWritesSymbolically(): void
313 {
314 $source = '<?php function target(array $input){return ["a"=>1,...$input,"b"=>2];}';
315 $symbol = 'target';
316 $input = new Sources([new Source('fixture.php', $source)]);
317 $configuration = new DeriverConfig();
318 $contributions = new Contributions();
319 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
320 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
321 $contributions1 = $providers->contributions;
322 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
323 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
324 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
325 $snapshotId = $identity->id;
326 $input1 = $providers->input;
327 $parser = new Parser(new ParserConfig($target->version));
328 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
329 $program = new Index($snapshotId, $trees);
330 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
331 self::assertSame(TermKind::ArraySet, $result->normalOutcomes[0]->values['return']->kind);
332 self::assertSame(TermKind::ArrayMerge, $result->normalOutcomes[0]->values['return']->operands[0]->kind);
333 self::assertSame([], $result->exceptionalOutcomes);
334 self::assertSame([], $result->frontiers);
335 }
336 public function testUnknownUnpackAfterTheMaximumIndexKeepsTheOccupiedIndexError(): void
337 {
338 $source = '<?php function target(array $input){return [PHP_INT_MAX=>1,...$input];}';
339 $symbol = 'target';
340 $input = new Sources([new Source('fixture.php', $source)]);
341 $configuration = new DeriverConfig();
342 $contributions = new Contributions();
343 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
344 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
345 $contributions1 = $providers->contributions;
346 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
347 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
348 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
349 $snapshotId = $identity->id;
350 $input1 = $providers->input;
351 $parser = new Parser(new ParserConfig($target->version));
352 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
353 $program = new Index($snapshotId, $trees);
354 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
355 self::assertSame(TermKind::ArrayMerge, $result->normalOutcomes[0]->values['return']->kind);
356 self::assertSame('Error', $result->exceptionalOutcomes[0]->exception->literal);
357 self::assertContains('WIDENED', array_column($result->frontiers, 'code'));
358 }
359 public function testSymbolicCountableKeepsUserCodeEffectsAndThrowables(): void
360 {
361 $source = '<?php function target(Countable $input){return count($input);}';
362 $symbol = 'target';
363 $input = new Sources([new Source('fixture.php', $source)]);
364 $configuration = new DeriverConfig();
365 $contributions = new Contributions();
366 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
367 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
368 $contributions1 = $providers->contributions;
369 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
370 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
371 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
372 $snapshotId = $identity->id;
373 $input1 = $providers->input;
374 $parser = new Parser(new ParserConfig($target->version));
375 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
376 $program = new Index($snapshotId, $trees);
377 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
378 self::assertContains('UNSUPPORTED_EVALUATOR_CASE', array_column($result->frontiers, 'code'));
379 self::assertNotEmpty($result->exceptionalOutcomes);
380 }
381}
382