packages/deriver/tests/Unit/Deriver/Evaluator/Execution/Offset/ReaderTest.php
1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Unit\Deriver\Evaluator\Execution\Offset;
6
7use Deriver\Deriver\Contributions;
8use Deriver\Deriver\DeriverConfig;
9use Deriver\Deriver\Evaluator\Call\BoundInstruction;
10use Deriver\Deriver\Evaluator\Call\Evaluators;
11use Deriver\Deriver\Evaluator\Call\Extensions;
12use Deriver\Deriver\Evaluator\Call\Registrations;
13use Deriver\Deriver\Evaluator\Call\StateRegistry;
14use Deriver\Deriver\Evaluator\EvaluatorConfig;
15use Deriver\Deriver\Evaluator\Evidence\Frontier;
16use Deriver\Deriver\Evaluator\Execution\Completion\Completion;
17use Deriver\Deriver\Evaluator\Execution\Context;
18use Deriver\Deriver\Evaluator\Execution\EvaluatorConfig as ExecutionEvaluatorConfig;
19use Deriver\Deriver\Evaluator\Execution\Memory\Memory;
20use Deriver\Deriver\Evaluator\Execution\Offset\Reader;
21use Deriver\Deriver\Evaluator\Execution\Offset\Strings;
22use Deriver\Deriver\Evaluator\Execution\State;
23use Deriver\Deriver\Evaluator\Request\Budget;
24use Deriver\Deriver\Evaluator\Request\QueryScope;
25use Deriver\Deriver\Evaluator\Request\ReturnQuery;
26use Deriver\Deriver\Evaluator\Scalar\Arrays;
27use Deriver\Deriver\Evaluator\Scalar\IntegerConversion;
28use Deriver\Deriver\Evaluator\Scalar\Operations;
29use Deriver\Deriver\Evaluator\TargetProfile;
30use Deriver\Deriver\Evaluator\Term;
31use Deriver\Deriver\Evaluator\TermKind;
32use Deriver\Deriver\Execution\QueryExecution;
33use Deriver\Deriver\Lowerer\Declaration\CallableIdentity;
34use Deriver\Deriver\Lowerer\Declaration\CallableSource;
35use Deriver\Deriver\Lowerer\Declaration\ConstantSignatures;
36use Deriver\Deriver\Lowerer\Declaration\Index;
37use Deriver\Deriver\Lowerer\Declaration\Indexer;
38use Deriver\Deriver\Lowerer\Declaration\Metadata\ClassDeclaration;
39use Deriver\Deriver\Lowerer\Declaration\Traits\Composition;
40use Deriver\Deriver\Lowerer\ExpressionLowering;
41use Deriver\Deriver\Lowerer\Graph\BasicBlock;
42use Deriver\Deriver\Lowerer\Graph\CallableGraph;
43use Deriver\Deriver\Lowerer\Graph\Instruction;
44use Deriver\Deriver\Lowerer\Graph\Terminator;
45use Deriver\Deriver\Lowerer\Hierarchy\Builtins;
46use Deriver\Deriver\Lowerer\Hierarchy\Dispatch;
47use Deriver\Deriver\Lowerer\InstructionEmitter;
48use Deriver\Deriver\Lowerer\Lowerer;
49use Deriver\Deriver\Lowerer\LoweringScope;
50use Deriver\Deriver\Lowerer\StatementLowering;
51use Deriver\Deriver\MicroEvaluatorConversion;
52use Deriver\Deriver\Parser\Node\File;
53use Deriver\Deriver\Parser\Parser;
54use Deriver\Deriver\Parser\ParserConfig;
55use Deriver\Deriver\Parser\TargetSyntax;
56use Deriver\Deriver\Providers\ProviderExpansion;
57use Deriver\Deriver\Resolver\AssignmentPatterns;
58use Deriver\Deriver\Resolver\ClassScope;
59use Deriver\Deriver\Resolver\ExpressionValidation;
60use Deriver\Deriver\Resolver\MagicContext;
61use Deriver\Deriver\Resolver\Resolver;
62use Deriver\Deriver\SnapshotIdentity;
63use Deriver\Deriver\Sources\LineMap;
64use Deriver\Deriver\Sources\Path;
65use Deriver\Deriver\Sources\Source;
66use Deriver\Deriver\Sources\SourceRef;
67use Deriver\Deriver\Sources\Sources;
68use Deriver\Exception\ParserException;
69use Deriver\Exception\ResolutionException;
70use PHPUnit\Framework\Attributes\CoversClass;
71use PHPUnit\Framework\Attributes\DataProvider;
72use PHPUnit\Framework\Attributes\Small;
73use PHPUnit\Framework\Attributes\UsesClass;
74use PHPUnit\Framework\TestCase;
75
76/**
77 * @covers \Deriver\Deriver\Evaluator\Execution\Offset\Reader
78 */
79#[UsesClass(Resolver::class)]
80#[UsesClass(ExpressionValidation::class)]
81#[UsesClass(ResolutionException::class)]
82#[UsesClass(ParserConfig::class)]
83#[UsesClass(ParserException::class)]
84#[CoversClass(Reader::class)]
85#[UsesClass(BasicBlock::class)]
86#[UsesClass(CallableGraph::class)]
87#[UsesClass(CallableIdentity::class)]
88#[UsesClass(ClassDeclaration::class)]
89#[UsesClass(Instruction::class)]
90#[UsesClass(Terminator::class)]
91#[UsesClass(Builtins::class)]
92#[UsesClass(Dispatch::class)]
93#[UsesClass(Completion::class)]
94#[UsesClass(Context::class)]
95#[UsesClass(Strings::class)]
96#[UsesClass(State::class)]
97#[UsesClass(Memory::class)]
98#[UsesClass(Extensions::class)]
99#[UsesClass(Evaluators::class)]
100#[UsesClass(StateRegistry::class)]
101#[UsesClass(DeriverConfig::class)]
102#[UsesClass(Sources::class)]
103#[UsesClass(Path::class)]
104#[UsesClass(Source::class)]
105#[UsesClass(TargetProfile::class)]
106#[UsesClass(Budget::class)]
107#[UsesClass(QueryScope::class)]
108#[UsesClass(ReturnQuery::class)]
109#[UsesClass(SourceRef::class)]
110#[UsesClass(Frontier::class)]
111#[UsesClass(Parser::class)]
112#[UsesClass(File::class)]
113#[UsesClass(Lowerer::class)]
114#[UsesClass(ExpressionLowering::class)]
115#[UsesClass(InstructionEmitter::class)]
116#[UsesClass(LoweringScope::class)]
117#[UsesClass(StatementLowering::class)]
118#[UsesClass(ConstantSignatures::class)]
119#[UsesClass(CallableSource::class)]
120#[UsesClass(Indexer::class)]
121#[UsesClass(Index::class)]
122#[UsesClass(Composition::class)]
123#[UsesClass(LineMap::class)]
124#[UsesClass(MagicContext::class)]
125#[UsesClass(AssignmentPatterns::class)]
126#[UsesClass(ClassScope::class)]
127#[UsesClass(TargetSyntax::class)]
128#[UsesClass(Arrays::class)]
129#[UsesClass(IntegerConversion::class)]
130#[UsesClass(Operations::class)]
131#[UsesClass(Term::class)]
132#[Small]
133final class ReaderTest extends TestCase
134{
135 public function testReadScalarOffsetsReturnNullWithAWarning(): void
136 {
137 $source = '<?php function target(){return 1;}';
138 $budget = new Budget();
139 $configuration = new DeriverConfig();
140 $contributions = new Contributions();
141 $input = new Sources([new Source('fixture.php', $source)]);
142 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
143 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
144 $contributions1 = $providers->contributions;
145 $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);
146 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
147 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
148 $snapshotId = $identity->id;
149 $input1 = $providers->input;
150 $parser = new Parser(new ParserConfig($target->version));
151 $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));
152 $program1 = new Index($snapshotId, $trees);
153 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
154 $snapshotId1 = 'test';
155 $input2 = new Sources([new Source('fixture.php', $source)]);
156 $profile = $execution->configuration->target;
157 $parser1 = new Parser(new ParserConfig($profile->version));
158 $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
159 $program = new Index($snapshotId1, $trees1);
160 $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
161 $body = $context->program->callable('target');
162 self::assertNotNull($body);
163 $instruction = new BoundInstruction('offset', 'write', $body->source, 'result', ['slot', 'rhs']);
164 $reader = new Reader($context);
165 self::assertNull($reader->read(Term::constant(4), Term::array([]), $instruction, new State())->native());
166 self::assertSame(['PHP_WARNING'], array_column(array_values($context->frontiers), 'code'));
167 }
168 public function testArrayRejectsIllegalKeysEvenInExistenceChecks(): void
169 {
170 $source = '<?php function target(){return 1;}';
171 $budget = new Budget();
172 $configuration = new DeriverConfig();
173 $contributions = new Contributions();
174 $input = new Sources([new Source('fixture.php', $source)]);
175 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
176 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
177 $contributions1 = $providers->contributions;
178 $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);
179 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
180 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
181 $snapshotId = $identity->id;
182 $input1 = $providers->input;
183 $parser = new Parser(new ParserConfig($target->version));
184 $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));
185 $program1 = new Index($snapshotId, $trees);
186 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
187 $snapshotId1 = 'test';
188 $input2 = new Sources([new Source('fixture.php', $source)]);
189 $profile = $execution->configuration->target;
190 $parser1 = new Parser(new ParserConfig($profile->version));
191 $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
192 $program = new Index($snapshotId1, $trees1);
193 $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
194 $body = $context->program->callable('target');
195 self::assertNotNull($body);
196 $instruction = new BoundInstruction('offset', 'write', $body->source, 'result', ['slot', 'rhs']);
197 $reader = new Reader($context);
198 self::assertSame('TypeError', $reader->array(Term::array([]), Term::array([]), $instruction, new State(), true)->literal);
199 self::assertSame(TermKind::Uninitialized, $reader->array(Term::array([]), Term::constant('absent'), $instruction, new State(), true)->kind);
200 self::assertSame([], $context->frontiers);
201 }
202 public function testArrayReadsOnlyIntegerHeadKeysOfAnUnknownMergeExactly(): void
203 {
204 $source = '<?php function target(){return 1;}';
205 $budget = new Budget();
206 $configuration = new DeriverConfig();
207 $contributions = new Contributions();
208 $input = new Sources([new Source('fixture.php', $source)]);
209 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
210 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
211 $contributions1 = $providers->contributions;
212 $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);
213 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
214 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
215 $snapshotId = $identity->id;
216 $input1 = $providers->input;
217 $parser = new Parser(new ParserConfig($target->version));
218 $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));
219 $program1 = new Index($snapshotId, $trees);
220 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
221 $snapshotId1 = 'test';
222 $input2 = new Sources([new Source('fixture.php', $source)]);
223 $profile = $execution->configuration->target;
224 $parser1 = new Parser(new ParserConfig($profile->version));
225 $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
226 $program = new Index($snapshotId1, $trees1);
227 $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
228 $body = $context->program->callable('target');
229 self::assertNotNull($body);
230 $instruction = new BoundInstruction('offset', 'read', $body->source, 'result', ['slot']);
231 $reader = new Reader($context);
232 $merge = (new Arrays())->merge(Term::fromNative(['id', 'name']), Term::parameter('x', 'array'));
233 self::assertSame('name', $reader->array($merge, Term::constant('1'), $instruction, new State(), false)->native());
234 self::assertSame(TermKind::ArrayRead, $reader->array($merge, Term::constant(2), $instruction, new State(), false)->kind);
235 $named = (new Arrays())->merge(Term::fromNative(['k' => 'id', 0 => 'name']), Term::parameter('x', 'array'));
236 self::assertSame(TermKind::ArrayRead, $reader->array($named, Term::constant(0), $instruction, new State(), false)->kind);
237 self::assertSame([], $context->frontiers);
238 }
239 public function testKeyTruncatesFloatKeysAndRecordsLossOfPrecision(): void
240 {
241 $source = '<?php function target(){return 1;}';
242 $budget = new Budget();
243 $configuration = new DeriverConfig();
244 $contributions = new Contributions();
245 $input = new Sources([new Source('fixture.php', $source)]);
246 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
247 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
248 $contributions1 = $providers->contributions;
249 $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);
250 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
251 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
252 $snapshotId = $identity->id;
253 $input1 = $providers->input;
254 $parser = new Parser(new ParserConfig($target->version));
255 $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));
256 $program1 = new Index($snapshotId, $trees);
257 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
258 $snapshotId1 = 'test';
259 $input2 = new Sources([new Source('fixture.php', $source)]);
260 $profile = $execution->configuration->target;
261 $parser1 = new Parser(new ParserConfig($profile->version));
262 $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
263 $program = new Index($snapshotId1, $trees1);
264 $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
265 $body = $context->program->callable('target');
266 self::assertNotNull($body);
267 $instruction = new BoundInstruction('offset', 'write', $body->source, 'result', ['slot', 'rhs']);
268 $key = (new Reader($context))->key(Term::constant(1.5), $instruction);
269 self::assertSame(1, $key->native());
270 self::assertSame(['PHP_WARNING'], array_column(array_values($context->frontiers), 'code'));
271 }
272 public function testPlainObjectRecognizesArrayAccessImplementations(): void
273 {
274 $source = '<?php class A implements ArrayAccess{} function target(){}';
275 $budget = new Budget();
276 $configuration = new DeriverConfig();
277 $contributions = new Contributions();
278 $input = new Sources([new Source('fixture.php', $source)]);
279 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
280 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
281 $contributions1 = $providers->contributions;
282 $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);
283 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
284 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
285 $snapshotId = $identity->id;
286 $input1 = $providers->input;
287 $parser = new Parser(new ParserConfig($target->version));
288 $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));
289 $program1 = new Index($snapshotId, $trees);
290 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
291 $snapshotId1 = 'test';
292 $input2 = new Sources([new Source('fixture.php', $source)]);
293 $profile = $execution->configuration->target;
294 $parser1 = new Parser(new ParserConfig($profile->version));
295 $trees1 = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input2, false));
296 $program = new Index($snapshotId1, $trees1);
297 $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
298 $reader = new Reader($context);
299 self::assertTrue($reader->plainObject(new Term(TermKind::Object, 'a', attributes: ['class' => 'stdClass'])));
300 self::assertFalse($reader->plainObject(new Term(TermKind::Object, 'b', attributes: ['class' => 'A'])));
301 self::assertTrue($reader->plainObject(new Term(TermKind::Closure, 'c')));
302 }
303 #[DataProvider('providerScalarPatterns')]
304 public function testReadTreatsScalarPatternEntriesAsNullWithoutOffsetDiagnostics(Term $container): void
305 {
306 $source1 = '<?php function target(){return 1;}';
307 $budget = new Budget();
308 $configuration = new DeriverConfig();
309 $contributions = new Contributions();
310 $input = new Sources([new Source('fixture.php', $source1)]);
311 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
312 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
313 $contributions1 = $providers->contributions;
314 $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);
315 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
316 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
317 $snapshotId = $identity->id;
318 $input1 = $providers->input;
319 $parser = new Parser(new ParserConfig($target->version));
320 $trees = array_map(static fn (Source $source2): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source2->path), $source2->contents, $source2->declarationsOnly))), iterator_to_array($input1, false));
321 $program1 = new Index($snapshotId, $trees);
322 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
323 $snapshotId1 = 'test';
324 $input2 = new Sources([new Source('fixture.php', $source1)]);
325 $profile = $execution->configuration->target;
326 $parser1 = new Parser(new ParserConfig($profile->version));
327 $trees1 = array_map(static fn (Source $source3): File => (new Resolver())->resolve($parser1->parse(new Source(Path::normalize($source3->path), $source3->contents, $source3->declarationsOnly))), iterator_to_array($input2, false));
328 $program = new Index($snapshotId1, $trees1);
329 $context = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
330 $source = new SourceRef('test', 'a.php', 0, 1);
331 $instruction = new BoundInstruction('i', 'array-read', $source, 'result', attributes: ['destructure' => true]);
332 $result = (new Reader($context))->read($container, Term::constant(1), $instruction, new State());
333 self::assertSame(TermKind::Constant, $result->kind);
334 self::assertNull($result->literal);
335 self::assertSame([], $context->frontiers);
336 }
337 /**
338 * @return iterable<string,array{Term}>
339 */
340 public static function providerScalarPatterns(): iterable
341 {
342 yield 'null' => [Term::constant(null)];
343 yield 'integer' => [Term::constant(42)];
344 yield 'float' => [Term::constant(1.5)];
345 yield 'true' => [Term::constant(true)];
346 yield 'false' => [Term::constant(false)];
347 yield 'string' => [Term::constant('abc')];
348 yield 'uninitialized' => [new Term(TermKind::Uninitialized)];
349 }
350}
351