-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support full github issue urls (#95)
- Loading branch information
Showing
6 changed files
with
203 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<?php | ||
|
||
namespace staabm\PHPStanTodoBy; | ||
|
||
use PhpParser\Node; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Rules\Rule; | ||
use staabm\PHPStanTodoBy\utils\CommentMatcher; | ||
use staabm\PHPStanTodoBy\utils\ExpiredCommentErrorBuilder; | ||
use staabm\PHPStanTodoBy\utils\ticket\GitHubTicketStatusFetcher; | ||
|
||
use function array_key_exists; | ||
use function in_array; | ||
use function trim; | ||
|
||
/** | ||
* @implements Rule<Node> | ||
*/ | ||
final class TodoByIssueUrlRule implements Rule | ||
{ | ||
private const ERROR_IDENTIFIER = 'url'; | ||
|
||
private const PATTERN = <<<'REGEXP' | ||
{ | ||
@?(?:TODO|FIXME|XXX) # possible @ prefix | ||
@?[a-zA-Z0-9_-]* # optional username | ||
\s*[:-]?\s* # optional colon or hyphen | ||
\s+ # keyword/version separator | ||
(?P<url>https://github.com/(?P<owner>[\S]{2,})/(?P<repo>[\S]+)/issues/(?P<issueNumber>\d+)) # url | ||
\s*[:-]?\s* # optional colon or hyphen | ||
(?P<comment>(?:(?!\*+/).)*) # rest of line as comment text, excluding block end | ||
}ix | ||
REGEXP; | ||
|
||
private ExpiredCommentErrorBuilder $errorBuilder; | ||
private GitHubTicketStatusFetcher $fetcher; | ||
|
||
public function __construct( | ||
ExpiredCommentErrorBuilder $errorBuilder, | ||
GitHubTicketStatusFetcher $fetcher | ||
) { | ||
$this->errorBuilder = $errorBuilder; | ||
$this->fetcher = $fetcher; | ||
} | ||
|
||
public function getNodeType(): string | ||
{ | ||
return Node::class; | ||
} | ||
|
||
public function processNode(Node $node, Scope $scope): array | ||
{ | ||
$it = CommentMatcher::matchComments($node, self::PATTERN); | ||
|
||
$errors = []; | ||
foreach ($it as $comment => $matches) { | ||
/** @var array<int, array<array{0: string, 1: int}>> $matches */ | ||
foreach ($matches as $match) { | ||
$url = $match['url'][0]; | ||
$owner = $match['owner'][0]; | ||
$repo = $match['repo'][0]; | ||
$issueNumber = $match['issueNumber'][0]; | ||
$todoText = trim($match['comment'][0]); | ||
$wholeMatchStartOffset = $match[0][1]; | ||
|
||
$apiUrl = $this->fetcher->buildUrl($owner, $repo, $issueNumber); | ||
$fetchedStatuses = $this->fetcher->fetchTicketStatusByUrls([$apiUrl => $apiUrl]); | ||
|
||
if (!array_key_exists($apiUrl, $fetchedStatuses) || null === $fetchedStatuses[$apiUrl]) { | ||
$errors[] = $this->errorBuilder->buildError( | ||
$comment, | ||
"Ticket $url doesn't exist or provided credentials do not allow for viewing it.", | ||
self::ERROR_IDENTIFIER, | ||
null, | ||
$wholeMatchStartOffset | ||
); | ||
|
||
continue; | ||
} | ||
|
||
$ticketStatus = $fetchedStatuses[$apiUrl]; | ||
if (!in_array($ticketStatus, GitHubTicketStatusFetcher::RESOLVED_STATUSES, true)) { | ||
continue; | ||
} | ||
|
||
if ('' !== $todoText) { | ||
$errorMessage = "Should have been resolved in {$url}: ". rtrim($todoText, '.') .'.'; | ||
} else { | ||
$errorMessage = "Comment should have been resolved with {$url}."; | ||
} | ||
|
||
$errors[] = $this->errorBuilder->buildError( | ||
$comment, | ||
$errorMessage, | ||
self::ERROR_IDENTIFIER, | ||
null, | ||
$wholeMatchStartOffset | ||
); | ||
} | ||
} | ||
|
||
return $errors; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
<?php | ||
|
||
namespace staabm\PHPStanTodoBy\Tests; | ||
|
||
use PHPStan\Rules\Rule; | ||
use PHPStan\Testing\RuleTestCase; | ||
use staabm\PHPStanTodoBy\TodoByIssueUrlRule; | ||
use staabm\PHPStanTodoBy\utils\ExpiredCommentErrorBuilder; | ||
use staabm\PHPStanTodoBy\utils\ticket\GitHubTicketStatusFetcher; | ||
|
||
/** | ||
* @extends RuleTestCase<TodoByIssueUrlRule> | ||
* @internal | ||
*/ | ||
final class TodoByIssueUrlRuleTest extends RuleTestCase | ||
{ | ||
protected function getRule(): Rule | ||
{ | ||
return new TodoByIssueUrlRule( | ||
new ExpiredCommentErrorBuilder(true), | ||
self::getContainer()->getByType(GitHubTicketStatusFetcher::class) | ||
); | ||
} | ||
|
||
/** | ||
* @param list<array{0: string, 1: int, 2?: string|null}> $errors | ||
* @dataProvider provideErrors | ||
*/ | ||
public function testRule(array $errors): void | ||
{ | ||
$this->analyse([__DIR__ . '/data/issue-urls.php'], $errors); | ||
} | ||
|
||
/** | ||
* @return iterable<array{list<array{0: string, 1: int, 2?: string|null}>}> | ||
*/ | ||
public static function provideErrors(): iterable | ||
{ | ||
yield [ | ||
[ | ||
[ | ||
'Should have been resolved in https://github.com/staabm/phpstan-todo-by/issues/47: we need todo something when this issue is resolved.', | ||
5, | ||
], | ||
[ | ||
'Comment should have been resolved with https://github.com/staabm/phpstan-todo-by/issues/47.', | ||
6, | ||
], | ||
], | ||
]; | ||
} | ||
|
||
public static function getAdditionalConfigFiles(): array | ||
{ | ||
return [ | ||
__DIR__ . '/../extension.neon', | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php | ||
|
||
namespace IssueUrls; | ||
|
||
// TODO: https://github.com/staabm/phpstan-todo-by/issues/47 we need todo something when this issue is resolved | ||
// TODO: https://github.com/staabm/phpstan-todo-by/issues/47 | ||
|
||
// FIXME: https://github.com/staabm/xhprof.io/issues/3 refencing a open issue should not trigger a error | ||
|