Failed Conditions
Push — mixed-collections ( 23b545 )
by Michael
03:24
created

AstBuilder::visitList()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 1
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Annotations\Parser\Visitor;
6
7
use Doctrine\Annotations\Parser\Ast\Annotation;
8
use Doctrine\Annotations\Parser\Ast\Annotations;
9
use Doctrine\Annotations\Parser\Ast\ClassConstantFetch;
10
use Doctrine\Annotations\Parser\Ast\Collection\Collection;
11
use Doctrine\Annotations\Parser\Ast\Collection\Entry;
12
use Doctrine\Annotations\Parser\Ast\ConstantFetch;
13
use Doctrine\Annotations\Parser\Ast\Node;
14
use Doctrine\Annotations\Parser\Ast\Pair;
15
use Doctrine\Annotations\Parser\Ast\Parameter\NamedParameter;
16
use Doctrine\Annotations\Parser\Ast\Parameter\UnnamedParameter;
17
use Doctrine\Annotations\Parser\Ast\Parameters;
18
use Doctrine\Annotations\Parser\Ast\Reference;
19
use Doctrine\Annotations\Parser\Ast\Scalar;
20
use Doctrine\Annotations\Parser\Ast\Scalar\BooleanScalar;
21
use Doctrine\Annotations\Parser\Ast\Scalar\FloatScalar;
22
use Doctrine\Annotations\Parser\Ast\Scalar\Identifier;
23
use Doctrine\Annotations\Parser\Ast\Scalar\IntegerScalar;
24
use Doctrine\Annotations\Parser\Ast\Scalar\NullScalar;
25
use Doctrine\Annotations\Parser\Ast\Scalar\StringScalar;
26
use Doctrine\Annotations\Parser\Nodes;
27
use Doctrine\Annotations\Parser\Tokens;
28
use Hoa\Compiler\Llk\TreeNode;
29
use Hoa\Visitor\Element;
30
use Hoa\Visitor\Visit;
31
use function assert;
32
use function ltrim;
33
use function sprintf;
34
use function str_replace;
35
use function strcasecmp;
36
37
final class AstBuilder implements Visit
38
{
39
    /**
40
     * @param mixed $handle
41
     * @param mixed $eldnah
42
     */
43
    public function visit(Element $node, &$handle = null, $eldnah = null) : Node
44
    {
45
        assert($node instanceof TreeNode);
46
47
        if ($node->isToken()) {
48
            return $this->visitToken($node);
49
        }
50
51
        return $this->visitNode($node);
52
    }
53
54
    private function visitNode(TreeNode $node) : Node
55
    {
56
        switch ($node->getId()) {
57
            case Nodes::ANNOTATIONS:
58
                return $this->visitAnnotations($node);
59
            case Nodes::ANNOTATION:
60
                return $this->visitAnnotation($node);
61
            case Nodes::COLLECTION:
62
                return $this->visitCollection($node);
63
            case Nodes::COLLECTION_ENTRY:
64
                return $this->visitCollectionEntry($node);
65
            case Nodes::PAIR:
66
                return $this->visitPair($node);
67
            case Nodes::PARAMETERS:
68
                return $this->visitParameters($node);
69
            case Nodes::NAMED_PARAMETERS:
70
                return $this->visitNamedParameter($node);
71
            case Nodes::UNNAMED_PARAMETERS:
72
                return $this->visitUnnamedParameter($node);
73
            case Nodes::VALUE:
74
                return $this->visitValue($node);
75
            case Nodes::STANDALONE_CONSTANT:
76
                return $this->visitStandaloneConstant($node);
77
            case Nodes::CLASS_CONSTANT:
78
                return $this->visitClassConstant($node);
79
            case Nodes::REFERENCE:
80
                return $this->visitReference($node);
81
            case Nodes::STRING:
82
                return $this->visitString($node);
83
        }
84
85
        assert(false, sprintf('Unsupported node %s.', $node->getId()));
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Doctrine\Annotations\Parser\Ast\Node. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
86
    }
87
88
    private function visitToken(TreeNode $node) : Scalar
89
    {
90
        $value = $node->getValueValue();
91
92
        switch ($node->getValueToken()) {
93
            case Tokens::IDENTIFIER:
94
                return new Identifier($value);
95
            case Tokens::NULL:
96
                return new NullScalar();
97
            case Tokens::BOOLEAN:
98
                return new BooleanScalar(strcasecmp($value, 'true') === 0);
99
            case Tokens::INTEGER:
100
                $intValue = (int) $value;
101
                assert((string) $intValue === $value, 'Integer overflow');
102
103
                return new IntegerScalar($intValue);
104
            case Tokens::FLOAT:
105
                return new FloatScalar((float) $value);
106
        }
107
108
        assert(false, sprintf('Unsupported token %s.', $node->getValueToken()));
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Doctrine\Annotations\Parser\Ast\Scalar. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
109
    }
110
111
    private function visitAnnotations(TreeNode $node) : Annotations
112
    {
113
        return new Annotations(
114
            ...(function (TreeNode ...$nodes) : iterable {
115
                foreach ($nodes as $node) {
116
                    yield $node->accept($this);
117
                }
118
            })(...$node->getChildren())
119
        );
120
    }
121
122
    private function visitAnnotation(TreeNode $node) : Annotation
123
    {
124
        $identifier = $node->getChild(0)->getValueValue();
125
126
        return new Annotation(
127
            new Reference(ltrim($identifier, '\\'), $identifier[0] === '\\'),
128
            $node->childExists(1) ? $node->getChild(1)->accept($this) : new Parameters()
0 ignored issues
show
Bug introduced by
It seems like $node->childExists(1) ? ...Parser\Ast\Parameters() can also be of type Doctrine\Annotations\Par...st\Scalar\BooleanScalar and Doctrine\Annotations\Parser\Ast\Scalar\FloatScalar and Doctrine\Annotations\Parser\Ast\Scalar\Identifier and Doctrine\Annotations\Par...st\Scalar\IntegerScalar and Doctrine\Annotations\Parser\Ast\Scalar\NullScalar; however, parameter $parameters of Doctrine\Annotations\Par...notation::__construct() does only seem to accept Doctrine\Annotations\Parser\Ast\Parameters, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

128
            /** @scrutinizer ignore-type */ $node->childExists(1) ? $node->getChild(1)->accept($this) : new Parameters()
Loading history...
129
        );
130
    }
131
132
    private function visitPair(TreeNode $node) : Pair
133
    {
134
        return new Pair(
135
            $node->getChild(0)->accept($this),
136
            $node->getChild(1)->accept($this)
137
        );
138
    }
139
140
    private function visitParameters(TreeNode $node) : Parameters
141
    {
142
        return new Parameters(
143
            ...(function (TreeNode ...$nodes) : iterable {
144
                foreach ($nodes as $node) {
145
                    yield $node->accept($this);
146
                }
147
            })(...$node->getChildren())
148
        );
149
    }
150
151
    private function visitNamedParameter(TreeNode $node) : NamedParameter
152
    {
153
        return new NamedParameter(
154
            $node->getChild(0)->accept($this),
0 ignored issues
show
Bug introduced by
It seems like $node->getChild(0)->accept($this) can also be of type Doctrine\Annotations\Par...st\Scalar\BooleanScalar and Doctrine\Annotations\Parser\Ast\Scalar\FloatScalar and Doctrine\Annotations\Par...st\Scalar\IntegerScalar and Doctrine\Annotations\Parser\Ast\Scalar\NullScalar; however, parameter $name of Doctrine\Annotations\Par...arameter::__construct() does only seem to accept Doctrine\Annotations\Parser\Ast\Scalar\Identifier, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

154
            /** @scrutinizer ignore-type */ $node->getChild(0)->accept($this),
Loading history...
155
            $node->getChild(1)->accept($this)
156
        );
157
    }
158
159
    private function visitUnnamedParameter(TreeNode $node) : UnnamedParameter
160
    {
161
        return new UnnamedParameter($node->getChild(0)->accept($this));
162
    }
163
164
    private function visitValue(TreeNode $node) : Node
165
    {
166
        return $node->getChild(0)->accept($this);
167
    }
168
169
    private function visitCollection(TreeNode $node) : Collection
170
    {
171
        return new Collection(
172
            ...(function (TreeNode ...$nodes) : iterable {
173
                foreach ($nodes as $node) {
174
                    yield $node->accept($this);
175
                }
176
            })(...$node->getChildren())
177
        );
178
    }
179
180
    private function visitCollectionEntry(TreeNode $node) : Entry
181
    {
182
        if ($node->getChildrenNumber() === 1) {
183
            return new Entry(null, $node->getChild(0)->accept($this));
184
        }
185
186
        return new Entry(
187
            $node->getChild(0)->accept($this),
188
            $node->getChild(1)->accept($this)
189
        );
190
    }
191
192
    private function visitStandaloneConstant(TreeNode $node) : ConstantFetch
193
    {
194
        return new ConstantFetch($node->getChild(0)->accept($this));
0 ignored issues
show
Bug introduced by
It seems like $node->getChild(0)->accept($this) can also be of type Doctrine\Annotations\Par...st\Scalar\BooleanScalar and Doctrine\Annotations\Parser\Ast\Scalar\FloatScalar and Doctrine\Annotations\Par...st\Scalar\IntegerScalar and Doctrine\Annotations\Parser\Ast\Scalar\NullScalar; however, parameter $name of Doctrine\Annotations\Par...antFetch::__construct() does only seem to accept Doctrine\Annotations\Parser\Ast\Scalar\Identifier, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

194
        return new ConstantFetch(/** @scrutinizer ignore-type */ $node->getChild(0)->accept($this));
Loading history...
195
    }
196
197
    private function visitClassConstant(TreeNode $node) : ClassConstantFetch
198
    {
199
        return new ClassConstantFetch(
200
            $node->getChild(0)->accept($this),
201
            $node->getChild(1)->accept($this)
0 ignored issues
show
Bug introduced by
It seems like $node->getChild(1)->accept($this) can also be of type Doctrine\Annotations\Par...st\Scalar\BooleanScalar and Doctrine\Annotations\Parser\Ast\Scalar\FloatScalar and Doctrine\Annotations\Par...st\Scalar\IntegerScalar and Doctrine\Annotations\Parser\Ast\Scalar\NullScalar; however, parameter $name of Doctrine\Annotations\Par...antFetch::__construct() does only seem to accept Doctrine\Annotations\Parser\Ast\Scalar\Identifier, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

201
            /** @scrutinizer ignore-type */ $node->getChild(1)->accept($this)
Loading history...
202
        );
203
    }
204
205
    private function visitReference(TreeNode $node) : Reference
206
    {
207
        $child = $node->getChild(0);
208
        $value = $child->getValueValue();
209
210
        if ($child->getValueToken() === Tokens::NAMESPACED_IDENTIFIER && $value[0] === '\\') {
211
            return new Reference(ltrim($value, '\\'), true);
212
        }
213
214
        return new Reference($value, false);
215
    }
216
217
    private function visitString(TreeNode $node) : StringScalar
218
    {
219
        return new StringScalar(str_replace('\\\\', '\\', $node->getChild(0) ? $node->getChild(0)->getValueValue() : ''));
220
    }
221
}
222