Skip to content

Commit 6eaf130

Browse files
author
Stephan Wentz
committed
Upgrade dependencies, add covers annotation rule
1 parent 7738391 commit 6eaf130

17 files changed

Lines changed: 352 additions & 179 deletions

composer.json

Lines changed: 45 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"name": "brainbits/phpstan-rules",
33
"description": "PHPStan extension with opinionated strict rules for better code in tests.",
4-
"keywords": [
5-
"phpstan",
6-
"phpstan-rules",
7-
"tests"
8-
],
4+
"keywords": [
5+
"phpstan",
6+
"phpstan-rules",
7+
"tests"
8+
],
99
"type": "phpstan-extension",
1010
"license": "MIT",
11-
"homepage": "https://github.com/brainbits/phpstan-rules",
11+
"homepage": "https://github.com/brainbits/phpstan-rules",
1212
"authors": [
1313
{
1414
"name": "Stephan Wentz",
@@ -23,53 +23,46 @@
2323
"phpstan/phpstan": "^0.12"
2424
},
2525
"require-dev": {
26-
"brainbits/phpcs-standard": "^3.0",
27-
"ergebnis/phpstan-rules": "^0.14.2",
28-
"ikvasnica/phpstan-clean-test": "^0.3.0",
26+
"brainbits/phpcs-standard": "^4.0",
2927
"php-coveralls/php-coveralls": "^2.0",
30-
"phpstan/phpstan-strict-rules": "~0.12",
28+
"phpstan/phpstan-php-parser": "^0.12.2",
29+
"phpstan/phpstan-phpunit": "^0.12.16",
3130
"phpunit/phpunit": "^8.5.2 || ^9.0.0"
3231
},
33-
"suggest": {
34-
"phpstan/phpstan-deprecation-rules": "Rules for disallowing deprecation code.",
35-
"phpstan/phpstan-strict-rules": "Strict and opinionated PHPStan rules.",
36-
"phpstan/phpstan-phpunit": "PHPStan extension and rules to integrate with PHPUnit testing framework.",
37-
"ergebnis/phpstan-rules": "Even more PHPStan strict and opinionated rules."
38-
},
39-
"scripts": {
40-
"check-all": [
41-
"phpcs",
42-
"@phpstan",
43-
"phpunit"
44-
],
45-
"phpstan": "phpstan analyse --ansi"
46-
},
47-
"autoload": {
48-
"psr-4": {
49-
"Brainbits\\PHPStan\\": "src/"
50-
}
51-
},
52-
"autoload-dev": {
53-
"classmap": [
54-
"tests/"
55-
]
56-
},
57-
"config": {
58-
"preferred-install": "dist",
59-
"sort-packages": true
60-
},
61-
"extra": {
62-
"branch-alias": {
63-
"dev-master": "1.0-dev"
64-
},
65-
"phpstan": {
66-
"includes": [
67-
"rules.neon"
68-
]
69-
}
70-
},
71-
"support": {
72-
"issues": "https://github.com/brainbits/phpstan-rules/issues",
73-
"source": "https://github.com/brainbits/phpstan-rules"
74-
}
32+
"scripts": {
33+
"check-all": [
34+
"phpcs",
35+
"@phpstan",
36+
"phpunit"
37+
],
38+
"phpstan": "phpstan analyse --ansi"
39+
},
40+
"autoload": {
41+
"psr-4": {
42+
"BrainbitsPhpStan\\": "src/"
43+
}
44+
},
45+
"autoload-dev": {
46+
"classmap": [
47+
"tests/"
48+
]
49+
},
50+
"config": {
51+
"preferred-install": "dist",
52+
"sort-packages": true
53+
},
54+
"extra": {
55+
"branch-alias": {
56+
"dev-master": "1.0-dev"
57+
},
58+
"phpstan": {
59+
"includes": [
60+
"rules.neon"
61+
]
62+
}
63+
},
64+
"support": {
65+
"issues": "https://github.com/brainbits/phpstan-rules/issues",
66+
"source": "https://github.com/brainbits/phpstan-rules"
67+
}
7568
}

