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