Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 148 | Zur aktuellen Revision | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of phpunit/php-code-coverage.
4
 *
5
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace SebastianBergmann\CodeCoverage\StaticAnalysis;
11
 
12
use function array_diff_key;
13
use function assert;
14
use function count;
15
use function current;
16
use function end;
17
use function explode;
18
use function max;
19
use function preg_match;
20
use function preg_quote;
21
use function range;
22
use function reset;
23
use function sprintf;
24
use PhpParser\Node;
25
use PhpParser\NodeVisitorAbstract;
26
 
27
/**
28
 * @internal This class is not covered by the backward compatibility promise for phpunit/php-code-coverage
29
 */
30
final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract
31
{
32
    /**
33
     * @var int
34
     */
35
    private $nextBranch = 0;
36
 
37
    /**
38
     * @var string
39
     */
40
    private $source;
41
 
42
    /**
43
     * @var array<int, int>
44
     */
45
    private $executableLinesGroupedByBranch = [];
46
 
47
    /**
48
     * @var array<int, bool>
49
     */
50
    private $unsets = [];
51
 
52
    /**
53
     * @var array<int, string>
54
     */
55
    private $commentsToCheckForUnset = [];
56
 
57
    public function __construct(string $source)
58
    {
59
        $this->source = $source;
60
    }
61
 
62
    public function enterNode(Node $node): void
63
    {
64
        foreach ($node->getComments() as $comment) {
65
            $commentLine = $comment->getStartLine();
66
 
67
            if (!isset($this->executableLinesGroupedByBranch[$commentLine])) {
68
                continue;
69
            }
70
 
71
            foreach (explode("\n", $comment->getText()) as $text) {
72
                $this->commentsToCheckForUnset[$commentLine] = $text;
73
                $commentLine++;
74
            }
75
        }
76
 
77
        if ($node instanceof Node\Scalar\String_ ||
78
            $node instanceof Node\Scalar\EncapsedStringPart) {
79
            $startLine = $node->getStartLine() + 1;
80
            $endLine   = $node->getEndLine() - 1;
81
 
82
            if ($startLine <= $endLine) {
83
                foreach (range($startLine, $endLine) as $line) {
84
                    unset($this->executableLinesGroupedByBranch[$line]);
85
                }
86
            }
87
 
88
            return;
89
        }
90
 
91
        if ($node instanceof Node\Stmt\Declare_ ||
92
            $node instanceof Node\Stmt\DeclareDeclare ||
93
            $node instanceof Node\Stmt\Else_ ||
94
            $node instanceof Node\Stmt\EnumCase ||
95
            $node instanceof Node\Stmt\Finally_ ||
96
            $node instanceof Node\Stmt\Interface_ ||
97
            $node instanceof Node\Stmt\Label ||
98
            $node instanceof Node\Stmt\Namespace_ ||
99
            $node instanceof Node\Stmt\Nop ||
100
            $node instanceof Node\Stmt\Switch_ ||
101
            $node instanceof Node\Stmt\TryCatch ||
102
            $node instanceof Node\Stmt\Use_ ||
103
            $node instanceof Node\Stmt\UseUse ||
200 lars 104
            $node instanceof Node\Expr\ConstFetch ||
148 lars 105
            $node instanceof Node\Expr\Match_ ||
106
            $node instanceof Node\Expr\Variable ||
107
            $node instanceof Node\ComplexType ||
108
            $node instanceof Node\Const_ ||
109
            $node instanceof Node\Identifier ||
110
            $node instanceof Node\Name ||
111
            $node instanceof Node\Param ||
112
            $node instanceof Node\Scalar) {
113
            return;
114
        }
115
 
116
        if ($node instanceof Node\Stmt\Throw_) {
117
            $this->setLineBranch($node->expr->getEndLine(), $node->expr->getEndLine(), ++$this->nextBranch);
118
 
119
            return;
120
        }
121
 
122
        if ($node instanceof Node\Stmt\Enum_ ||
123
            $node instanceof Node\Stmt\Function_ ||
124
            $node instanceof Node\Stmt\Class_ ||
125
            $node instanceof Node\Stmt\ClassMethod ||
126
            $node instanceof Node\Expr\Closure ||
127
            $node instanceof Node\Stmt\Trait_) {
128
            $isConcreteClassLike = $node instanceof Node\Stmt\Enum_ || $node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Trait_;
129
 
130
            if (null !== $node->stmts) {
131
                foreach ($node->stmts as $stmt) {
132
                    if ($stmt instanceof Node\Stmt\Nop) {
133
                        continue;
134
                    }
135
 
136
                    foreach (range($stmt->getStartLine(), $stmt->getEndLine()) as $line) {
137
                        unset($this->executableLinesGroupedByBranch[$line]);
138
 
139
                        if (
140
                            $isConcreteClassLike &&
141
                            !$stmt instanceof Node\Stmt\ClassMethod
142
                        ) {
143
                            $this->unsets[$line] = true;
144
                        }
145
                    }
146
                }
147
            }
148
 
149
            if ($isConcreteClassLike) {
150
                return;
151
            }
152
 
153
            $hasEmptyBody = [] === $node->stmts ||
154
                null === $node->stmts ||
155
                (
156
                    1 === count($node->stmts) &&
157
                    $node->stmts[0] instanceof Node\Stmt\Nop
158
                );
159
 
160
            if ($hasEmptyBody) {
161
                if ($node->getEndLine() === $node->getStartLine()) {
162
                    return;
163
                }
164
 
165
                $this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
166
 
167
                return;
168
            }
169
 
170
            return;
171
        }
172
 
173
        if ($node instanceof Node\Expr\ArrowFunction) {
174
            $startLine = max(
175
                $node->getStartLine() + 1,
176
                $node->expr->getStartLine()
177
            );
178
 
179
            $endLine = $node->expr->getEndLine();
180
 
181
            if ($endLine < $startLine) {
182
                return;
183
            }
184
 
185
            $this->setLineBranch($startLine, $endLine, ++$this->nextBranch);
186
 
187
            return;
188
        }
189
 
190
        if ($node instanceof Node\Expr\Ternary) {
191
            if (null !== $node->if &&
192
                $node->getStartLine() !== $node->if->getEndLine()) {
193
                $this->setLineBranch($node->if->getStartLine(), $node->if->getEndLine(), ++$this->nextBranch);
194
            }
195
 
196
            if ($node->getStartLine() !== $node->else->getEndLine()) {
197
                $this->setLineBranch($node->else->getStartLine(), $node->else->getEndLine(), ++$this->nextBranch);
198
            }
199
 
200
            return;
201
        }
202
 
203
        if ($node instanceof Node\Expr\BinaryOp\Coalesce) {
204
            if ($node->getStartLine() !== $node->getEndLine()) {
205
                $this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
206
            }
207
 
208
            return;
209
        }
210
 
211
        if ($node instanceof Node\Stmt\If_ ||
212
            $node instanceof Node\Stmt\ElseIf_ ||
213
            $node instanceof Node\Stmt\Case_) {
214
            if (null === $node->cond) {
215
                return;
216
            }
217
 
218
            $this->setLineBranch(
219
                $node->cond->getStartLine(),
220
                $node->cond->getStartLine(),
221
                ++$this->nextBranch
222
            );
223
 
224
            return;
225
        }
226
 
227
        if ($node instanceof Node\Stmt\For_) {
228
            $startLine = null;
229
            $endLine   = null;
230
 
231
            if ([] !== $node->init) {
232
                $startLine = $node->init[0]->getStartLine();
233
 
234
                end($node->init);
235
 
236
                $endLine = current($node->init)->getEndLine();
237
 
238
                reset($node->init);
239
            }
240
 
241
            if ([] !== $node->cond) {
242
                if (null === $startLine) {
243
                    $startLine = $node->cond[0]->getStartLine();
244
                }
245
 
246
                end($node->cond);
247
 
248
                $endLine = current($node->cond)->getEndLine();
249
 
250
                reset($node->cond);
251
            }
252
 
253
            if ([] !== $node->loop) {
254
                if (null === $startLine) {
255
                    $startLine = $node->loop[0]->getStartLine();
256
                }
257
 
258
                end($node->loop);
259
 
260
                $endLine = current($node->loop)->getEndLine();
261
 
262
                reset($node->loop);
263
            }
264
 
265
            if (null === $startLine || null === $endLine) {
266
                return;
267
            }
268
 
269
            $this->setLineBranch(
270
                $startLine,
271
                $endLine,
272
                ++$this->nextBranch
273
            );
274
 
275
            return;
276
        }
277
 
278
        if ($node instanceof Node\Stmt\Foreach_) {
279
            $this->setLineBranch(
280
                $node->expr->getStartLine(),
281
                $node->valueVar->getEndLine(),
282
                ++$this->nextBranch
283
            );
284
 
285
            return;
286
        }
287
 
288
        if ($node instanceof Node\Stmt\While_ ||
289
            $node instanceof Node\Stmt\Do_) {
290
            $this->setLineBranch(
291
                $node->cond->getStartLine(),
292
                $node->cond->getEndLine(),
293
                ++$this->nextBranch
294
            );
295
 
296
            return;
297
        }
298
 
299
        if ($node instanceof Node\Stmt\Catch_) {
300
            assert([] !== $node->types);
301
            $startLine = $node->types[0]->getStartLine();
302
            end($node->types);
303
            $endLine = current($node->types)->getEndLine();
304
 
305
            $this->setLineBranch(
306
                $startLine,
307
                $endLine,
308
                ++$this->nextBranch
309
            );
310
 
311
            return;
312
        }
313
 
314
        if ($node instanceof Node\Expr\CallLike) {
315
            if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) {
316
                $branch = $this->executableLinesGroupedByBranch[$node->getStartLine()];
317
            } else {
318
                $branch = ++$this->nextBranch;
319
            }
320
 
321
            $this->setLineBranch($node->getStartLine(), $node->getEndLine(), $branch);
322
 
323
            return;
324
        }
325
 
326
        if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) {
327
            return;
328
        }
