Failed Conditions
Pull Request — new-parser-ast-metadata (#2)
by
unknown
02:16
created

AstBuilder   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Test Coverage

Coverage 95.4%

Importance

Changes 0
Metric Value
wmc 41
eloc 86
dl 0
loc 175
ccs 83
cts 87
cp 0.954
rs 9.1199
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A visitAnnotations() 0 8 2
A visitParameters() 0 8 2
A visitConstant() 0 5 1
A visitReference() 0 10 3
A visitString() 0 3 2
A visitAnnotation() 0 7 2
A visitUnnamedParameter() 0 3 1
A visitValue() 0 3 1
A visitList() 0 8 2
A visit() 0 9 2
A visitNamedParameter() 0 5 1
A visitToken() 0 21 6
C visitNode() 0 30 13
A visitPair() 0 5 1
A visitMap() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like AstBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AstBuilder, and based on these observations, apply Extract Interface, too.

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

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

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

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