From 98d71d5b9276cd4734d88d56e1d7708760bedcef Mon Sep 17 00:00:00 2001 From: Can Vural Date: Wed, 23 Nov 2022 21:04:48 +0100 Subject: [PATCH] finish --- bootstrap.php | 8 ++- extension.neon | 11 +++- .../UsedRouteFacadeViewCollector.php | 59 +++++++++++++++++++ .../UsedViewInAnotherViewCollector.php | 6 +- src/Rules/UnusedViewsRule.php | 2 + .../resources/views/index.blade.php | 2 +- .../resources/views/route-view.blade.php | 1 + .../ReturnTypes/Helpers/ViewExtension.php | 21 ------- ...CompatibleWithClassConstructorRuleTest.php | 7 --- tests/Rules/Data/FooController.php | 11 ++-- ...ServiceProviderMissingProvidesRuleTest.php | 7 --- tests/Rules/UnusedViewsRuleTest.php | 13 +++- 12 files changed, 98 insertions(+), 50 deletions(-) create mode 100644 src/Collectors/UsedRouteFacadeViewCollector.php create mode 100644 tests/Application/resources/views/route-view.blade.php delete mode 100644 tests/Features/ReturnTypes/Helpers/ViewExtension.php diff --git a/bootstrap.php b/bootstrap.php index 904ad05a2..c4a028e3c 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -8,7 +8,9 @@ use NunoMaduro\Larastan\ApplicationResolver; use Orchestra\Testbench\Concerns\CreatesApplication; -define('LARAVEL_START', microtime(true)); +if (! defined('LARAVEL_START')) { + define('LARAVEL_START', microtime(true)); +} if (file_exists($applicationPath = getcwd().'/bootstrap/app.php')) { // Applications and Local Dev $app = require $applicationPath; @@ -26,4 +28,6 @@ $app->boot(); } -define('LARAVEL_VERSION', $app->version()); +if (! defined('LARAVEL_VERSION')) { + define('LARAVEL_VERSION', $app->version()); +} diff --git a/extension.neon b/extension.neon index fab960024..b32cb5fe3 100644 --- a/extension.neon +++ b/extension.neon @@ -21,6 +21,7 @@ parameters: viewDirectories: [] checkModelProperties: false checkPhpDocMissingReturn: false + checkUnusedViews: false parametersSchema: checkOctaneCompatibility: bool() @@ -32,6 +33,7 @@ parametersSchema: viewDirectories: listOf(string()) squashedMigrationsPath: listOf(string()) checkModelProperties: bool() + checkUnusedViews: bool() conditionalTags: NunoMaduro\Larastan\Rules\NoModelMakeRule: @@ -44,6 +46,8 @@ conditionalTags: phpstan.rules.rule: %checkModelProperties% NunoMaduro\Larastan\Rules\ModelProperties\ModelPropertyStaticCallRule: phpstan.rules.rule: %checkModelProperties% + NunoMaduro\Larastan\Rules\UnusedViewsRule: + phpstan.rules.rule: %checkUnusedViews% services: - @@ -442,8 +446,6 @@ services: - class: NunoMaduro\Larastan\Rules\UnusedViewsRule - tags: - - phpstan.rules.rule - class: NunoMaduro\Larastan\Collectors\UsedViewFunctionCollector @@ -464,6 +466,11 @@ services: class: NunoMaduro\Larastan\Collectors\UsedViewFacadeMakeCollector tags: - phpstan.collector + + - + class: NunoMaduro\Larastan\Collectors\UsedRouteFacadeViewCollector + tags: + - phpstan.collector - class: NunoMaduro\Larastan\Collectors\UsedViewInAnotherViewCollector arguments: diff --git a/src/Collectors/UsedRouteFacadeViewCollector.php b/src/Collectors/UsedRouteFacadeViewCollector.php new file mode 100644 index 000000000..2586c9ee2 --- /dev/null +++ b/src/Collectors/UsedRouteFacadeViewCollector.php @@ -0,0 +1,59 @@ + */ +final class UsedRouteFacadeViewCollector implements Collector +{ + public function getNodeType(): string + { + return Node\Expr\StaticCall::class; + } + + /** @param Node\Expr\StaticCall $node */ + public function processNode(Node $node, Scope $scope): ?string + { + $name = $node->name; + + if (! $name instanceof Node\Identifier) { + return null; + } + + if ($name->name !== 'view') { + return null; + } + + if (count($node->getArgs()) < 2) { + return null; + } + + $class = $node->class; + + if (! $class instanceof Node\Name) { + return null; + } + + $class = $scope->resolveName($class); + + if (! (new ObjectType(Route::class))->isSuperTypeOf(new ObjectType($class))->yes()) { + return null; + } + + $template = $node->getArgs()[1]->value; + + if (! $template instanceof Node\Scalar\String_) { + return null; + } + + return ViewName::normalize($template->value); + } +} diff --git a/src/Collectors/UsedViewInAnotherViewCollector.php b/src/Collectors/UsedViewInAnotherViewCollector.php index e74579b1e..bacad6536 100644 --- a/src/Collectors/UsedViewInAnotherViewCollector.php +++ b/src/Collectors/UsedViewInAnotherViewCollector.php @@ -9,8 +9,8 @@ final class UsedViewInAnotherViewCollector { - /** @see https://regex101.com/r/8gosof/1 */ - private const VIEW_NAME_REGEX = '/@(extends|include(If|Unless|When|First)?)(\(.*?\'(.*?)\'(\)|,))/m'; + /** @see https://regex101.com/r/OyHHCY/1 */ + private const VIEW_NAME_REGEX = '/@(extends|include(If|Unless|When|First)?)(\(.*?([\'"])(.*?)([\'"])([),]))/m'; public function __construct(private Parser $parser, private ViewFileHelper $viewFileHelper) { @@ -53,7 +53,7 @@ private function processNodes(array $nodes): array preg_match_all(self::VIEW_NAME_REGEX, $node->value, $matches, PREG_SET_ORDER, 0); $usedViews = array_merge($usedViews, array_map(function ($match) { - return $match[4]; + return $match[5]; }, $matches)); } diff --git a/src/Rules/UnusedViewsRule.php b/src/Rules/UnusedViewsRule.php index 6896b146a..27a527d35 100644 --- a/src/Rules/UnusedViewsRule.php +++ b/src/Rules/UnusedViewsRule.php @@ -8,6 +8,7 @@ use Illuminate\View\Factory; use NunoMaduro\Larastan\Collectors\UsedEmailViewCollector; use NunoMaduro\Larastan\Collectors\UsedViewFacadeMakeCollector; +use NunoMaduro\Larastan\Collectors\UsedRouteFacadeViewCollector; use NunoMaduro\Larastan\Collectors\UsedViewFunctionCollector; use NunoMaduro\Larastan\Collectors\UsedViewInAnotherViewCollector; use NunoMaduro\Larastan\Collectors\UsedViewMakeCollector; @@ -44,6 +45,7 @@ public function processNode(Node $node, Scope $scope): array $node->get(UsedEmailViewCollector::class), $node->get(UsedViewMakeCollector::class), $node->get(UsedViewFacadeMakeCollector::class), + $node->get(UsedRouteFacadeViewCollector::class), $this->viewsUsedInOtherViews, ])->flatten()->unique()->toArray(); diff --git a/tests/Application/resources/views/index.blade.php b/tests/Application/resources/views/index.blade.php index b750de39d..89451e726 100644 --- a/tests/Application/resources/views/index.blade.php +++ b/tests/Application/resources/views/index.blade.php @@ -6,4 +6,4 @@ Lorem ipsum -@include('home') +@include("home") diff --git a/tests/Application/resources/views/route-view.blade.php b/tests/Application/resources/views/route-view.blade.php new file mode 100644 index 000000000..0f7910319 --- /dev/null +++ b/tests/Application/resources/views/route-view.blade.php @@ -0,0 +1 @@ +This is used in a Route::view diff --git a/tests/Features/ReturnTypes/Helpers/ViewExtension.php b/tests/Features/ReturnTypes/Helpers/ViewExtension.php deleted file mode 100644 index e6c9a1fb7..000000000 --- a/tests/Features/ReturnTypes/Helpers/ViewExtension.php +++ /dev/null @@ -1,21 +0,0 @@ -markdown('emails.markdown'); } - public function foo(): self - { - return $this->markdown('home'); - } - public function bar(): self { return $this->view('emails.view'); @@ -61,3 +57,8 @@ function viewStaticMake(): View { return \Illuminate\Support\Facades\View::make('view-static-make'); } + +function routeView(): void +{ + Route::view('/welcome', 'route-view'); +} diff --git a/tests/Rules/DeferrableServiceProviderMissingProvidesRuleTest.php b/tests/Rules/DeferrableServiceProviderMissingProvidesRuleTest.php index 9c06cf18c..b1d8b256e 100644 --- a/tests/Rules/DeferrableServiceProviderMissingProvidesRuleTest.php +++ b/tests/Rules/DeferrableServiceProviderMissingProvidesRuleTest.php @@ -47,11 +47,4 @@ protected function getRule(): Rule { return new DeferrableServiceProviderMissingProvidesRule(); } - - public static function getAdditionalConfigFiles(): array - { - return [ - __DIR__.'/phpstan-rules.neon', - ]; - } } diff --git a/tests/Rules/UnusedViewsRuleTest.php b/tests/Rules/UnusedViewsRuleTest.php index 6016a4556..236a13bc6 100644 --- a/tests/Rules/UnusedViewsRuleTest.php +++ b/tests/Rules/UnusedViewsRuleTest.php @@ -3,13 +3,13 @@ namespace Rules; use NunoMaduro\Larastan\Collectors\UsedEmailViewCollector; +use NunoMaduro\Larastan\Collectors\UsedRouteFacadeViewCollector; use NunoMaduro\Larastan\Collectors\UsedViewFacadeMakeCollector; use NunoMaduro\Larastan\Collectors\UsedViewFunctionCollector; use NunoMaduro\Larastan\Collectors\UsedViewInAnotherViewCollector; use NunoMaduro\Larastan\Collectors\UsedViewMakeCollector; use NunoMaduro\Larastan\Rules\UnusedViewsRule; use NunoMaduro\Larastan\Support\ViewFileHelper; -use PHPStan\File\FileHelper; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; @@ -18,7 +18,7 @@ class UnusedViewsRuleTest extends RuleTestCase { protected function getRule(): Rule { - $viewFileHelper = new ViewFileHelper([__DIR__.'/../Application/resources/views'], $this->getContainer()->getByType(FileHelper::class)); + $viewFileHelper = new ViewFileHelper([__DIR__.'/../Application/resources/views'], $this->getFileHelper()); return new UnusedViewsRule(new UsedViewInAnotherViewCollector( $this->getContainer()->getService('currentPhpVersionSimpleDirectParser'), @@ -33,9 +33,18 @@ protected function getCollectors(): array new UsedEmailViewCollector, new UsedViewMakeCollector, new UsedViewFacadeMakeCollector, + new UsedRouteFacadeViewCollector, ]; } + protected function setUp(): void + { + parent::setUp(); + + // This is a workaround for a weird PHPStan container cache issue. + require __DIR__.'/../../bootstrap.php'; + } + public function testRule(): void { $this->analyse([__DIR__.'/Data/FooController.php'], [