329
 
330
        $this->setLineBranch($node->getStartLine(), $node->getEndLine(), ++$this->nextBranch);
331
    }
332
 
333
    public function afterTraverse(array $nodes): void
334
    {
335
        $lines = explode("\n", $this->source);
336
 
337
        foreach ($lines as $lineNumber => $line) {
338
            $lineNumber++;
339
 
340
            if (1 === preg_match('/^\s*$/', $line) ||
341
                (
342
                    isset($this->commentsToCheckForUnset[$lineNumber]) &&
343
                    1 === preg_match(sprintf('/^\s*%s\s*$/', preg_quote($this->commentsToCheckForUnset[$lineNumber], '/')), $line)
344
                )) {
345
                unset($this->executableLinesGroupedByBranch[$lineNumber]);
346
            }
347
        }
348
 
349
        $this->executableLinesGroupedByBranch = array_diff_key(
350
            $this->executableLinesGroupedByBranch,
351
            $this->unsets
352
        );
353
    }
354
 
355
    public function executableLinesGroupedByBranch(): array
356
    {
357
        return $this->executableLinesGroupedByBranch;
358
    }
359
 
360
    private function setLineBranch(int $start, int $end, int $branch): void
361
    {
362
        foreach (range($start, $end) as $line) {
363
            $this->executableLinesGroupedByBranch[$line] = $branch;
364
        }
365
    }
366
}