• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sanmai / phpstan-rules / 16045486657

03 Jul 2025 08:29AM UTC coverage: 94.382%. First build
16045486657

Pull #2

github

web-flow
Merge 9850cd73f into ab8883211
Pull Request #2: Fix the build

20 of 22 new or added lines in 3 files covered. (90.91%)

84 of 89 relevant lines covered (94.38%)

25.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.59
/src/Rules/NoNestedLoopsRule.php
1
<?php
2

3
/**
4
 * Copyright 2025 Alexey Kopytko <alexey@kopytko.com>
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18

19
declare(strict_types=1);
20

21
namespace Sanmai\PHPStanRules\Rules;
22

23
use PhpParser\Node;
24
use PhpParser\Node\Stmt\Do_;
25
use PhpParser\Node\Stmt\For_;
26
use PhpParser\Node\Stmt\Foreach_;
27
use PhpParser\Node\Stmt\While_;
28
use PHPStan\Analyser\Scope;
29
use PHPStan\Rules\Rule;
30
use PHPStan\Rules\RuleErrorBuilder;
31
use Override;
32

33
/**
34
 * @implements Rule<Node>
35
 */
36
final class NoNestedLoopsRule implements Rule
37
{
38
    #[Override]
39
    public function getNodeType(): string
40
    {
41
        return Node::class;
16✔
42
    }
43

44
    /**
45
     * @return list<\PHPStan\Rules\IdentifierRuleError>
46
     */
47
    #[Override]
48
    public function processNode(Node $node, Scope $scope): array
49
    {
50
        if (!$this->isLoopNode($node)) {
16✔
51
            return [];
16✔
52
        }
53

54
        // Only check direct statements within the loop body, not inside function calls
55
        $stmts = $this->getLoopStatements($node);
12✔
56
        if (null === $stmts) {
12✔
57
            return [];
×
58
        }
59

60
        $hasNestedLoop = $this->hasDirectNestedLoop($stmts);
12✔
61

62
        if ($hasNestedLoop) {
12✔
63
            return [
8✔
64
                RuleErrorBuilder::message(
8✔
65
                    'Nested loops are not allowed. Use functional approaches like map(), filter(), or extract to a separate method.'
8✔
66
                )
8✔
67
                    ->identifier('sanmai.noNestedLoops')
8✔
68
                    ->build(),
8✔
69
            ];
8✔
70
        }
71

72
        return [];
12✔
73
    }
74

75
    /**
76
     * @phpstan-assert-if-true For_|Foreach_|While_|Do_ $node
77
     * @psalm-assert-if-true For_|Foreach_|While_|Do_ $node
78
     */
79
    private function isLoopNode(Node $node): bool
80
    {
81
        return $node instanceof For_
16✔
82
            || $node instanceof Foreach_
16✔
83
            || $node instanceof While_
16✔
84
            || $node instanceof Do_;
16✔
85
    }
86

87

88
    /**
89
     * @return array<Node\Stmt>|null
90
     */
91
    private function getLoopStatements(Node $node): ?array
92
    {
93
        if (!$this->isLoopNode($node)) {
12✔
NEW
94
            return null;
×
95
        }
96

97
        return $node->stmts;
12✔
98
    }
99

100
    /**
101
     * @param array<Node> $stmts
102
     */
103
    private function hasDirectNestedLoop(array $stmts): bool
104
    {
105
        // Simply check if any direct statement is a loop
106
        foreach ($stmts as $stmt) {
12✔
107
            if ($this->isLoopNode($stmt)) {
12✔
108
                return true;
8✔
109
            }
110
        }
111

112
        return false;
12✔
113
    }
114
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc