Failed Conditions
Push — new-parser-ast-metadata ( 6127e0...5a4a16 )
by Michael
12s
created

AstBuilder::visitAnnotation()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 7
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\Collection\ListCollection;
10
use Doctrine\Annotations\Parser\Ast\Collection\MapCollection;
11
use Doctrine\Annotations\Parser\Ast\ConstantFetch;
12
use Doctrine\Annotations\Parser\Ast\Node;
13
use Doctrine\Annotations\Parser\Ast\Pair;
14
use Doctrine\Annotations\Parser\Ast\Parameter\NamedParameter;
15
use Doctrine\Annotations\Parser\Ast\Parameter\UnnamedParameter;
16
use Doctrine\Annotations\Parser\Ast\Parameters;
17
use Doctrine\Annotations\Parser\Ast\Reference;
18
use Doctrine\Annotations\Parser\Ast\Scalar;
19
use Doctrine\Annotations\Parser\Ast\Scalar\BooleanScalar;
20
use Doctrine\Annotations\Parser\Ast\Scalar\FloatScalar;
21
use Doctrine\Annotations\Parser\Ast\Scalar\Identifier;
22
use Doctrine\Annotations\Parser\Ast\Scalar\IntegerScalar;
23
use Doctrine\Annotations\Parser\Ast\Scalar\NullScalar;
24
use Doctrine\Annotations\Parser\Ast\Scalar\StringScalar;
25
use Doctrine\Annotations\Parser\Nodes;
26
use Doctrine\Annotations\Parser\Tokens;
27
use Hoa\Compiler\Llk\TreeNode;
28
use Hoa\Visitor\Element;
29
use Hoa\Visitor\Visit;
30
use function assert;
31
use function ltrim;
32
use function sprintf;
33
use function str_replace;
34
use function strcasecmp;
35
36
final class AstBuilder implements Visit
37
{
38
    /**
39
     * @param mixed $handle
40
     * @param mixed $eldnah
41
     */
42 29
    public function visit(Element $node, &$handle = null, $eldnah = null) : Node
43
    {
44 29
        assert($node instanceof TreeNode);
45
46 29
        if ($node->isToken()) {
47 16
            return $this->visitToken($node);
48
        }
49
50 29
        return $this->visitNode($node);
51
    }
52
53 29
    private function visitNode(TreeNode $node) : Node
54
    {
55 29
        switch ($node->getId()) {
56
            case Nodes::ANNOTATIONS:
57 29
                return $this->visitAnnotations($node);
58
            case Nodes::ANNOTATION:
59 28
                return $this->visitAnnotation($node);
60
            case Nodes::PAIR:
61 3
                return $this->visitPair($node);
62
            case Nodes::PARAMETERS:
63 22
                return $this->visitParameters($node);
64
            case Nodes::NAMED_PARAMETERS:
65 13
                return $this->visitNamedParameter($node);
66
            case Nodes::UNNAMED_PARAMETERS:
67 15
                return $this->visitUnnamedParameter($node);
68
            case Nodes::VALUE:
69 22
                return $this->visitValue($node);
70
            case Nodes::MAP:
71 3
                return $this->visitMap($node);
72
            case Nodes::LIST:
73 6
                return $this->visitList($node);
74
            case Nodes::CONSTANT:
75 3
                return $this->visitConstant($node);
76
            case Nodes::REFERENCE:
77 3
                return $this->visitReference($node);
78
            case Nodes::STRING:
79 15
                return $this->visitString($node);
80
        }
81
82
        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...
83
    }
84
85 16
    private function visitToken(TreeNode $node) : Scalar
86
    {
87 16
        $value = $node->getValueValue();
88
89 16
        switch ($node->getValueToken()) {
90 16
            case Tokens::IDENTIFIER:
91 14
                return new Identifier($value);
92 10
            case Tokens::NULL:
93 1
                return new NullScalar();
94 10
            case Tokens::BOOLEAN:
95 4
                return new BooleanScalar(strcasecmp($value, 'true') === 0);
96 8
            case Tokens::INTEGER:
97 8
                $intValue = (int) $value;
98 8
                assert((string) $intValue === $value, 'Integer overflow');
99
100 8
                return new IntegerScalar($intValue);
101 3
            case Tokens::FLOAT:
102 3
                return new FloatScalar((float) $value);
103
        }
104
105
        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...
106
    }
107
108 29
    private function visitAnnotations(TreeNode $node) : Annotations
109
    {
110 29
        return new Annotations(
111
            ...(function (TreeNode ...$nodes) : iterable {
112 29
                foreach ($nodes as $node) {
113 28
                    yield $node->accept($this);
114
                }
115 29
            })(...$node->getChildren())
116
        );
117
    }
118
119 28
    private function visitAnnotation(TreeNode $node) : Annotation
120
    {
121 28
        $identifier = $node->getChild(0)->getValueValue();
122
123 28
        return new Annotation(
124 28
            new Reference(ltrim($identifier, '\\'), $identifier[0] === '\\'),
125 28
            $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

125
            /** @scrutinizer ignore-type */ $node->childExists(1) ? $node->getChild(1)->accept($this) : new Parameters()
Loading history...
126
        );
127
    }
128
129 3
    private function visitPair(TreeNode $node) : Pair
130
    {
131 3
        return new Pair(
132 3
            $node->getChild(0)->accept($this),
133 3
            $node->getChild(1)->accept($this)
134
        );
135
    }
136
137 22
    private function visitParameters(TreeNode $node) : Parameters
138
    {
139 22
        return new Parameters(
140
            ...(function (TreeNode ...$nodes) : iterable {
141 22
                foreach ($nodes as $node) {
142 22
                    yield $node->accept($this);
143
                }
144 22
            })(...$node->getChildren())
145
        );
146
    }
147
148 13
    private function visitNamedParameter(TreeNode $node) : NamedParameter
149
    {
150 13
        return new NamedParameter(
151 13
            $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\IntegerScalar and Doctrine\Annotations\Par...st\Scalar\BooleanScalar and Doctrine\Annotations\Parser\Ast\Scalar\FloatScalar 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

151
            /** @scrutinizer ignore-type */ $node->getChild(0)->accept($this),
Loading history...
152 13
            $node->getChild(1)->accept($this)
153
        );
154
    }
155
156 15
    private function visitUnnamedParameter(TreeNode $node) : UnnamedParameter
157
    {
158 15
        return new UnnamedParameter($node->getChild(0)->accept($this));
159
    }
160
161 22
    private function visitValue(TreeNode $node) : Node
162
    {
163 22
        return $node->getChild(0)->accept($this);
164
    }
165
166 3
    private function visitMap(TreeNode $node) : MapCollection
167
    {
168 3
        return new MapCollection(
169
            ...(function (TreeNode ...$nodes) : iterable {
170 3
                foreach ($nodes as $node) {
171 3
                    yield $node->accept($this);
172
                }
173 3
            })(...$node->getChildren())
174
        );
175
    }
176
177 6
    private function visitList(TreeNode $node) : ListCollection
178
    {
179 6
        return new ListCollection(
180
            ...(function (TreeNode ...$nodes) : iterable {
181 6
                foreach ($nodes as $node) {
182 6
                    yield $node->accept($this);
183
                }
184 6
            })(...$node->getChildren())
185
        );
186
    }
187
188 3
    private function visitConstant(TreeNode $node) : ConstantFetch
189
    {
190 3
        return new ConstantFetch(
191 3
            $node->getChild(0)->accept($this),
192 3
            $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\IntegerScalar and Doctrine\Annotations\Par...st\Scalar\BooleanScalar and Doctrine\Annotations\Parser\Ast\Scalar\FloatScalar 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

192
            /** @scrutinizer ignore-type */ $node->getChild(1)->accept($this)
Loading history...
193
        );
194
    }
195
196 3
    private function visitReference(TreeNode $node) : Reference
197
    {
198 3
        $child = $node->getChild(0);
199 3
        $value = $child->getValueValue();
200
201 3
        if ($child->getValueToken() === Tokens::NAMESPACED_IDENTIFIER && $value[0] === '\\') {
202 1
            return new Reference(ltrim($value, '\\'), true);
203
        }
204
205 3
        return new Reference($value, false);
206
    }
207
208 15
    private function visitString(TreeNode $node) : StringScalar
209
    {
210 15
        return new StringScalar(str_replace('\\\\', '\\', $node->getChild(0) ? $node->getChild(0)->getValueValue() : ''));
211
    }
212
}
213