Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PHPStan bleeding edge compatibility #98

Merged
merged 14 commits into from
May 2, 2022
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
11 changes: 10 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,16 @@ dist: "bionic"
php:
- "8.0"
- "7.4"
- "7.1"
- "7.2"

jobs:
include:
- php: "8.1"
script:
- "composer test:syntax -- --no-progress"
- "composer test:phpunit -- --verbose"
# - "composer test:cs -- -s"
- "composer test:phpstan -- --ansi --memory-limit=1G --no-progress"

cache:
directories:
Expand Down
10 changes: 5 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
"phpstan"
],
"require": {
"php": "^7.1 || ^8.0",
"php": "^7.2 || ^8.0",
"php-stubs/wordpress-stubs": "^4.7 || ^5.0",
"phpstan/phpstan": "^1.0",
"phpstan/phpstan": "^1.6",
"symfony/polyfill-php73": "^1.12.0"
},
"require-dev": {
"composer/composer": "^2.1.12",
"composer/composer": "^2.1.14",
"dealerdirect/phpcodesniffer-composer-installer": "^0.7",
"php-parallel-lint/php-parallel-lint": "^1.1",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^7 || ^9",
"phpstan/phpstan-strict-rules": "^1.2",
"phpunit/phpunit": "^8 || ^9",
"szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^0.6"
},
"autoload": {
Expand Down
4 changes: 4 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ services:
class: SzepeViktor\PHPStan\WordPress\TermExistsDynamicFunctionReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension
-
class: SzepeViktor\PHPStan\WordPress\HookDocsVisitor
tags:
- phpstan.parser.richParserNodeVisitor
rules:
- SzepeViktor\PHPStan\WordPress\HookDocsRule
- SzepeViktor\PHPStan\WordPress\IsWpErrorRule
Expand Down
17 changes: 2 additions & 15 deletions src/HookDocBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,7 @@ public function getNullableHookDocBlock(FuncCall $functionCall, Scope $scope): ?

private static function getNullableNodeComment(FuncCall $node): ?\PhpParser\Comment\Doc
{
$startLine = $node->getStartLine();

while ($node !== null && $node->getStartLine() === $startLine) {
// Fetch the docblock from the node.
$comment = $node->getDocComment();

if ($comment !== null) {
return $comment;
}

/** @var \PhpParser\Node|null */
$node = $node->getAttribute('parent');
}

return null;
/** @var \PhpParser\Comment\Doc|null */
return $node->getAttribute('latestDocComment');
}
}
48 changes: 48 additions & 0 deletions src/HookDocsVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/**
* Custom node visitor to fetch the docblock for a function call.
*/

declare(strict_types=1);

namespace SzepeViktor\PHPStan\WordPress;

use PhpParser\Node;

final class HookDocsVisitor extends \PhpParser\NodeVisitorAbstract
{
/** @var int|null */
protected $latestStartLine = null;

/** @var \PhpParser\Comment\Doc|null */
protected $latestDocComment = null;

// phpcs:ignore SlevomatCodingStandard.Functions.UnusedParameter
public function beforeTraverse(array $nodes): ?array
{
$this->latestStartLine = null;
$this->latestDocComment = null;

return null;
}

public function enterNode(Node $node): ?Node
{
if ($node->getStartLine() !== $this->latestStartLine) {
$this->latestDocComment = null;
}

$this->latestStartLine = $node->getStartLine();

$doc = $node->getDocComment();

if ($doc !== null) {
$this->latestDocComment = $doc;
}

$node->setAttribute('latestDocComment', $this->latestDocComment);

return null;
}
}
32 changes: 21 additions & 11 deletions tests/HookDocsRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,45 +38,55 @@ public function testRule(): void
[
[
'Expected 2 @param tags, found 1.',
22,
19,
],
[
'Expected 2 @param tags, found 3.',
31,
28,
],
[
'@param string $one does not accept actual type of parameter: int|string.',
43,
40,
],
[
'@param string $one does not accept actual type of parameter: int.',
53,
50,
],
[
'@param tag must not be named $this. Choose a descriptive alias, for example $instance.',
82,
79,
],
[
'Expected 2 @param tags, found 1.',
97,
94,
],
[
'@param ChildTestClass $one does not accept actual type of parameter: ParentTestClass.',
134,
'@param SzepeViktor\PHPStan\WordPress\Tests\ChildTestClass $one does not accept actual type of parameter: SzepeViktor\PHPStan\WordPress\Tests\ParentTestClass.',
131,
],
[
'@param string $one does not accept actual type of parameter: string|null.',
155,
152,
],
[
'One or more @param tags has an invalid name or invalid syntax.',
170,
167,
],
[
'One or more @param tags has an invalid name or invalid syntax.',
206,
203,
],
[
'Expected 2 @param tags, found 1.',
214,
],
]
);
}

