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