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

sanmai / phpstan-rules / 16045390351

03 Jul 2025 08:23AM UTC coverage: 94.382%. First build
16045390351

Pull #2

github

web-flow
Merge 64b3ab231 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
    private function isLoopNode(Node $node): bool
76
    {
77
        return $node instanceof For_
16✔
78
            || $node instanceof Foreach_
16✔
79
            || $node instanceof While_
16✔
80
            || $node instanceof Do_;
16✔
81
    }
82

83

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

93
        // We know it's a loop node, so we can safely access stmts property
94
        /** @psalm-suppress NoInterfaceProperties, MixedReturnStatement */
95
        /** @phpstan-ignore-next-line */
96
        return $node->stmts;
12✔
97
    }
98

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

111
        return false;
12✔
112
    }
113
}
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