public static function getAdditionalConfigFiles(): array
{
// Path to your project's phpstan.neon, or extension.neon in case of custom extension packages.
return [dirname(__DIR__) . '/extension.neon'];
}
}
6 changes: 6 additions & 0 deletions tests/IsWpErrorRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,10 @@ public function testRule(): void
]
);
}

public static function getAdditionalConfigFiles(): array
{
// Path to your project's phpstan.neon, or extension.neon in case of custom extension packages.
return [dirname(__DIR__) . '/extension.neon'];
}
}
1 change: 0 additions & 1 deletion tests/data/apply_filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

use function PHPStan\Testing\assertType;
use function apply_filters;
use function returnValue;

$value = apply_filters('filter', 'Hello, World');
assertType('mixed', $value);
Expand Down
31 changes: 23 additions & 8 deletions tests/data/hook-docs.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

namespace SzepeViktor\PHPStan\WordPress\Tests;

use ChildTestClass;
use ParentTestClass;

// phpcs:disable Squiz.NamingConventions.ValidFunctionName.NotCamelCaps,Squiz.NamingConventions.ValidVariableName.NotCamelCaps,Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps

$one = 1;
Expand Down Expand Up @@ -87,7 +84,7 @@ function wide_param_type(string $one)
* @param \stdClass $instance Instance.
* @param array $args Args
*/
do_action('action', $this, $args);
do_action('action', $this, []);

/**
* This param tag renames the `$this` variable, which is fine, but still has a missing param tag.
Expand All @@ -109,7 +106,7 @@ function correct_inherited_param_type(ChildTestClass $one)
/**
* This param tag is for a super class of the variable, which is fine.
*
* @param \ParentTestClass $one First parameter.
* @param ParentTestClass $one First parameter.
*/
$args = apply_filters('filter', $one);
}
Expand All @@ -119,7 +116,7 @@ function correct_interface_param_type(ChildTestClass $one)
/**
* This param tag is for the interface of the variable, which is fine.
*
* @param \TestInterface $one First parameter.
* @param TestInterface $one First parameter.
*/
$args = apply_filters('filter', $one);
}
Expand All @@ -129,7 +126,7 @@ function incorrect_inherited_param_type(ParentTestClass $one)
/**
* This param tag is for a child class of the variable. Oh no.
*
* @param \ChildTestClass $one First parameter.
* @param ChildTestClass $one First parameter.
*/
$args = apply_filters('filter', $one);
}
Expand Down Expand Up @@ -203,8 +200,26 @@ function incorrect_nullable_param_type(?string $one = null)
* @param bool has_site_pending_automated_transfer( $this->blog_id )
* @param int $blog_id Blog identifier.
*/
return apply_filters(
$value = apply_filters(
'filter',
false,
$this->blog_id
);

/**
* This filter is wrapped inside another function call, which is weird but ok. Its param count is incorrect.
*
* @param int $number
*/
$value = intval(apply_filters('filter', 123, $foo));

/**
* This is a docblock for an unrelated function.
*
* It exists to ensure the undocumented filter below does not have its docblock inherited from this function.
*
* @param bool $yes
*/
function foo( bool $yes ) {}

$value = apply_filters('filter', 123, $foo);
6 changes: 4 additions & 2 deletions tests/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

declare(strict_types=1);

namespace SzepeViktor\PHPStan\WordPress\Tests;

/**
* Returns the passed value.
*
Expand All @@ -18,10 +20,10 @@ interface TestInterface
{
}

class ParentTestClass implements \TestInterface
class ParentTestClass implements TestInterface
{
}

class ChildTestClass extends \ParentTestClass
class ChildTestClass extends ParentTestClass
{
}