Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 200 | Zur aktuellen Revision | Details | 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 ||
104
            $node instanceof Node\Expr\Match_ ||
105
            $node instanceof Node\Expr\Variable ||
106
            $node instanceof Node\ComplexType ||
107
            $node instanceof Node\Const_ ||
108
            $node instanceof Node\Identifier ||
109
            $node instanceof Node\Name ||
110
            $node instanceof Node\Param ||
111
            $node instanceof Node\Scalar) {
112
            return;
113
        }
114
 
115
        if ($node instanceof Node\Stmt\Throw_) {
116
            $this->setLineBranch($node->expr->getEndLine(), $node->expr->getEndLine(), ++$this->nextBranch);
117
 
118
            return;
119
        }
120
 
121
        if ($node instanceof Node\Stmt\Enum_ ||
122
            $node instanceof Node\Stmt\Function_ ||
123
            $node instanceof Node\Stmt\Class_ ||
124
            $node instanceof Node\Stmt\ClassMethod ||
125
            $node instanceof Node\Expr\Closure ||
126
            $node instanceof Node\Stmt\Trait_) {
127
            $isConcreteClassLike = $node instanceof Node\Stmt\Enum_ || $node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Trait_;
128
 
129
            if (null !== $node->stmts) {
130
                foreach ($node->stmts as $stmt) {
131
                    if ($stmt instanceof Node\Stmt\Nop) {
132
                        continue;
133
                    }
134
 
135
                    foreach (range($stmt->getStartLine(), $stmt->getEndLine()) as $line) {
136
                        unset($this->executableLinesGroupedByBranch[$line]);
137
 
138
                        if (
139
                            $isConcreteClassLike &&
140
                            !$stmt instanceof Node\Stmt\ClassMethod
141
                        ) {
142
                            $this->unsets[$line] = true;
143
                        }
144
                    }
145
                }
146
            }
147
 
148
            if ($isConcreteClassLike) {
149
                return;
150
            }
151
 
152
            $hasEmptyBody = [] === $node->stmts ||
153
                null === $node->stmts ||
154
                (
155
                    1 === count($node->stmts) &&
156
                    $node->stmts[0] instanceof Node\Stmt\Nop
157
                );
158
 
159
            if ($hasEmptyBody) {
160
                if ($node->getEndLine() === $node->getStartLine()) {
161
                    return;
162
                }
163
 
164
                $this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
165
 
166
                return;
167
            }
168
 
169
            return;
170
        }
171
 
172
        if ($node instanceof Node\Expr\ArrowFunction) {
173
            $startLine = max(
174
                $node->getStartLine() + 1,
175
                $node->expr->getStartLine()
176
            );
177
 
178
            $endLine = $node->expr->getEndLine();
179
 
180
            if ($endLine < $startLine) {
181
                return;
182
            }
183
 
184
            $this->setLineBranch($startLine, $endLine, ++$this->nextBranch);
185
 
186
            return;
187
        }
188
 
189
        if ($node instanceof Node\Expr\Ternary) {
190
            if (null !== $node->if &&
191
                $node->getStartLine() !== $node->if->getEndLine()) {
192
                $this->setLineBranch($node->if->getStartLine(), $node->if->getEndLine(), ++$this->nextBranch);
193
            }
194
 
195
            if ($node->getStartLine() !== $node->else->getEndLine()) {
196
                $this->setLineBranch($node->else->getStartLine(), $node->else->getEndLine(), ++$this->nextBranch);
197
            }
198
 
199
            return;
200
        }
201
 
202
        if ($node instanceof Node\Expr\BinaryOp\Coalesce) {
203
            if ($node->getStartLine() !== $node->getEndLine()) {
204
                $this->setLineBranch($node->getEndLine(), $node->getEndLine(), ++$this->nextBranch);
205
            }
206
 
207
            return;
208
        }
209
 
210
        if ($node instanceof Node\Stmt\If_ ||
211
            $node instanceof Node\Stmt\ElseIf_ ||
212
            $node instanceof Node\Stmt\Case_) {
213
            if (null === $node->cond) {
214
                return;
215
            }
216
 
217
            $this->setLineBranch(
218
                $node->cond->getStartLine(),
219
                $node->cond->getStartLine(),
220
                ++$this->nextBranch
221
            );
222
 
223
            return;
224
        }
