Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/static-rules.neon
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ rules:
- Symplify\PHPStanRules\Rules\NoReferenceRule
- Symplify\PHPStanRules\Rules\ForbiddenStaticClassConstFetchRule
- Symplify\PHPStanRules\Rules\Complexity\ForbiddenArrayMethodCallRule

# docblock
- Symplify\PHPStanRules\Rules\NoMissnamedDocTagRule
2 changes: 2 additions & 0 deletions src/Enum/RuleIdentifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,6 @@ final class RuleIdentifier
public const RULE_IDENTIFIER = 'symplify.foreachCeption';

public const NO_MISSING_VARIABLE_DIM_FETCH = 'symplify.noMissingVariableDimFetch';

public const NO_MISSNAMED_DOC_TAG = 'symplify.noMissnamedDocTag';
}
103 changes: 103 additions & 0 deletions src/Rules/NoMissnamedDocTagRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

declare(strict_types=1);

namespace Symplify\PHPStanRules\Rules;

use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use Symplify\PHPStanRules\Enum\RuleIdentifier;

/**
* @implements Rule<Class_>
*
* @see \Symplify\PHPStanRules\Tests\Rules\NoMissnamedDocTagRule\NoMissnamedDocTagRuleTest
*/
final class NoMissnamedDocTagRule implements Rule
{
/**
* @api used in tests
*/
public const CONSTANT_ERROR_MESSAGE = 'Constant doc comment tag must be @var, "%s" given';

/**
* @api used in tests
*/
public const PROPERTY_ERROR_MESSAGE = 'Property doc comment tag must be @var, "%s" given';

/**
* @api used in tests
*/
public const METHOD_ERROR_MESSAGE = 'Method doc comment tag must be @param or @return, "%s" given';

public function getNodeType(): string
{
return Class_::class;
}

/**
* @param Class_ $node
* @return array<RuleError>
*/
public function processNode(Node $node, Scope $scope): array
{
$ruleErrors = [];

foreach ($node->getMethods() as $classMethod) {
// match "@return" and "@param" tags
if ($classMethod->getDocComment() === null) {
continue;
}

$matches = Strings::match($classMethod->getDocComment()->getText(), '#(@var)\b#mi');
if ($matches === null) {
continue;
}

$ruleErrors[] = RuleErrorBuilder::message(sprintf(self::METHOD_ERROR_MESSAGE, $matches[1]))
->identifier(RuleIdentifier::NO_MISSNAMED_DOC_TAG)
->line($classMethod->getStartLine())
->build();
}

foreach ($node->getProperties() as $property) {
// match "@return" and "@param" tags
if ($property->getDocComment() === null) {
continue;
}

$matches = Strings::match($property->getDocComment()->getText(), '#(@param|@return)\b#mi');
if ($matches === null) {
continue;
}

$ruleErrors[] = RuleErrorBuilder::message(sprintf(self::PROPERTY_ERROR_MESSAGE, $matches[1]))
->identifier(RuleIdentifier::NO_MISSNAMED_DOC_TAG)
->line($property->getStartLine())
->build();
}

foreach ($node->getConstants() as $classConst) {
if ($classConst->getDocComment() === null) {
continue;
}

$matches = Strings::match($classConst->getDocComment()->getText(), '#(@param|@return)\b#mi');
if ($matches === null) {
continue;
}

$ruleErrors[] = RuleErrorBuilder::message(sprintf(self::CONSTANT_ERROR_MESSAGE, $matches[1]))
->identifier(RuleIdentifier::NO_MISSNAMED_DOC_TAG)
->line($classConst->getStartLine())
->build();
}

return $ruleErrors;
}
}
15 changes: 15 additions & 0 deletions tests/Rules/NoMissnamedDocTagRule/Fixture/ClassMethodNonReturn.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Symplify\PHPStanRules\Tests\Rules\NoMissnamedDocTagRule\Fixture;

final class ClassMethodNonReturn
{
/**
* @var string
*/
public function run()
{
}
}
13 changes: 13 additions & 0 deletions tests/Rules/NoMissnamedDocTagRule/Fixture/SkipValidPropertyTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Symplify\PHPStanRules\Tests\Rules\NoMissnamedDocTagRule\Fixture;

class SkipValidPropertyTag
{
/**
* @var string
*/
private $property;
}
13 changes: 13 additions & 0 deletions tests/Rules/NoMissnamedDocTagRule/Fixture/SomeClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Symplify\PHPStanRules\Tests\Rules\NoMissnamedDocTagRule\Fixture;

class SomeClass
{
/**
* @return string
*/
private $property;
}
13 changes: 13 additions & 0 deletions tests/Rules/NoMissnamedDocTagRule/Fixture/SomeConstant.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Symplify\PHPStanRules\Tests\Rules\NoMissnamedDocTagRule\Fixture;

class SomeConstant
{
/**
* @return string
*/
private const NAME = 'value';
}
56 changes: 56 additions & 0 deletions tests/Rules/NoMissnamedDocTagRule/NoMissnamedDocTagRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace Symplify\PHPStanRules\Tests\Rules\NoMissnamedDocTagRule;

use Iterator;
use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;
use PHPUnit\Framework\Attributes\DataProvider;
use Symplify\PHPStanRules\Rules\NoMissnamedDocTagRule;

final class NoMissnamedDocTagRuleTest extends RuleTestCase
{
/**
* @param array<int, array<string|int>> $expectedErrorMessagesWithLines
*/
#[DataProvider('provideData')]
public function testRule(string $filePath, array $expectedErrorMessagesWithLines): void
{
$this->analyse([$filePath], $expectedErrorMessagesWithLines);
}

/**
* @return Iterator<mixed>
*/
public static function provideData(): Iterator
{
yield [__DIR__ . '/Fixture/ClassMethodNonReturn.php', [
[sprintf(NoMissnamedDocTagRule::METHOD_ERROR_MESSAGE, '@var'), 12],
]];

yield [__DIR__ . '/Fixture/SomeClass.php', [
[sprintf(NoMissnamedDocTagRule::PROPERTY_ERROR_MESSAGE, '@return'), 12],
]];

yield [__DIR__ . '/Fixture/SomeConstant.php', [
[sprintf(NoMissnamedDocTagRule::CONSTANT_ERROR_MESSAGE, '@return'), 12],
]];

yield [__DIR__ . '/Fixture/SkipValidPropertyTag.php', []];
}

/**
* @return array<int, string>
*/
public static function getAdditionalConfigFiles(): array
{
return [__DIR__ . '/config/configured_rule.neon'];
}

protected function getRule(): Rule
{
return self::getContainer()->getByType(NoMissnamedDocTagRule::class);
}
}
5 changes: 5 additions & 0 deletions tests/Rules/NoMissnamedDocTagRule/config/configured_rule.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
includes:
- ../../../config/included_services.neon

rules:
- Symplify\PHPStanRules\Rules\NoMissnamedDocTagRule