OcpSinceChecker.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright 2023 Daniel Kesselberg <mail@danielkesselberg.de>
  5. *
  6. * @author 2023 Daniel Kesselberg <mail@danielkesselberg.de>
  7. *
  8. * This code is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Affero General Public License, version 3,
  10. * as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License, version 3,
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>
  19. *
  20. */
  21. use PhpParser\Node\Stmt;
  22. use PhpParser\Node\Stmt\ClassLike;
  23. use Psalm\CodeLocation;
  24. use Psalm\DocComment;
  25. use Psalm\Exception\DocblockParseException;
  26. use Psalm\FileSource;
  27. use Psalm\Issue\InvalidDocblock;
  28. use Psalm\IssueBuffer;
  29. use Psalm\Plugin\EventHandler\Event\AfterClassLikeVisitEvent;
  30. class OcpSinceChecker implements Psalm\Plugin\EventHandler\AfterClassLikeVisitInterface {
  31. public static function afterClassLikeVisit(AfterClassLikeVisitEvent $event): void {
  32. $stmt = $event->getStmt();
  33. $statementsSource = $event->getStatementsSource();
  34. self::checkClassComment($stmt, $statementsSource);
  35. foreach ($stmt->getMethods() as $method) {
  36. self::checkMethodOrConstantComment($method, $statementsSource, 'method');
  37. }
  38. foreach ($stmt->getConstants() as $constant) {
  39. self::checkMethodOrConstantComment($constant, $statementsSource, 'constant');
  40. }
  41. }
  42. private static function checkClassComment(ClassLike $stmt, FileSource $statementsSource): void {
  43. $docblock = $stmt->getDocComment();
  44. if ($docblock === null) {
  45. IssueBuffer::maybeAdd(
  46. new InvalidDocblock(
  47. 'PHPDoc is required for classes/interfaces in OCP.',
  48. new CodeLocation($statementsSource, $stmt)
  49. )
  50. );
  51. return;
  52. }
  53. try {
  54. $parsedDocblock = DocComment::parsePreservingLength($docblock);
  55. } catch (DocblockParseException $e) {
  56. IssueBuffer::maybeAdd(
  57. new InvalidDocblock(
  58. $e->getMessage(),
  59. new CodeLocation($statementsSource, $stmt)
  60. )
  61. );
  62. return;
  63. }
  64. if (!isset($parsedDocblock->tags['since'])) {
  65. IssueBuffer::maybeAdd(
  66. new InvalidDocblock(
  67. '@since is required for classes/interfaces in OCP.',
  68. new CodeLocation($statementsSource, $stmt)
  69. )
  70. );
  71. }
  72. if (isset($parsedDocblock->tags['depreacted'])) {
  73. IssueBuffer::maybeAdd(
  74. new InvalidDocblock(
  75. 'Typo in @deprecated for classes/interfaces in OCP.',
  76. new CodeLocation($statementsSource, $stmt)
  77. )
  78. );
  79. }
  80. }
  81. private static function checkMethodOrConstantComment(Stmt $stmt, FileSource $statementsSource, string $type): void {
  82. $docblock = $stmt->getDocComment();
  83. if ($docblock === null) {
  84. IssueBuffer::maybeAdd(
  85. new InvalidDocblock(
  86. 'PHPDoc is required for ' . $type . 's in OCP.',
  87. new CodeLocation($statementsSource, $stmt)
  88. ),
  89. );
  90. return;
  91. }
  92. try {
  93. $parsedDocblock = DocComment::parsePreservingLength($docblock);
  94. } catch (DocblockParseException $e) {
  95. IssueBuffer::maybeAdd(
  96. new InvalidDocblock(
  97. $e->getMessage(),
  98. new CodeLocation($statementsSource, $stmt)
  99. )
  100. );
  101. return;
  102. }
  103. if (!isset($parsedDocblock->tags['since'])) {
  104. IssueBuffer::maybeAdd(
  105. new InvalidDocblock(
  106. '@since is required for ' . $type . 's in OCP.',
  107. new CodeLocation($statementsSource, $stmt)
  108. )
  109. );
  110. }
  111. if (isset($parsedDocblock->tags['depreacted'])) {
  112. IssueBuffer::maybeAdd(
  113. new InvalidDocblock(
  114. 'Typo in @deprecated for ' . $type . ' in OCP.',
  115. new CodeLocation($statementsSource, $stmt)
  116. )
  117. );
  118. }
  119. }
  120. }