Skip to content

Commit 8c8abac

Browse files
spawniavladar
authored andcommitted
Upgrade to phpstan 0.12 (#592)
* wip phpstan 0.12 * Fix some errors, ignore others * improve generics * add required generic "casts" * Clean up phpstan.neon.dist ignoreErrors * Comment out failing type hint in NodeList.php * Strongly type Parser.php * Upgrade to phpstan 0.12.1 and remove ignored errors * Elaborate on NodeList/Visitor strict typing TODO * Revert new self -> new static * Add rationale for ignoring the new static() warning * Bump to phpstan 0.12.2 and adapt ignoreErrors
1 parent ffa8860 commit 8c8abac

23 files changed

+276
-341
lines changed

composer.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
},
1616
"require-dev": {
1717
"doctrine/coding-standard": "^6.0",
18-
"phpbench/phpbench": "^0.14.0",
19-
"phpstan/phpstan": "0.11.16",
20-
"phpstan/phpstan-phpunit": "0.11.2",
21-
"phpstan/phpstan-strict-rules": "0.11.1",
18+
"phpbench/phpbench": "^0.14",
19+
"phpstan/phpstan": "0.12.2",
20+
"phpstan/phpstan-phpunit": "0.12.1",
21+
"phpstan/phpstan-strict-rules": "0.12.0",
2222
"phpunit/phpcov": "^5.0",
2323
"phpunit/phpunit": "^7.2",
2424
"psr/http-message": "^1.0",
@@ -51,7 +51,7 @@
5151
"test": "phpunit",
5252
"lint" : "phpcs",
5353
"fix" : "phpcbf",
54-
"stan": "phpstan analyse --ansi --memory-limit 256M",
54+
"stan": "phpstan analyse --ansi --memory-limit 2048M",
5555
"check": "composer lint && composer stan && composer test"
5656
}
5757
}

phpstan.neon.dist

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,40 @@
11
parameters:
22
level: 3
33

4-
reportUnmatchedIgnoredErrors: false
5-
64
inferPrivatePropertyTypeFromConstructor: true
75

86
paths:
97
- %currentWorkingDirectory%/src
108
- %currentWorkingDirectory%/tests
119

1210
ignoreErrors:
13-
- "~Construct empty\\(\\) is not allowed\\. Use more strict comparison~"
14-
- "~(Method|Property) .+::.+(\\(\\))? (has parameter \\$\\w+ with no|has no return|has no) typehint specified~"
15-
- "~Variable property access on .+~"
11+
# Since this is a library that is supposed to be flexible, we don't
12+
# want to lock down every possible extension point.
13+
- "~Unsafe usage of new static\\(\\)~"
14+
15+
# This class uses magic methods to reduce a whole lot of boilerplate required to
16+
# allow partial parsing of language fragments.
1617
- "~Variable method call on GraphQL\\\\Language\\\\Parser\\.~"
17-
- "~Variable method call on static\\(GraphQL\\\\Server\\\\ServerConfig\\)~" # TODO get rid of
18-
- "~Only booleans are allowed in .*~" # TODO https://github.com/phpstan/phpstan-strict-rules/issues/2
19-
# A whole class of errors in PHPStan is a result of PHP's lack of union types.
20-
# This commonly happens in the parts of the code that deal with the GraphQL
21-
# type system where we can currently use interfaces and lose type safety.
22-
# Until we find a better way, we can list related error's here.
23-
- "~Access to an undefined property GraphQL\\\\Type\\\\Definition\\\\NamedType::\\$name~"
18+
2419
# Those come from graphql-php\tests\Language\VisitorTest.php
2520
- "~Access to an undefined property GraphQL\\\\Language\\\\AST\\\\.+::\\$didEnter~"
2621
- "~Access to an undefined property GraphQL\\\\Language\\\\AST\\\\.+::\\$didLeave~"
2722
- "~Access to an undefined property GraphQL\\\\Language\\\\AST\\\\Node::\\$value~"
2823

24+
# TODO fix those errors to improve type safety
25+
- "~Construct empty\\(\\) is not allowed\\. Use more strict comparison~"
26+
- "~Variable property access on .+~"
27+
- "~Anonymous function should have native return typehint~"
28+
- "~Anonymous function should return .* but return statement is missing.~"
29+
- "~Anonymous function sometimes return something but return statement at the end is missing.~"
30+
- "~Short ternary operator is not allowed. Use null coalesce operator if applicable or consider using long ternary.~"
31+
32+
# TODO convert to less magical code
33+
- "~Variable method call on static\\(GraphQL\\\\Server\\\\ServerConfig\\)~"
34+
35+
# TODO cast booleans explicitly https://github.com/phpstan/phpstan-strict-rules/issues/2
36+
- "~Only booleans are allowed in .*~"
37+
2938
includes:
3039
- vendor/phpstan/phpstan-phpunit/extension.neon
3140
- vendor/phpstan/phpstan-phpunit/rules.neon

