packages/deriver/tests/Unit/Deriver/Lowerer/Declaration/PropertyWriteIndexTest.php
1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Unit\Deriver\Lowerer\Declaration;
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\Index\DefinitionIndex;
14use Deriver\Deriver\Evaluator\TargetProfile;
15use Deriver\Deriver\Evaluator\Term;
16use Deriver\Deriver\Lowerer\Declaration\Index;
17use Deriver\Deriver\Lowerer\Declaration\PropertyWriteIndex as Subject;
18use Deriver\Deriver\MicroEvaluatorConversion;
19use Deriver\Deriver\Parser\Node\File;
20use Deriver\Deriver\Parser\Parser;
21use Deriver\Deriver\Parser\ParserConfig;
22use Deriver\Deriver\Providers\ProviderExpansion;
23use Deriver\Deriver\Publication\CandidateQuery;
24use Deriver\Deriver\Resolver\Resolver;
25use Deriver\Deriver\SnapshotIdentity;
26use Deriver\Deriver\Sources\Path;
27use Deriver\Deriver\Sources\Source;
28use Deriver\Deriver\Sources\Sources;
29use PhpParser\Node\Expr\AssignOp;
30use PhpParser\NodeFinder;
31use PHPUnit\Framework\Attributes\CoversNothing;
32use PHPUnit\Framework\TestCase;
33
34#[CoversNothing]
35final class PropertyWriteIndexTest extends TestCase
36{
37 public function testOwnersExcludesUnrelatedBodies(): void
38 {
39 $prepared1Input = new Sources([new Source('candidate.php', '<?php ' . 'class C{public $x=1;function write(){$this->x=2;}function read(){return $this->x;}}')]);
40 $prepared1Configuration = new DeriverConfig();
41 $prepared1Contributions = new Contributions();
42 $prepared1Target = new TargetProfile($prepared1Configuration->phpVersion, $prepared1Configuration->integerBits, $prepared1Configuration->floatPrecision);
43 $prepared1Providers = new ProviderExpansion($prepared1Input, array_map(Term::fromNative(...), $prepared1Configuration->environment), $prepared1Contributions);
44 $contributions = $prepared1Providers->contributions;
45 $prepared1Evaluators = new Evaluators(new TargetProfile($prepared1Configuration->phpVersion, $prepared1Configuration->integerBits, $prepared1Configuration->floatPrecision), new Registrations($contributions->evaluators, $contributions->providers, $contributions->domains, $contributions->intrinsics, $contributions->stateSlots), $prepared1Configuration->standardEvaluators);
46 $prepared1Settings = new EvaluatorConfig($prepared1Target, $prepared1Providers->environment, $prepared1Configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $prepared1Configuration->evaluators));
47 $prepared1Identity = new SnapshotIdentity($prepared1Providers->input, $prepared1Settings, $prepared1Evaluators, $prepared1Providers->manifest);
48 $snapshotId = $prepared1Identity->id;
49 $input = $prepared1Providers->input;
50 $parser = new Parser(new ParserConfig($prepared1Target->version));
51 $trees = array_map(static fn (Source $source): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source->path), $source->contents, $source->declarationsOnly))), iterator_to_array($input, false));
52 $prepared1Program = new Index($snapshotId, $trees);
53 $prepared1Snapshot = $prepared1Identity->snapshot(array_map(Frontier::fromDiagnostic(...), $prepared1Program->diagnostics()));
54 $session = new CandidateQuery(new DefinitionIndex($prepared1Program), $prepared1Settings, $prepared1Evaluators, $prepared1Snapshot);
55 self::assertSame(['C::write'], (new Subject($prepared1Program))->owners('x'));
56 }
57 public function testContainsFindsNestedElementWrites(): void
58 {
59 $prepared2Input = new Sources([new Source('candidate.php', '<?php ' . 'class C{public $x=[];function write(){$this->x["a"]=2;}}')]);
60 $prepared2Configuration = new DeriverConfig();
61 $prepared2Contributions = new Contributions();
62 $prepared2Target = new TargetProfile($prepared2Configuration->phpVersion, $prepared2Configuration->integerBits, $prepared2Configuration->floatPrecision);
63 $prepared2Providers = new ProviderExpansion($prepared2Input, array_map(Term::fromNative(...), $prepared2Configuration->environment), $prepared2Contributions);
64 $contributions = $prepared2Providers->contributions;
65 $prepared2Evaluators = new Evaluators(new TargetProfile($prepared2Configuration->phpVersion, $prepared2Configuration->integerBits, $prepared2Configuration->floatPrecision), new Registrations($contributions->evaluators, $contributions->providers, $contributions->domains, $contributions->intrinsics, $contributions->stateSlots), $prepared2Configuration->standardEvaluators);
66 $prepared2Settings = new EvaluatorConfig($prepared2Target, $prepared2Providers->environment, $prepared2Configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $prepared2Configuration->evaluators));
67 $prepared2Identity = new SnapshotIdentity($prepared2Providers->input, $prepared2Settings, $prepared2Evaluators, $prepared2Providers->manifest);
68 $snapshotId = $prepared2Identity->id;
69 $input = $prepared2Providers->input;
70 $parser = new Parser(new ParserConfig($prepared2Target->version));
71 $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($input, false));
72 $prepared2Program = new Index($snapshotId, $trees);
73 $prepared2Snapshot = $prepared2Identity->snapshot(array_map(Frontier::fromDiagnostic(...), $prepared2Program->diagnostics()));
74 $session = new CandidateQuery(new DefinitionIndex($prepared2Program), $prepared2Settings, $prepared2Evaluators, $prepared2Snapshot);
75 $source = $prepared2Program->declarations['c::write'];
76 self::assertTrue((new Subject($prepared2Program))->contains($source->node, 'x'));
77 }
78 public function testMutationRecognizesCompoundAssignments(): void
79 {
80 $source = '<?php function target(){$x+=1;}';
81 $snapshotId = 'test';
82 $input = new Sources([new Source('fixture.php', $source)]);
83 $profile = new TargetProfile();
84 $parser = new Parser(new ParserConfig($profile->version));
85 $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($input, false));
86 $index = new Index($snapshotId, $trees);
87 $node = (new NodeFinder())->findFirst($index->declarations['target']->node, static fn ($node): bool => $node instanceof AssignOp);
88 self::assertNotNull($node);
89 self::assertTrue((new Subject($index))->mutation($node));
90 }
91}
92