OcpSinceChecker.php 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. use PhpParser\Node\Stmt;
  8. use PhpParser\Node\Stmt\ClassLike;
  9. use Psalm\CodeLocation;
  10. use Psalm\DocComment;
  11. use Psalm\Exception\DocblockParseException;
  12. use Psalm\FileSource;
  13. use Psalm\Issue\InvalidDocblock;
  14. use Psalm\IssueBuffer;
  15. use Psalm\Plugin\EventHandler\Event\AfterClassLikeVisitEvent;
  16. class OcpSinceChecker implements Psalm\Plugin\EventHandler\AfterClassLikeVisitInterface {
  17. public static function afterClassLikeVisit(AfterClassLikeVisitEvent $event): void {
  18. $stmt = $event->getStmt();
  19. $statementsSource = $event->getStatementsSource();
  20. self::checkClassComment($stmt, $statementsSource);
  21. foreach ($stmt->getMethods() as $method) {
  22. self::checkMethodOrConstantComment($method, $statementsSource, 'method');
  23. }
  24. foreach ($stmt->getConstants() as $constant) {
  25. self::checkMethodOrConstantComment($constant, $statementsSource, 'constant');
  26. }
  27. }
  28. private static function checkClassComment(ClassLike $stmt, FileSource $statementsSource): void {
  29. $docblock = $stmt->getDocComment();
  30. if ($docblock === null) {
  31. IssueBuffer::maybeAdd(
  32. new InvalidDocblock(
  33. 'PHPDoc is required for classes/interfaces in OCP.',
  34. new CodeLocation($statementsSource, $stmt)
  35. )
  36. );
  37. return;
  38. }
  39. try {
  40. $parsedDocblock = DocComment::parsePreservingLength($docblock);
  41. } catch (DocblockParseException $e) {
  42. IssueBuffer::maybeAdd(
  43. new InvalidDocblock(
  44. $e->getMessage(),
  45. new CodeLocation($statementsSource, $stmt)
  46. )
  47. );
  48. return;
  49. }
  50. if (!isset($parsedDocblock->tags['since'])) {
  51. IssueBuffer::maybeAdd(
  52. new InvalidDocblock(
  53. '@since is required for classes/interfaces in OCP.',
  54. new CodeLocation($statementsSource, $stmt)
  55. )
  56. );
  57. }
  58. if (isset($parsedDocblock->tags['depreacted'])) {
  59. IssueBuffer::maybeAdd(
  60. new InvalidDocblock(
  61. 'Typo in @deprecated for classes/interfaces in OCP.',
  62. new CodeLocation($statementsSource, $stmt)
  63. )
  64. );
  65. }
  66. }
  67. private static function checkMethodOrConstantComment(Stmt $stmt, FileSource $statementsSource, string $type): void {
  68. $docblock = $stmt->getDocComment();
  69. if ($docblock === null) {
  70. IssueBuffer::maybeAdd(
  71. new InvalidDocblock(
  72. 'PHPDoc is required for ' . $type . 's in OCP.',
  73. new CodeLocation($statementsSource, $stmt)
  74. ),
  75. );
  76. return;
  77. }
  78. try {
  79. $parsedDocblock = DocComment::parsePreservingLength($docblock);
  80. } catch (DocblockParseException $e) {
  81. IssueBuffer::maybeAdd(
  82. new InvalidDocblock(
  83. $e->getMessage(),
  84. new CodeLocation($statementsSource, $stmt)
  85. )
  86. );
  87. return;
  88. }
  89. if (!isset($parsedDocblock->tags['since'])) {
  90. IssueBuffer::maybeAdd(
  91. new InvalidDocblock(
  92. '@since is required for ' . $type . 's in OCP.',
  93. new CodeLocation($statementsSource, $stmt)
  94. )
  95. );
  96. }
  97. if (isset($parsedDocblock->tags['depreacted'])) {
  98. IssueBuffer::maybeAdd(
  99. new InvalidDocblock(
  100. 'Typo in @deprecated for ' . $type . ' in OCP.',
  101. new CodeLocation($statementsSource, $stmt)
  102. )
  103. );
  104. }
  105. }
  106. }