packages/deriver/tests/Unit/Deriver/Evaluator/Execution/Call/Preparation/EntryPropertiesTest.php
1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Unit\Deriver\Evaluator\Execution\Call\Preparation;
6
7use Deriver\Deriver\Contributions;
8use Deriver\Deriver\DeriverConfig;
9use Deriver\Deriver\Evaluator\Call\Evaluators;
10use Deriver\Deriver\Evaluator\Call\Extensions;
11use Deriver\Deriver\Evaluator\Call\Registrations;
12use Deriver\Deriver\Evaluator\Call\StateRegistry;
13use Deriver\Deriver\Evaluator\EvaluatorConfig;
14use Deriver\Deriver\Evaluator\Evidence\Frontier;
15use Deriver\Deriver\Evaluator\Execution\Call\Native\Properties;
16use Deriver\Deriver\Evaluator\Execution\Call\Native\Signatures;
17use Deriver\Deriver\Evaluator\Execution\Call\Preparation\EntryProperties;
18use Deriver\Deriver\Evaluator\Execution\Call\TypeBinding;
19use Deriver\Deriver\Evaluator\Execution\Call\TypeCheck;
20use Deriver\Deriver\Evaluator\Execution\Completion\Completion;
21use Deriver\Deriver\Evaluator\Execution\Context;
22use Deriver\Deriver\Evaluator\Execution\EvaluatorConfig as ExecutionEvaluatorConfig;
23use Deriver\Deriver\Evaluator\Execution\Memory\Location;
24use Deriver\Deriver\Evaluator\Execution\Memory\Memory;
25use Deriver\Deriver\Evaluator\Execution\State;
26use Deriver\Deriver\Evaluator\Execution\Transfer\ObjectAccess;
27use Deriver\Deriver\Evaluator\Execution\Transfer\PropertyLookup;
28use Deriver\Deriver\Evaluator\Execution\Transfer\ReferenceAssignment;
29use Deriver\Deriver\Evaluator\Request\Budget;
30use Deriver\Deriver\Evaluator\Request\QueryScope;
31use Deriver\Deriver\Evaluator\Request\ReturnQuery;
32use Deriver\Deriver\Evaluator\Scalar\Arrays;
33use Deriver\Deriver\Evaluator\Scalar\Operations;
34use Deriver\Deriver\Evaluator\TargetProfile;
35use Deriver\Deriver\Evaluator\Term;
36use Deriver\Deriver\Evaluator\TermKind;
37use Deriver\Deriver\Execution\QueryExecution;
38use Deriver\Deriver\Lowerer\Declaration\CallableIdentity;
39use Deriver\Deriver\Lowerer\Declaration\CallableSource;
40use Deriver\Deriver\Lowerer\Declaration\ConstantSignatures;
41use Deriver\Deriver\Lowerer\Declaration\Index;
42use Deriver\Deriver\Lowerer\Declaration\Indexer;
43use Deriver\Deriver\Lowerer\Declaration\Metadata\ClassDeclaration;
44use Deriver\Deriver\Lowerer\Declaration\Metadata\PropertyDeclaration;
45use Deriver\Deriver\Lowerer\Declaration\Traits\Composition;
46use Deriver\Deriver\Lowerer\ExpressionLowering;
47use Deriver\Deriver\Lowerer\Graph\BasicBlock;
48use Deriver\Deriver\Lowerer\Graph\CallableGraph;
49use Deriver\Deriver\Lowerer\Graph\Instruction;
50use Deriver\Deriver\Lowerer\Graph\Terminator;
51use Deriver\Deriver\Lowerer\Hierarchy\Builtins;
52use Deriver\Deriver\Lowerer\Hierarchy\Dispatch;
53use Deriver\Deriver\Lowerer\InstructionEmitter;
54use Deriver\Deriver\Lowerer\Lowerer;
55use Deriver\Deriver\Lowerer\LoweringScope;
56use Deriver\Deriver\MicroEvaluatorConversion;
57use Deriver\Deriver\Parser\Node\File;
58use Deriver\Deriver\Parser\Parser;
59use Deriver\Deriver\Parser\ParserConfig;
60use Deriver\Deriver\Parser\TargetSyntax;
61use Deriver\Deriver\Providers\ProviderExpansion;
62use Deriver\Deriver\Resolver\AssignmentPatterns;
63use Deriver\Deriver\Resolver\ClassScope;
64use Deriver\Deriver\Resolver\ExpressionValidation;
65use Deriver\Deriver\Resolver\MagicContext;
66use Deriver\Deriver\Resolver\Resolver;
67use Deriver\Deriver\SnapshotIdentity;
68use Deriver\Deriver\Sources\LineMap;
69use Deriver\Deriver\Sources\Path;
70use Deriver\Deriver\Sources\Source;
71use Deriver\Deriver\Sources\SourceRef;
72use Deriver\Deriver\Sources\Sources;
73use Deriver\Exception\InvalidInputException;
74use Deriver\Exception\ParserException;
75use Deriver\Exception\ResolutionException;
76use PHPUnit\Framework\Attributes\CoversClass;
77use PHPUnit\Framework\Attributes\DataProvider;
78use PHPUnit\Framework\Attributes\Small;
79use PHPUnit\Framework\Attributes\UsesClass;
80use PHPUnit\Framework\TestCase;
81
82#[UsesClass(Resolver::class)]
83#[UsesClass(ExpressionValidation::class)]
84#[UsesClass(ResolutionException::class)]
85#[UsesClass(ParserConfig::class)]
86#[UsesClass(ParserException::class)]
87#[CoversClass(EntryProperties::class)]
88#[UsesClass(BasicBlock::class)]
89#[UsesClass(CallableGraph::class)]
90#[UsesClass(CallableIdentity::class)]
91#[UsesClass(ClassDeclaration::class)]
92#[UsesClass(Instruction::class)]
93#[UsesClass(PropertyDeclaration::class)]
94#[UsesClass(Terminator::class)]
95#[UsesClass(Dispatch::class)]
96#[UsesClass(TypeBinding::class)]
97#[UsesClass(TypeCheck::class)]
98#[UsesClass(Completion::class)]
99#[UsesClass(Context::class)]
100#[UsesClass(State::class)]
101#[UsesClass(ObjectAccess::class)]
102#[UsesClass(PropertyLookup::class)]
103#[UsesClass(ReferenceAssignment::class)]
104#[UsesClass(Location::class)]
105#[UsesClass(Memory::class)]
106#[UsesClass(Extensions::class)]
107#[UsesClass(Evaluators::class)]
108#[UsesClass(StateRegistry::class)]
109#[UsesClass(DeriverConfig::class)]
110#[UsesClass(Sources::class)]
111#[UsesClass(Path::class)]
112#[UsesClass(Source::class)]
113#[UsesClass(TargetProfile::class)]
114#[UsesClass(Budget::class)]
115#[UsesClass(QueryScope::class)]
116#[UsesClass(ReturnQuery::class)]
117#[UsesClass(SourceRef::class)]
118#[UsesClass(Parser::class)]
119#[UsesClass(File::class)]
120#[UsesClass(Lowerer::class)]
121#[UsesClass(ExpressionLowering::class)]
122#[UsesClass(InstructionEmitter::class)]
123#[UsesClass(LoweringScope::class)]
124#[UsesClass(ConstantSignatures::class)]
125#[UsesClass(CallableSource::class)]
126#[UsesClass(Indexer::class)]
127#[UsesClass(Index::class)]
128#[UsesClass(Composition::class)]
129#[UsesClass(LineMap::class)]
130#[UsesClass(MagicContext::class)]
131#[UsesClass(AssignmentPatterns::class)]
132#[UsesClass(ClassScope::class)]
133#[UsesClass(TargetSyntax::class)]
134#[UsesClass(Arrays::class)]
135#[UsesClass(Operations::class)]
136#[UsesClass(Term::class)]
137#[UsesClass(Builtins::class)]
138#[UsesClass(Properties::class)]
139#[UsesClass(Signatures::class)]
140#[Small]
141final class EntryPropertiesTest extends TestCase
142{
143 public function testApplyWithoutPropertiesLeavesTheEntryStateUntouched(): void
144 {
145 $symbol = 'free';
146 $source = '<?php declare(strict_types=1); class Base{private string $table="users";protected ?int $limit=null;}' . 'final class Box extends Base{private string $order="name";public static int $count=0;public int|string $key=0;public $loose;function run(){}static function make(){}}function free(){}';
147 $budget = new Budget();
148 $configuration = new DeriverConfig();
149 $contributions = new Contributions();
150 $input = new Sources([new Source('fixture.php', $source)]);
151 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
152 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
153 $contributions1 = $providers->contributions;
154 $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);
155 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
156 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
157 $snapshotId = $identity->id;
158 $input1 = $providers->input;
159 $parser = new Parser(new ParserConfig($target->version));
160 $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));
161 $program1 = new Index($snapshotId, $trees);
162 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
163 $snapshotId1 = 'test';
164 $input2 = new Sources([new Source('fixture.php', $source)]);
165 $profile = $execution->configuration->target;
166 $parser1 = new Parser(new ParserConfig($profile->version));
167 $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));
168 $program = new Index($snapshotId1, $trees1);
169 $context1 = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
170 $body1 = $context1->program->callable($symbol);
171 self::assertNotNull($body1);
172 [$context, $body] = [$context1, $body1];
173 $state = new State();
174 (new EntryProperties($context))->apply($body, null, [], $state);
175 self::assertSame([], $state->memory->cells);
176 self::assertSame([], $state->memory->propertyTypes);
177 }
178 public function testApplyWritesDeclaredSlotsAndKeepsTheRecordOpenForUnsuppliedProperties(): void
179 {
180 $symbol = 'Box::run';
181 $source = '<?php declare(strict_types=1); class Base{private string $table="users";protected ?int $limit=null;}' . 'final class Box extends Base{private string $order="name";public static int $count=0;public int|string $key=0;public $loose;function run(){}static function make(){}}function free(){}';
182 $budget = new Budget();
183 $configuration = new DeriverConfig();
184 $contributions = new Contributions();
185 $input = new Sources([new Source('fixture.php', $source)]);
186 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
187 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
188 $contributions1 = $providers->contributions;
189 $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);
190 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
191 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
192 $snapshotId = $identity->id;
193 $input1 = $providers->input;
194 $parser = new Parser(new ParserConfig($target->version));
195 $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));
196 $program1 = new Index($snapshotId, $trees);
197 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
198 $snapshotId1 = 'test';
199 $input2 = new Sources([new Source('fixture.php', $source)]);
200 $profile = $execution->configuration->target;
201 $parser1 = new Parser(new ParserConfig($profile->version));
202 $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));
203 $program = new Index($snapshotId1, $trees1);
204 $context1 = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
205 $body1 = $context1->program->callable($symbol);
206 self::assertNotNull($body1);
207 [$context, $body] = [$context1, $body1];
208 $state = new State();
209 $order = Term::constant('email');
210 (new EntryProperties($context))->apply($body, Term::parameter('this', 'Box'), ['order' => $order, 'table' => Term::constant('posts'), 'limit' => Term::constant(null), 'loose' => Term::array([])], $state);
211 $record = $state->memory->cells['object:this'];
212 self::assertSame(['Box::order', 'Base::table', 'limit', 'loose'], array_keys($record->operands));
213 self::assertSame($order, $record->operands['Box::order']);
214 self::assertSame('posts', $record->operands['Base::table']->native());
215 self::assertTrue($record->attributes['open']);
216 self::assertSame(['Box::order' => 'string', 'Base::table' => 'string', 'limit' => 'int|null', 'loose' => 'mixed'], $state->memory->propertyTypes['object:this']);
217 }
218 public function testApplyResolvesTheClassOfAnExplicitReceiverIdentity(): void
219 {
220 $symbol = 'Box::run';
221 $source = '<?php declare(strict_types=1); class Base{private string $table="users";protected ?int $limit=null;}' . 'final class Box extends Base{private string $order="name";public static int $count=0;public int|string $key=0;public $loose;function run(){}static function make(){}}function free(){}';
222 $budget = new Budget();
223 $configuration = new DeriverConfig();
224 $contributions = new Contributions();
225 $input = new Sources([new Source('fixture.php', $source)]);
226 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
227 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
228 $contributions1 = $providers->contributions;
229 $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);
230 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
231 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
232 $snapshotId = $identity->id;
233 $input1 = $providers->input;
234 $parser = new Parser(new ParserConfig($target->version));
235 $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));
236 $program1 = new Index($snapshotId, $trees);
237 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
238 $snapshotId1 = 'test';
239 $input2 = new Sources([new Source('fixture.php', $source)]);
240 $profile = $execution->configuration->target;
241 $parser1 = new Parser(new ParserConfig($profile->version));
242 $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));
243 $program = new Index($snapshotId1, $trees1);
244 $context1 = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
245 $body1 = $context1->program->callable($symbol);
246 self::assertNotNull($body1);
247 [$context, $body] = [$context1, $body1];
248 $state = new State();
249 (new EntryProperties($context))->apply($body, new Term(TermKind::Object, 'provided', attributes: ['class' => '\Box']), ['key' => Term::constant('k')], $state);
250 self::assertSame('k', $state->memory->cells['object:provided']->operands['key']->native());
251 }
252 public function testApplyKeepsASymbolicValueWithinTheDeclaredType(): void
253 {
254 $symbol = 'Box::run';
255 $source = '<?php declare(strict_types=1); class Base{private string $table="users";protected ?int $limit=null;}' . 'final class Box extends Base{private string $order="name";public static int $count=0;public int|string $key=0;public $loose;function run(){}static function make(){}}function free(){}';
256 $budget = new Budget();
257 $configuration = new DeriverConfig();
258 $contributions = new Contributions();
259 $input = new Sources([new Source('fixture.php', $source)]);
260 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
261 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
262 $contributions1 = $providers->contributions;
263 $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);
264 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
265 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
266 $snapshotId = $identity->id;
267 $input1 = $providers->input;
268 $parser = new Parser(new ParserConfig($target->version));
269 $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));
270 $program1 = new Index($snapshotId, $trees);
271 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
272 $snapshotId1 = 'test';
273 $input2 = new Sources([new Source('fixture.php', $source)]);
274 $profile = $execution->configuration->target;
275 $parser1 = new Parser(new ParserConfig($profile->version));
276 $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));
277 $program = new Index($snapshotId1, $trees1);
278 $context1 = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
279 $body1 = $context1->program->callable($symbol);
280 self::assertNotNull($body1);
281 [$context, $body] = [$context1, $body1];
282 $state = new State();
283 (new EntryProperties($context))->apply($body, Term::parameter('this', 'Box'), ['order' => Term::parameter('order')], $state);
284 $value = $state->memory->cells['object:this']->operands['Box::order'];
285 self::assertFalse($value->isConcrete());
286 self::assertSame('string', $value->attributes['type'] ?? null);
287 }
288 /**
289 * @param string $symbol Entry callable
290 * @param Term|null $receiver Bound receiver
291 * @param array<string, Term> $properties Supplied values
292 */
293 #[DataProvider('providerRejected')]
294 public function testApplyRejectsPropertiesNoPhpObjectCanHave(string $symbol, ?Term $receiver, array $properties, string $message): void
295 {
296 $source = '<?php declare(strict_types=1); class Base{private string $table="users";protected ?int $limit=null;}' . 'final class Box extends Base{private string $order="name";public static int $count=0;public int|string $key=0;public $loose;function run(){}static function make(){}}function free(){}';
297 $budget = new Budget();
298 $configuration = new DeriverConfig();
299 $contributions = new Contributions();
300 $input = new Sources([new Source('fixture.php', $source)]);
301 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
302 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
303 $contributions1 = $providers->contributions;
304 $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);
305 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
306 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
307 $snapshotId = $identity->id;
308 $input1 = $providers->input;
309 $parser = new Parser(new ParserConfig($target->version));
310 $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));
311 $program1 = new Index($snapshotId, $trees);
312 $execution = new QueryExecution($program1, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program1->diagnostics())));
313 $snapshotId1 = 'test';
314 $input2 = new Sources([new Source('fixture.php', $source)]);
315 $profile = $execution->configuration->target;
316 $parser1 = new Parser(new ParserConfig($profile->version));
317 $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));
318 $program = new Index($snapshotId1, $trees1);
319 $context1 = new Context($program, new ReturnQuery('target', budget: $budget), $execution->configuration, $execution->evaluators);
320 $body1 = $context1->program->callable($symbol);
321 self::assertNotNull($body1);
322 [$context, $body] = [$context1, $body1];
323 $this->expectException(InvalidInputException::class);
324 $this->expectExceptionMessage($message);
325 (new EntryProperties($context))->apply($body, $receiver, $properties, new State());
326 }
327 /**
328 * @return iterable<string, array{string, Term|null, array<string, Term>, string}>
329 */
330 public static function providerRejected(): iterable
331 {
332 $value = ['order' => Term::constant('x')];
333 yield 'function entry' => ['free', null, $value, 'Entry properties require an instance receiver: free'];
334 yield 'static entry' => ['Box::make', null, $value, 'Entry properties require an instance receiver: Box::make'];
335 yield 'anonymous receiver identity' => ['Box::run', new Term(TermKind::Object, attributes: ['class' => 'Box']), $value, 'Entry properties require an instance receiver'];
336 yield 'undeclared property' => ['Box::run', Term::parameter('this', 'Box'), ['missing' => Term::constant('x')], 'Entry property is not a declared instance property: Box::$missing'];
337 yield 'static property' => ['Box::run', Term::parameter('this', 'Box'), ['count' => Term::constant(1)], 'Entry property is not a declared instance property: Box::$count'];
338 yield 'scalar type violation' => ['Box::run', Term::parameter('this', 'Box'), ['order' => Term::constant(1)], 'Entry property value does not satisfy the declared type string: Box::$order'];
339 yield 'nullable type violation' => ['Box::run', Term::parameter('this', 'Box'), ['limit' => Term::constant('ten')], 'Entry property value does not satisfy the declared type int|null: Box::$limit'];
340 yield 'union type violation' => ['Box::run', Term::parameter('this', 'Box'), ['key' => Term::constant(1.5)], 'Entry property value does not satisfy the declared type int|string: Box::$key'];
341 }
342}
343