From b4ab3df2ec85fbe477d7721344a8cd3630b437a1 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 16 Aug 2026 13:59:28 +0900 Subject: feat(plugin): guard Rust-owned classes the worker has no proxy for The worker's autoloader fell through to the real Composer source for every Rust-owned FQCN without a proxy stub, so plugin code doing `new Filesystem()` or subclassing `LibraryInstaller` silently ran on a second instance the Rust side never sees. An unimplemented part of the plugin API has to fail with an explicit error naming it, not quietly work on a disconnected copy. The stub generator now emits a guard class for each of those FQCNs: the real declaration, hierarchy and constants, with every constructor and method raising an explicit error. References satisfied by the declaration alone (`instanceof`, `X::class`, `Link::TYPE_REQUIRE`) keep working. Two FQCNs stay resolvable to the real class, each listed with the worker-side mechanism that makes a natively constructed instance correct. The error had nowhere to go: `Installer::run` dropped the `Result` of both `dispatch_script` calls, so an exception from a listener ended in exit 0. Both propagate now, the way the exception does upstream. Three real-plugin E2E comparisons stop at a guard and are ignored, each naming the class it needs. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/plugin-stub-generator/src/SourceFile.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'scripts/plugin-stub-generator/src/SourceFile.php') diff --git a/scripts/plugin-stub-generator/src/SourceFile.php b/scripts/plugin-stub-generator/src/SourceFile.php index 771e83a8..e20bf00f 100644 --- a/scripts/plugin-stub-generator/src/SourceFile.php +++ b/scripts/plugin-stub-generator/src/SourceFile.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Shirabe\PluginStubGenerator; +use PhpParser\Node\Name; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\GroupUse; use PhpParser\Node\Stmt\Namespace_; @@ -83,4 +84,24 @@ final class SourceFile { return implode("\n", array_slice($this->lines, $startLine - 1, $endLine - $startLine + 1)); } + + /** This file's imports, restricted to the names the emitted text actually uses. */ + public function importsUsedBy(string $emittedText): string + { + $kept = []; + foreach ($this->aliases as $alias => $fqcn) { + if (preg_match('/(?getAttribute('resolvedName'); + return $resolved instanceof Name ? $resolved->toString() : $name->toString(); + } } -- cgit v1.3.1-4-g156e