packages/deriver/tests/Integration/CallableReplacementTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Integration;
6
7use Deriver\Deriver\Candidate\Snapshot;
8use Deriver\Deriver\Evaluation;
9use Deriver\Deriver\EvaluationOperation;
10use Deriver\Deriver\Evaluator\Call\Evaluators;
11use Deriver\Deriver\Evaluator\EvaluatorConfig;
12use Deriver\Deriver\Evaluator\Index\DefinitionIndex;
13use Deriver\Deriver\Evaluator\Request\ReturnQuery;
14use Deriver\Deriver\Evaluator\TargetProfile;
15use Deriver\Deriver\Evaluator\Term;
16use Deriver\Deriver\Lowerer\Declaration\Index;
17use Deriver\Deriver\MicroEvaluator;
18use Deriver\Deriver\MicroEvaluatorConversion;
19use Deriver\Deriver\Parser\Parser;
20use Deriver\Deriver\Parser\ParserConfig;
21use Deriver\Deriver\Publication\CandidateQuery;
22use Deriver\Deriver\Resolver\Resolver;
23use Deriver\Deriver\Sources\Source;
24use PHPUnit\Framework\Attributes\CoversNothing;
25use PHPUnit\Framework\Attributes\DataProvider;
26use PHPUnit\Framework\TestCase;
27
28#[CoversNothing]
29final class CallableReplacementTest extends TestCase
30{
31 /**
32 * @return iterable<string, array{string, string, string}>
33 */
34 public static function providerCalls(): iterable
35 {
36 yield 'direct function' => ['function subject($unused){return expensive();}', 'subject', 'subject'];
37 yield 'direct static method' => ['class Subject{static function run($unused){return expensive();}}', 'Subject::run', 'Subject::run'];
38 yield 'direct instance method' => ['class Subject{function run($unused){return expensive();}}', 'Subject::run', 'Subject::run'];
39 yield 'function call' => ['function replacement($unused){return expensive();} function subject(){return replacement(expensive());}', 'subject', 'replacement'];
40 yield 'static method call' => ['class Subject{static function run($unused){return expensive();}} function subject(){return Subject::run(expensive());}', 'subject', 'Subject::run'];
41 yield 'instance method call' => ['class Subject{function run($unused){return expensive();}} function subject(){return (new Subject())->run(expensive());}', 'subject', 'Subject::run'];
42 yield 'conditional definition' => ['if(condition()){function subject(){return expensive();}}else{function subject(){return expensive();}}', 'subject', 'subject'];
43 }
44
45 #[DataProvider('providerCalls')]
46 public function testReplacesTheSelectedCallableBeforeItsBody(string $source, string $symbol, string $replaced): void
47 {
48 $evaluators = [new MicroEvaluator('replace', '1', EvaluationOperation::Function, $replaced, static fn (): Term => Term::constant(42))];
49 $tree = (new Resolver())->resolve((new Parser(new ParserConfig('8.3')))->parse(new Source('branches.php', '<?php ' . $source)));
50 $program = new Index('branches', [$tree]);
51 $target = new TargetProfile();
52 $settings = new EvaluatorConfig($target, evaluators: array_map((new MicroEvaluatorConversion())->convert(...), $evaluators));
53 $query = new CandidateQuery(new DefinitionIndex($program), $settings, new Evaluators($target), new Snapshot('branches', [], [], $target, 'environment'));
54 $result = $query->derive(new ReturnQuery($symbol));
55 self::assertSame([42], array_map(static fn ($candidate) => $candidate->result, iterator_to_array($result)));
56 self::assertSame([], $result->statistics->expandedBodies);
57 self::assertArrayNotHasKey($replaced . ':$unused', $result->statistics->expandedReferences);
58 }
59
60 /**
61 * @return iterable<string, array{int|string}>
62 */
63 public static function providerParameters(): iterable
64 {
65 yield 'position' => [0];
66 yield 'name' => ['value'];
67 }
68
69 #[DataProvider('providerParameters')]
70 public function testDirectReplacementDemandsOnlyItsSelectedParameter(int|string $key): void
71 {
72 $source = 'function subject($value,$unused){return expensive();} function caller(){return subject(42,expensive());}';
73 $evaluators = [new MicroEvaluator('replace', '1', EvaluationOperation::Function, 'subject', static fn (Evaluation $evaluation): Term => $evaluation->input($key))];
74 $tree = (new Resolver())->resolve((new Parser(new ParserConfig('8.3')))->parse(new Source('branches.php', '<?php ' . $source)));
75 $program = new Index('branches', [$tree]);
76 $target = new TargetProfile();
77 $settings = new EvaluatorConfig($target, evaluators: array_map((new MicroEvaluatorConversion())->convert(...), $evaluators));
78 $query = new CandidateQuery(new DefinitionIndex($program), $settings, new Evaluators($target), new Snapshot('branches', [], [], $target, 'environment'));
79 $result = $query->derive(new ReturnQuery('subject'));
80 self::assertSame([42], array_map(static fn ($candidate) => $candidate->result, iterator_to_array($result)));
81 self::assertSame([], $result->statistics->expandedBodies);
82 }
83}
84