src/Experimental/Executor/Collector.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,24 @@
66

77
use Generator;
88
use GraphQL\Error\Error;
9+
use GraphQL\Language\AST\BooleanValueNode;
910
use GraphQL\Language\AST\DefinitionNode;
1011
use GraphQL\Language\AST\DocumentNode;
12+
use GraphQL\Language\AST\EnumValueNode;
1113
use GraphQL\Language\AST\FieldNode;
14+
use GraphQL\Language\AST\FloatValueNode;
1215
use GraphQL\Language\AST\FragmentDefinitionNode;
1316
use GraphQL\Language\AST\FragmentSpreadNode;
1417
use GraphQL\Language\AST\InlineFragmentNode;
18+
use GraphQL\Language\AST\IntValueNode;
19+
use GraphQL\Language\AST\ListValueNode;
1520
use GraphQL\Language\AST\Node;
21+
use GraphQL\Language\AST\NullValueNode;
22+
use GraphQL\Language\AST\ObjectValueNode;
1623
use GraphQL\Language\AST\OperationDefinitionNode;
1724
use GraphQL\Language\AST\SelectionSetNode;
25+
use GraphQL\Language\AST\StringValueNode;
26+
use GraphQL\Language\AST\VariableNode;
1827
use GraphQL\Type\Definition\AbstractType;
1928
use GraphQL\Type\Definition\Directive;
2029
use GraphQL\Type\Definition\ObjectType;

src/Language/AST/DocumentNode.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ class DocumentNode extends Node
99
/** @var string */
1010
public $kind = NodeKind::DOCUMENT;
1111

12-
/** @var NodeList|array<DefinitionNode> */
12+
/** @var NodeList<DefinitionNode&Node> */
1313
public $definitions;
1414
}

src/Language/AST/EnumTypeDefinitionNode.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class EnumTypeDefinitionNode extends Node implements TypeDefinitionNode
1515
/** @var DirectiveNode[] */
1616
public $directives;
1717

18-
/** @var EnumValueDefinitionNode[]|NodeList|null */
18+
/** @var NodeList<EnumValueDefinitionNode>|null */
1919
public $values;
2020

2121
/** @var StringValueNode|null */

src/Language/AST/FieldDefinitionNode.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ class FieldDefinitionNode extends Node
1212
/** @var NameNode */
1313
public $name;
1414

15-
/** @var InputValueDefinitionNode[]|NodeList */
15+
/** @var NodeList<InputValueDefinitionNode> */
1616
public $arguments;
1717

1818
/** @var NamedTypeNode|ListTypeNode|NonNullTypeNode */
1919
public $type;
2020

21-
/** @var DirectiveNode[]|NodeList */
21+
/** @var NodeList<DirectiveNode> */
2222
public $directives;
2323

2424
/** @var StringValueNode|null */

src/Language/AST/FragmentDefinitionNode.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ class FragmentDefinitionNode extends Node implements ExecutableDefinitionNode, H
1616
* Note: fragment variable definitions are experimental and may be changed
1717
* or removed in the future.
1818
*
19-
* @var VariableDefinitionNode[]|NodeList
19+
* @var NodeList<VariableDefinitionNode>
2020
*/
2121
public $variableDefinitions;
2222

2323
/** @var NamedTypeNode */
2424
public $typeCondition;
2525

26-
/** @var DirectiveNode[]|NodeList */
26+
/** @var NodeList<DirectiveNode> */
2727
public $directives;
2828

2929
/** @var SelectionSetNode */

src/Language/AST/ListValueNode.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ class ListValueNode extends Node implements ValueNode
99
/** @var string */
1010
public $kind = NodeKind::LST;
1111

12-
/** @var ValueNode[]|NodeList */
12+
/** @var NodeList<ValueNode&Node> */
1313
public $values;
1414
}

src/Language/AST/NodeList.php

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,101 +6,132 @@
66

77
use ArrayAccess;
88
use Countable;
9-
use Generator;
109
use GraphQL\Utils\AST;
10+
use InvalidArgumentException;
1111
use IteratorAggregate;
12+
use Traversable;
1213
use function array_merge;
1314
use function array_splice;
1415
use function count;
1516
use function is_array;
1617

