|
2 | 2 |
|
3 | 3 | namespace Psalm\LaravelPlugin\Handlers;
|
4 | 4 |
|
5 |
| -use Illuminate\Console\Command; |
6 |
| -use Illuminate\Foundation\Http\FormRequest; |
7 |
| -use Illuminate\Notifications\Notification; |
8 | 5 | use Psalm\Plugin\EventHandler\AfterClassLikeVisitInterface;
|
9 | 6 | use Psalm\Plugin\EventHandler\Event\AfterClassLikeVisitEvent;
|
| 7 | +use Psalm\Storage\ClassLikeStorage; |
10 | 8 | use Psalm\Storage\MethodStorage;
|
11 |
| -use function array_key_exists; |
| 9 | +use Psalm\Storage\PropertyStorage; |
| 10 | +use function array_intersect; |
12 | 11 | use function in_array;
|
13 | 12 |
|
14 | 13 | class SuppressHandler implements AfterClassLikeVisitInterface
|
15 | 14 | {
|
16 |
| - private const UNUSED_CLASSES = [ |
17 |
| - "App\Console\Kernel", |
18 |
| - "App\Exceptions\Handler", |
19 |
| - "App\Http\Controllers\Controller", |
20 |
| - "App\Http\Kernel", |
21 |
| - "App\Http\Middleware\Authenticate", |
22 |
| - "App\Http\Middleware\TrustHosts", |
23 |
| - "App\Providers\AppServiceProvider", |
24 |
| - "App\Providers\AuthServiceProvider", |
25 |
| - "App\Providers\BroadcastServiceProvider", |
26 |
| - "App\Providers\EventServiceProvider", |
| 15 | + /** |
| 16 | + * @var array<string, list<class-string>> |
| 17 | + */ |
| 18 | + private const BY_CLASS = [ |
| 19 | + 'UnusedClass' => [ |
| 20 | + 'App\Console\Kernel', |
| 21 | + 'App\Exceptions\Handler', |
| 22 | + 'App\Http\Controllers\Controller', |
| 23 | + 'App\Http\Kernel', |
| 24 | + 'App\Http\Middleware\Authenticate', |
| 25 | + 'App\Http\Middleware\TrustHosts', |
| 26 | + 'App\Providers\AppServiceProvider', |
| 27 | + 'App\Providers\AuthServiceProvider', |
| 28 | + 'App\Providers\BroadcastServiceProvider', |
| 29 | + 'App\Providers\EventServiceProvider', |
| 30 | + ], |
27 | 31 | ];
|
28 | 32 |
|
29 |
| - private const UNUSED_METHODS = [ |
30 |
| - "App\Http\Middleware\RedirectIfAuthenticated" => ["handle"], |
| 33 | + /** |
| 34 | + * @var array<string, array<class-string, list<string>>> |
| 35 | + */ |
| 36 | + private const BY_CLASS_METHOD = [ |
| 37 | + 'PossiblyUnusedMethod' => [ |
| 38 | + 'App\Http\Middleware\RedirectIfAuthenticated' => ['handle'], |
| 39 | + ], |
| 40 | + ]; |
| 41 | + |
| 42 | + /** |
| 43 | + * @var array<string, list<class-string>> |
| 44 | + */ |
| 45 | + private const BY_PARENT_CLASS = [ |
| 46 | + 'PropertyNotSetInConstructor' => [ |
| 47 | + 'Illuminate\Console\Command', |
| 48 | + 'Illuminate\Foundation\Http\FormRequest', |
| 49 | + 'Illuminate\Notifications\Notification', |
| 50 | + ], |
| 51 | + ]; |
| 52 | + |
| 53 | + /** |
| 54 | + * @var array<string, array<class-string, list<string>>> |
| 55 | + */ |
| 56 | + private const BY_PARENT_CLASS_PROPERTY = [ |
| 57 | + 'NonInvariantDocblockPropertyType' => [ |
| 58 | + 'Illuminate\Console\Command' => ['description'], |
| 59 | + ], |
31 | 60 | ];
|
32 | 61 |
|
33 | 62 | public static function afterClassLikeVisit(AfterClassLikeVisitEvent $event)
|
34 | 63 | {
|
35 |
| - $storage = $event->getStorage(); |
| 64 | + $class = $event->getStorage(); |
36 | 65 |
|
37 |
| - if (in_array(Command::class, $storage->parent_classes)) { |
38 |
| - if (!in_array('PropertyNotSetInConstructor', $storage->suppressed_issues)) { |
39 |
| - $storage->suppressed_issues[] = 'PropertyNotSetInConstructor'; |
40 |
| - } |
41 |
| - if (isset($storage->properties['description'])) { |
42 |
| - $property = $storage->properties['description']; |
43 |
| - if (!in_array('NonInvariantDocblockPropertyType', $property->suppressed_issues)) { |
44 |
| - $property->suppressed_issues[] = 'NonInvariantDocblockPropertyType'; |
45 |
| - } |
| 66 | + foreach (self::BY_CLASS as $issue => $class_names) { |
| 67 | + if (in_array($class->name, $class_names)) { |
| 68 | + self::suppress($issue, $class); |
46 | 69 | }
|
47 | 70 | }
|
48 | 71 |
|
49 |
| - // FormRequest: suppress PropertyNotSetInConstructor. |
50 |
| - if (in_array(FormRequest::class, $storage->parent_classes) && !in_array('PropertyNotSetInConstructor', $storage->suppressed_issues)) { |
51 |
| - $storage->suppressed_issues[] = 'PropertyNotSetInConstructor'; |
| 72 | + foreach (self::BY_CLASS_METHOD as $issue => $method_by_class) { |
| 73 | + foreach ($method_by_class[$class->name] ?? [] as $method_name) { |
| 74 | + self::suppress($issue, $class->methods[$method_name] ?? null); |
| 75 | + } |
52 | 76 | }
|
53 | 77 |
|
54 |
| - // Notification: suppress PropertyNotSetInConstructor. |
55 |
| - if (in_array(Notification::class, $storage->parent_classes) && !in_array('PropertyNotSetInConstructor', $storage->suppressed_issues)) { |
56 |
| - $storage->suppressed_issues[] = 'PropertyNotSetInConstructor'; |
57 |
| - } |
| 78 | + foreach (self::BY_PARENT_CLASS as $issue => $parent_classes) { |
| 79 | + if (!array_intersect($class->parent_classes, $parent_classes)) { |
| 80 | + continue; |
| 81 | + } |
58 | 82 |
|
59 |
| - // Suppress UnusedClass on well-known classes. |
60 |
| - if (in_array($storage->name, self::UNUSED_CLASSES)) { |
61 |
| - $storage->suppressed_issues[] = 'UnusedClass'; |
| 83 | + self::suppress($issue, $class); |
62 | 84 | }
|
63 | 85 |
|
64 |
| - // Suppress PossiblyUnusedMethod on well-known methods. |
65 |
| - if (array_key_exists($storage->name, self::UNUSED_METHODS)) { |
66 |
| - foreach (self::UNUSED_METHODS[$storage->name] as $method_name) { |
67 |
| - $method = $storage->methods[$method_name] ?? null; |
68 |
| - if ($method instanceof MethodStorage) { |
69 |
| - $method->suppressed_issues[] = 'PossiblyUnusedMethod'; |
| 86 | + foreach (self::BY_PARENT_CLASS_PROPERTY as $issue => $methods_by_parent_class) { |
| 87 | + foreach ($methods_by_parent_class as $parent_class => $property_names) { |
| 88 | + if (!in_array($parent_class, $class->parent_classes)) { |
| 89 | + continue; |
| 90 | + } |
| 91 | + |
| 92 | + foreach ($property_names as $property_name) { |
| 93 | + self::suppress($issue, $class->properties[$property_name] ?? null); |
70 | 94 | }
|
71 | 95 | }
|
72 | 96 | }
|
73 | 97 | }
|
| 98 | + |
| 99 | + /** |
| 100 | + * @param string $issue |
| 101 | + * @param ClassLikeStorage|PropertyStorage|MethodStorage|null $storage |
| 102 | + */ |
| 103 | + private static function suppress(string $issue, $storage): void |
| 104 | + { |
| 105 | + if ($storage && !in_array($issue, $storage->suppressed_issues)) { |
| 106 | + $storage->suppressed_issues[] = $issue; |
| 107 | + } |
| 108 | + } |
74 | 109 | }
|
0 commit comments