phpcs.xml.dist

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<file>src</file>
66
<file>tests</file>
7-
<exclude-pattern>*/data/*</exclude-pattern>
7+
<exclude-pattern>*/Fixture/*</exclude-pattern>
88

99
<!-- Start here -->
1010
<arg name="basepath" value="."/>
@@ -21,16 +21,6 @@
2121
<!-- arg value="sp"/ -->
2222

2323
<!-- Use brainbits coding standard -->
24-
<rule ref="BrainbitsCodingStandard">
25-
<exclude name="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint"/>
26-
<exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint"/>
27-
<exclude name="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingAnyTypeHint"/>
28-
<exclude name="SlevomatCodingStandard.Functions.TrailingCommaInCall.MissingTrailingComma"/>
24+
<rule ref="BrainbitsCodingStandardTest">
2925
</rule>
30-
31-
<!-- Forbid suffix "Exception" for exception classes -->
32-
<rule ref="SlevomatCodingStandard.Classes.SuperfluousExceptionNaming"/>
33-
34-
<!-- Force commas after the last parameter in function or method call -->
35-
<rule ref="SlevomatCodingStandard.Functions.TrailingCommaInCall"/>
3626
</ruleset>

phpstan.neon

Lines changed: 0 additions & 21 deletions
This file was deleted.

phpstan.neon.dist

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
includes:
2+
- phar://phpstan.phar/conf/config.levelmax.neon
3+
- phar://phpstan.phar/conf/bleedingEdge.neon
4+
- vendor/phpstan/phpstan-phpunit/extension.neon
5+
- vendor/phpstan/phpstan-php-parser/extension.neon
6+
- rules.neon
7+
8+
parameters:
9+
paths:
10+
- src
11+
- tests
12+
excludes_analyse:
13+
- %currentWorkingDirectory%/tests/Fixture

phpunit.xml.dist

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1-
<phpunit
2-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3-
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
4-
colors="true"
5-
verbose="true"
6-
>
7-
<testsuites>
8-
<testsuite name="main">
9-
<directory>tests</directory>
10-
</testsuite>
11-
</testsuites>
12-
<filter>
13-
<whitelist processUncoveredFilesFromWhitelist="true">
14-
<directory suffix=".php">src</directory>
15-
</whitelist>
16-
</filter>
1+
<?xml version="1.0"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="./vendor/autoload.php" colors="true" verbose="true">
3+
<coverage processUncoveredFiles="true">
4+
<include>
5+
<directory suffix=".php">src</directory>
6+
</include>
7+
</coverage>
8+
<testsuites>
9+
<testsuite name="main">
10+
<directory>tests</directory>
11+
</testsuite>
12+
</testsuites>
1713
</phpunit>

rules.neon

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@ parametersSchema:
99

1010
services:
1111
-
12-
class: Brainbits\PHPStan\Rules\CoversAnnotationRule
12+
class: BrainbitsPhpStan\CoversAnnotationRule
1313
arguments:
1414
unitTestNamespaceContainsString: %brainbits.unitTestNamespaceContainsString%
1515
tags:
1616
- phpstan.rules.rule
1717

18+
19+
-
20+
class: BrainbitsPhpStan\CoversExistsRule
21+
tags:
22+
- phpstan.rules.rule
23+
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Brainbits\PHPStan\Rules;
5+
namespace BrainbitsPhpStan;
66

77
use Nette\Utils\Strings;
88
use PhpParser\Comment\Doc;
@@ -12,27 +12,26 @@
1212
use PHPStan\Rules\Rule;
1313
use PHPStan\Rules\RuleError;
1414
use PHPStan\Rules\RuleErrorBuilder;
15+
1516
use function array_key_exists;
1617
use function preg_match;
1718
use function preg_split;
1819
use function sha1;
1920
use function sprintf;
2021

22+
// phpcs:disable SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint
23+
2124
/**
22-
* @implements \PHPStan\Rules\Rule<\PhpParser\Node>
25+
* @implements Rule<Node>
2326
*/
2427
final class CoversAnnotationRule implements Rule
2528
{
2629
private const TEST_CLASS_ENDING_STRING = 'Test';
2730

28-
/**
29-
* @var string
30-
*/
31+
/** @var string */
3132
private $unitTestNamespaceContainsString;
3233

33-
/**
34-
* @var array<string, bool>
35-
*/
34+
/** @var array<string, bool> */
3635
private $alreadyParsedDocComments = [];
3736

3837
public function __construct(string $unitTestNamespaceContainsString)
@@ -53,7 +52,8 @@ public function processNode(Node $node, Scope $scope): array
5352
$messages = [];
5453
$lines = $this->getAnnotationLines($node, $scope);
5554

56-
$isUnitTest = $node instanceof Class_ && $node->extends
55+
$isUnitTest = $node instanceof Class_
56+
&& (bool) $node->extends
5757
&& $this->isUnitTest((string) $scope->getNamespace(), (string) $node->name, $this->unitTestNamespaceContainsString);
5858

5959
$hasCovers = false;

src/CoversExistsRule.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace BrainbitsPhpStan;
6+
7+
use PhpParser\Node;
8+
use PHPStan\Analyser\Scope;
9+
use PHPStan\Broker\Broker;
10+
use PHPStan\Rules\Rule;
11+
use PHPStan\Rules\RuleError;
12+
use PHPStan\Rules\RuleErrorBuilder;
13+
14+
use function preg_match;
15+
use function preg_split;
16+
use function sha1;
17+
use function sprintf;
18+
19+
// phpcs:disable SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint
20+
21+
/**
22+
* @implements Rule<Node>
23+
*/
24+
final class CoversExistsRule implements Rule
25+
{
26+
/** @var Broker */
27+
private $broker;
28+
/** @var bool[] */
29+
private $alreadyParsedDocComments = [];
30+
31+
public function __construct(Broker $broker)
32+
{
33+
$this->broker = $broker;
34+
}
35+
36+
public function getNodeType(): string
37+
{
38+
return Node::class;
39+
}
40+
41+
/**
42+
* @return RuleError[] errors
43+
*/
44+
public function processNode(Node $node, Scope $scope): array
45+
{
46+
$messages = [];
47+
$docComment = $node->getDocComment();
48+
if (empty($docComment)) {
49+
return $messages;
50+
}
51+
52+
$hash = sha1(sprintf(
53+
'%s:%s:%s:%s',
54+
$scope->getFile(),
55+
$docComment->getStartLine(),
56+
$docComment->getStartFilePos(),
57+
$docComment->getText()
58+
));
59+
if (isset($this->alreadyParsedDocComments[$hash])) {
60+
return $messages;
61+
}
62+
63+
$this->alreadyParsedDocComments[$hash] = true;
64+
65+
$lines = preg_split('/\R/u', $docComment->getText());
66+
if ($lines === false) {
67+
return $messages;
68+
}
69+
70+
foreach ($lines as $lineNumber => $lineContent) {
71+
$matches = [];
72+
73+
if (! preg_match('/^(?:\s*\*\s*@(?:covers|coversDefaultClass)\h+)\\\\?(?<className>\w[^:\s]*)(?:::\S+)?\s*$/u', $lineContent, $matches)) {
74+
continue;
75+
}
76+
77+
if ($this->broker->hasClass($matches['className'])) {
78+
continue;
79+
}
80+
81+
$messages[] = RuleErrorBuilder::message(sprintf('Class %s does not exist.', $matches['className']))->line($docComment->getStartLine() + $lineNumber)->build();
82+
}
83+
84+
return $messages;
85+
}
86+
}

tests/CoversAnnotationRuleTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace BrainbitsPhpStan\Tests;
6+
7+
use BrainbitsPhpStan\CoversAnnotationRule;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Testing\RuleTestCase;
10+
11+
/**
12+
* @covers \BrainbitsPhpStan\CoversAnnotationRule
13+
* @extends RuleTestCase<CoversAnnotationRule>
14+
*/
15+
final class CoversAnnotationRuleTest extends RuleTestCase
16+
{
17+
public function testRule(): void
18+
{
19+
$this->analyse([__DIR__ . '/fixture/CoversAnnotationRule/fixture-without-unit.php'], []);
20+
$this->analyse([__DIR__ . '/fixture/CoversAnnotationRule/Unit/fixture-with-unit.php'], [
21+
['No @covers or @coversDefaultClass found in test.', 17],
22+
]);
23+
}
24+
25+
protected function getRule(): Rule
26+
{
27+
return new CoversAnnotationRule('Unit');
28+
}
29+
}

0 commit comments

Comments
 (0)