18+
/**
19+
* @template T of Node
20+
* @phpstan-implements ArrayAccess<int|string, T>
21+
* @phpstan-implements IteratorAggregate<T>
22+
*/
1723
class NodeList implements ArrayAccess, IteratorAggregate, Countable
1824
{
19-
/** @var Node[]|mixed[] */
25+
/**
26+
* @var Node[]
27+
* @phpstan-var array<T>
28+
*/
2029
private $nodes;
2130

2231
/**
23-
* @param Node[]|mixed[] $nodes
32+
* @param Node[] $nodes
2433
*
25-
* @return static
34+
* @phpstan-param array<T> $nodes
35+
* @phpstan-return self<T>
2636
*/
27-
public static function create(array $nodes)
37+
public static function create(array $nodes) : self
2838
{
2939
return new static($nodes);
3040
}
3141

3242
/**
33-
* @param Node[]|mixed[] $nodes
43+
* @param Node[] $nodes
44+
*
45+
* @phpstan-param array<T> $nodes
3446
*/
3547
public function __construct(array $nodes)
3648
{
3749
$this->nodes = $nodes;
3850
}
3951

4052
/**
41-
* @param mixed $offset
42-
*
43-
* @return bool
53+
* @param int|string $offset
4454
*/
45-
public function offsetExists($offset)
55+
public function offsetExists($offset) : bool
4656
{
4757
return isset($this->nodes[$offset]);
4858
}
4959

5060
/**
51-
* @param mixed $offset
61+
* TODO enable strict typing by changing how the Visitor deals with NodeList.
62+
* Ideally, this function should always return a Node instance.
63+
* However, the Visitor currently allows mutation of the NodeList
64+
* and puts arbitrary values in the NodeList, such as strings.
65+
* We will have to switch to using an array or a less strict
66+
* type instead so we can enable strict typing in this class.
67+
*
68+
* @param int|string $offset
5269
*
53-
* @return mixed
70+
* @phpstan-return T
5471
*/
55-
public function offsetGet($offset)
72+
public function offsetGet($offset)// : Node
5673
{
5774
$item = $this->nodes[$offset];
5875

5976
if (is_array($item) && isset($item['kind'])) {
60-
$this->nodes[$offset] = $item = AST::fromArray($item);
77+
/** @phpstan-var T $node */
78+
$node = AST::fromArray($item);
79+
$this->nodes[$offset] = $node;
6180
}
6281

63-
return $item;
82+
return $this->nodes[$offset];
6483
}
6584

6685
/**
67-
* @param mixed $offset
68-
* @param mixed $value
86+
* @param int|string $offset
87+
* @param Node|mixed[] $value
88+
*
89+
* @phpstan-param T|mixed[] $value
6990
*/
70-
public function offsetSet($offset, $value)
91+
public function offsetSet($offset, $value) : void
7192
{
72-
if (is_array($value) && isset($value['kind'])) {
73-
$value = AST::fromArray($value);
93+
if (is_array($value)) {
94+
if (isset($value['kind'])) {
95+
/** @phpstan-var T $node */
96+
$node = AST::fromArray($value);
97+
$this->nodes[$offset] = $node;
98+
99+
return;
100+
}
101+
102+
throw new InvalidArgumentException(
103+
'Expected array value to be valid node data structure, missing key "kind"'
104+
);
74105
}
106+
75107
$this->nodes[$offset] = $value;
76108
}
77109

78110
/**
79-
* @param mixed $offset
111+
* @param int|string $offset
80112
*/
81-
public function offsetUnset($offset)
113+
public function offsetUnset($offset) : void
82114
{
83115
unset($this->nodes[$offset]);
84116
}
85117

86118
/**
87-
* @param int $offset
88-
* @param int $length
89119
* @param mixed $replacement
90120
*
91-
* @return NodeList
121+
* @phpstan-return NodeList<T>
92122
*/
93-
public function splice($offset, $length, $replacement = null)
123+
public function splice(int $offset, int $length, $replacement = null) : NodeList
94124
{
95125
return new NodeList(array_splice($this->nodes, $offset, $length, $replacement));
96126
}
97127

98128
/**
99129
* @param NodeList|Node[] $list
100130
*
101-
* @return NodeList
131+
* @phpstan-param NodeList<T>|array<T> $list
132+
* @phpstan-return NodeList<T>
102133
*/
103-
public function merge($list)
134+
public function merge($list) : NodeList
104135
{
105136
if ($list instanceof self) {
106137
$list = $list->nodes;
@@ -109,20 +140,14 @@ public function merge($list)
109140
return new NodeList(array_merge($this->nodes, $list));
110141
}
111142

112-
/**
113-
* @return Generator
114-
*/
115-
public function getIterator()
143+
public function getIterator() : Traversable
116144
{
117145
foreach ($this->nodes as $key => $_) {
118146
yield $this->offsetGet($key);
119147
}
120148
}
121149

122-
/**
123-
* @return int
124-
*/
125-
public function count()
150+
public function count() : int
126151
{
127152
return count($this->nodes);
128153
}

src/Language/AST/ObjectValueNode.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ class ObjectValueNode extends Node implements ValueNode
99
/** @var string */
1010
public $kind = NodeKind::OBJECT;
1111

12-
/** @var ObjectFieldNode[]|NodeList */
12+
/** @var NodeList<ObjectFieldNode> */
1313
public $fields;
1414
}

0 commit comments

Comments
 (0)