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

sanmai / pipeline / 16436877530

22 Jul 2025 06:47AM UTC coverage: 98.695% (-1.3%) from 100.0%
16436877530

push

github

web-flow
Extend TypeInferenceTest (#227)

165 of 173 new or added lines in 6 files covered. (95.38%)

605 of 613 relevant lines covered (98.69%)

336.52 hits per line

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

96.97
/src/PHPStan/TypeNarrower.php
1
<?php
2

3
/**
4
 * Copyright 2017, 2018 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 Pipeline\PHPStan;
22

23
use PHPStan\Type\Generic\GenericObjectType;
24
use PHPStan\Type\NeverType;
25
use PHPStan\Type\Type;
26
use PHPStan\Type\UnionType;
27

28
use function count;
29
use function method_exists;
30
use function in_array;
31

32
/**
33
 * Applies type narrowing logic based on filter parameters.
34
 */
35
class TypeNarrower
36
{
37
    public function __construct(
38
        private FilterTypeNarrowingHelper $helper
39
    ) {}
44✔
40

41
    /**
42
     * Narrow the pipeline type based on strict mode.
43
     *
44
     * @return Type|null The narrowed type, or null if no narrowing occurred
45
     */
46
    public function narrowForStrictMode(Type $keyType, Type $valueType): ?Type
47
    {
48
        if ($valueType instanceof UnionType) {
16✔
49
            $filteredTypes = $this->helper->removeFalsyTypesFromUnion($valueType);
8✔
50

51
            if ([] !== $filteredTypes && count($filteredTypes) < count($valueType->getTypes())) {
8✔
52
                return $this->helper->createGenericTypeWithFilteredValues($keyType, $filteredTypes);
4✔
53
            }
54
        } elseif ($valueType->isNull()->yes() || $valueType->isFalse()->yes()) {
8✔
55
            // If the entire type is falsy, filter() would return empty
56
            return new GenericObjectType(\Pipeline\Standard::class, [$keyType, new NeverType()]);
4✔
57
        }
58

59
        return null;
8✔
60
    }
61

62
    /**
63
     * Narrow the pipeline type based on a callback that filters to a specific type.
64
     *
65
     * @return Type|null The narrowed type, or null if no narrowing occurred
66
     */
67
    public function narrowForCallback(Type $keyType, Type $valueType, Type $targetType): ?Type
68
    {
69
        if ($valueType instanceof UnionType) {
4✔
70
            $filteredTypes = $this->helper->filterUnionTypeByTarget($valueType, $targetType);
4✔
71

72
            if ([] !== $filteredTypes) {
4✔
NEW
73
                return $this->helper->createGenericTypeWithFilteredValues($keyType, $filteredTypes);
×
74
            }
75
        }
76

77
        return null;
4✔
78
    }
79

80
    /**
81
     * Narrow the pipeline type based on default filter (removes all falsy values).
82
     *
83
     * @return Type|null The narrowed type, or null if no narrowing occurred
84
     */
85
    public function narrowForDefaultFilter(Type $keyType, Type $valueType): ?Type
86
    {
87
        if ($valueType instanceof UnionType) {
24✔
88
            $filteredTypes = $this->helper->removeFalsyValuesFromUnion($valueType);
8✔
89

90
            if ([] !== $filteredTypes && count($filteredTypes) < count($valueType->getTypes())) {
8✔
91
                return $this->helper->createGenericTypeWithFilteredValues($keyType, $filteredTypes);
4✔
92
            }
93
        } elseif ($this->isFalsyType($valueType)) {
16✔
94
            // If the entire type is falsy, filter() would return empty
95
            return new GenericObjectType(\Pipeline\Standard::class, [$keyType, new NeverType()]);
12✔
96
        }
97

98
        return null;
12✔
99
    }
100

101
    /**
102
     * Check if a type is entirely falsy (would be removed by default filter).
103
     */
104
    private function isFalsyType(Type $type): bool
105
    {
106
        // Check common falsy types
107
        if ($type->isNull()->yes()) {
16✔
108
            return true;
4✔
109
        }
110

111
        if ($type->isFalse()->yes()) {
12✔
112
            return true;
4✔
113
        }
114

115
        // Check for literal 0
116
        // Check for literal 0.0
117
        // Check for empty string
118

119
        if ($type->isConstantScalarValue()->yes()) {
8✔
120
            $value = $type->getConstantScalarValues()[0] ?? null;
4✔
121
            return 0 === $value || 0.0 === $value || '' === $value;
4✔
122
        }
123

124
        // Check for empty array
125
        if ($type->isArray()->yes()) {
8✔
126
            $arraySize = $type->getArraySize();
4✔
127
            if ($arraySize->isConstantScalarValue()->yes()) {
4✔
128
                $values = $arraySize->getConstantScalarValues();
4✔
129
                return in_array(0, $values, true);
4✔
130
            }
131
        }
132

133
        return false;
4✔
134
    }
135
}
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