225
 
226
        if ($node instanceof Node\Stmt\For_) {
227
            $startLine = null;
228
            $endLine   = null;
229
 
230
            if ([] !== $node->init) {
231
                $startLine = $node->init[0]->getStartLine();
232
 
233
                end($node->init);
234
 
235
                $endLine = current($node->init)->getEndLine();
236
 
237
                reset($node->init);
238
            }
239
 
240
            if ([] !== $node->cond) {
241
                if (null === $startLine) {
242
                    $startLine = $node->cond[0]->getStartLine();
243
                }
244
 
245
                end($node->cond);
246
 
247
                $endLine = current($node->cond)->getEndLine();
248
 
249
                reset($node->cond);
250
            }
251
 
252
            if ([] !== $node->loop) {
253
                if (null === $startLine) {
254
                    $startLine = $node->loop[0]->getStartLine();
255
                }
256
 
257
                end($node->loop);
258
 
259
                $endLine = current($node->loop)->getEndLine();
260
 
261
                reset($node->loop);
262
            }
263
 
264
            if (null === $startLine || null === $endLine) {
265
                return;
266
            }
267
 
268
            $this->setLineBranch(
269
                $startLine,
270
                $endLine,
271
                ++$this->nextBranch
272
            );
273
 
274
            return;
275
        }
276
 
277
        if ($node instanceof Node\Stmt\Foreach_) {
278
            $this->setLineBranch(
279
                $node->expr->getStartLine(),
280
                $node->valueVar->getEndLine(),
281
                ++$this->nextBranch
282
            );
283
 
284
            return;
285
        }
286
 
287
        if ($node instanceof Node\Stmt\While_ ||
288
            $node instanceof Node\Stmt\Do_) {
289
            $this->setLineBranch(
290
                $node->cond->getStartLine(),
291
                $node->cond->getEndLine(),
292
                ++$this->nextBranch
293
            );
294
 
295
            return;
296
        }
297
 
298
        if ($node instanceof Node\Stmt\Catch_) {
299
            assert([] !== $node->types);
300
            $startLine = $node->types[0]->getStartLine();
301
            end($node->types);
302
            $endLine = current($node->types)->getEndLine();
303
 
304
            $this->setLineBranch(
305
                $startLine,
306
                $endLine,
307
                ++$this->nextBranch
308
            );
309
 
310
            return;
311
        }
312
 
313
        if ($node instanceof Node\Expr\CallLike) {
314
            if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) {
315
                $branch = $this->executableLinesGroupedByBranch[$node->getStartLine()];
316
            } else {
317
                $branch = ++$this->nextBranch;
318
            }
319
 
320
            $this->setLineBranch($node->getStartLine(), $node->getEndLine(), $branch);
321
 
322
            return;
323
        }
324
 
325
        if (isset($this->executableLinesGroupedByBranch[$node->getStartLine()])) {
326
            return;
327
        }
328
 
329
        $this->setLineBranch($node->getStartLine(), $node->getEndLine(), ++$this->nextBranch);
330
    }
331
 
332
    public function afterTraverse(array $nodes): void
333
    {
334
        $lines = explode("\n", $this->source);
335
 
336
        foreach ($lines as $lineNumber => $line) {
337
            $lineNumber++;
338
 
339
            if (1 === preg_match('/^\s*$/', $line) ||
340
                (
341
                    isset($this->commentsToCheckForUnset[$lineNumber]) &&
342
                    1 === preg_match(sprintf('/^\s*%s\s*$/', preg_quote($this->commentsToCheckForUnset[$lineNumber], '/')), $line)
343
                )) {
344
                unset($this->executableLinesGroupedByBranch[$lineNumber]);
345
            }
346
        }
347
 
348
        $this->executableLinesGroupedByBranch = array_diff_key(
349
            $this->executableLinesGroupedByBranch,
350
            $this->unsets
351
        );
352
    }
353
 
354
    public function executableLinesGroupedByBranch(): array
355
    {
356
        return $this->executableLinesGroupedByBranch;
357
    }
358
 
359
    private function setLineBranch(int $start, int $end, int $branch): void
360
    {
361
        foreach (range($start, $end) as $line) {
362
            $this->executableLinesGroupedByBranch[$line] = $branch;
363
        }
364
    }
365
}