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

keradus / PHP-CS-Fixer / 17252691116

26 Aug 2025 11:09PM UTC coverage: 94.743% (-0.01%) from 94.755%
17252691116

push

github

keradus
chore: apply phpdoc_tag_no_named_arguments

28313 of 29884 relevant lines covered (94.74%)

45.64 hits per line

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

99.07
/src/Fixer/PhpUnit/PhpUnitExpectationFixer.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of PHP CS Fixer.
7
 *
8
 * (c) Fabien Potencier <fabien@symfony.com>
9
 *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14

15
namespace PhpCsFixer\Fixer\PhpUnit;
16

17
use PhpCsFixer\Fixer\AbstractPhpUnitFixer;
18
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
19
use PhpCsFixer\Fixer\ConfigurableFixerTrait;
20
use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
21
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
22
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
23
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
24
use PhpCsFixer\FixerDefinition\CodeSample;
25
use PhpCsFixer\FixerDefinition\FixerDefinition;
26
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
27
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
28
use PhpCsFixer\Tokenizer\Analyzer\WhitespacesAnalyzer;
29
use PhpCsFixer\Tokenizer\Token;
30
use PhpCsFixer\Tokenizer\Tokens;
31

32
/**
33
 * @phpstan-type _AutogeneratedInputConfiguration array{
34
 *  target?: '5.2'|'5.6'|'8.4'|'newest',
35
 * }
36
 * @phpstan-type _AutogeneratedComputedConfiguration array{
37
 *  target: '5.2'|'5.6'|'8.4'|'newest',
38
 * }
39
 *
40
 * @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
41
 *
42
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
43
 *
44
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
45
 */
46
final class PhpUnitExpectationFixer extends AbstractPhpUnitFixer implements ConfigurableFixerInterface, WhitespacesAwareFixerInterface
47
{
48
    /** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
49
    use ConfigurableFixerTrait;
50

51
    /**
52
     * @var array<string, string>
53
     */
54
    private array $methodMap = [];
55

56
    public function getDefinition(): FixerDefinitionInterface
57
    {
58
        return new FixerDefinition(
3✔
59
            'Usages of `->setExpectedException*` methods MUST be replaced by `->expectException*` methods.',
3✔
60
            [
3✔
61
                new CodeSample(
3✔
62
                    '<?php
3✔
63
final class MyTest extends \PHPUnit_Framework_TestCase
64
{
65
    public function testFoo()
66
    {
67
        $this->setExpectedException("RuntimeException", "Msg", 123);
68
        foo();
69
    }
70

71
    public function testBar()
72
    {
73
        $this->setExpectedExceptionRegExp("RuntimeException", "/Msg.*/", 123);
74
        bar();
75
    }
76
}
77
'
3✔
78
                ),
3✔
79
                new CodeSample(
3✔
80
                    '<?php
3✔
81
final class MyTest extends \PHPUnit_Framework_TestCase
82
{
83
    public function testFoo()
84
    {
85
        $this->setExpectedException("RuntimeException", null, 123);
86
        foo();
87
    }
88

89
    public function testBar()
90
    {
91
        $this->setExpectedExceptionRegExp("RuntimeException", "/Msg.*/", 123);
92
        bar();
93
    }
94
}
95
',
3✔
96
                    ['target' => PhpUnitTargetVersion::VERSION_8_4]
3✔
97
                ),
3✔
98
                new CodeSample(
3✔
99
                    '<?php
3✔
100
final class MyTest extends \PHPUnit_Framework_TestCase
101
{
102
    public function testFoo()
103
    {
104
        $this->setExpectedException("RuntimeException", null, 123);
105
        foo();
106
    }
107

108
    public function testBar()
109
    {
110
        $this->setExpectedExceptionRegExp("RuntimeException", "/Msg.*/", 123);
111
        bar();
112
    }
113
}
114
',
3✔
115
                    ['target' => PhpUnitTargetVersion::VERSION_5_6]
3✔
116
                ),
3✔
117
                new CodeSample(
3✔
118
                    '<?php
3✔
119
final class MyTest extends \PHPUnit_Framework_TestCase
120
{
121
    public function testFoo()
122
    {
123
        $this->setExpectedException("RuntimeException", "Msg", 123);
124
        foo();
125
    }
126

127
    public function testBar()
128
    {
129
        $this->setExpectedExceptionRegExp("RuntimeException", "/Msg.*/", 123);
130
        bar();
131
    }
132
}
133
',
3✔
134
                    ['target' => PhpUnitTargetVersion::VERSION_5_2]
3✔
135
                ),
3✔
136
            ],
3✔
137
            null,
3✔
138
            'Risky when PHPUnit classes are overridden or not accessible, or when project has PHPUnit incompatibilities.'
3✔
139
        );
3✔
140
    }
141

142
    /**
143
     * {@inheritdoc}
144
     *
145
     * Must run after PhpUnitNoExpectationAnnotationFixer.
146
     */
147
    public function getPriority(): int
148
    {
149
        return 0;
1✔
150
    }
151

152
    public function isRisky(): bool
153
    {
154
        return true;
1✔
155
    }
156

157
    protected function configurePostNormalisation(): void
158
    {
159
        $this->methodMap = [
24✔
160
            'setExpectedException' => 'expectExceptionMessage',
24✔
161
        ];
24✔
162

163
        if (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_5_6)) {
24✔
164
            $this->methodMap['setExpectedExceptionRegExp'] = 'expectExceptionMessageRegExp';
24✔
165
        }
166

167
        if (PhpUnitTargetVersion::fulfills($this->configuration['target'], PhpUnitTargetVersion::VERSION_8_4)) {
24✔
168
            $this->methodMap['setExpectedExceptionRegExp'] = 'expectExceptionMessageMatches';
24✔
169
            $this->methodMap['expectExceptionMessageRegExp'] = 'expectExceptionMessageMatches';
24✔
170
        }
171
    }
172

173
    protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
174
    {
175
        return new FixerConfigurationResolver([
24✔
176
            (new FixerOptionBuilder('target', 'Target version of PHPUnit.'))
24✔
177
                ->setAllowedTypes(['string'])
24✔
178
                ->setAllowedValues([PhpUnitTargetVersion::VERSION_5_2, PhpUnitTargetVersion::VERSION_5_6, PhpUnitTargetVersion::VERSION_8_4, PhpUnitTargetVersion::VERSION_NEWEST])
24✔
179
                ->setDefault(PhpUnitTargetVersion::VERSION_NEWEST)
24✔
180
                ->getOption(),
24✔
181
        ]);
24✔
182
    }
183

184
    protected function applyPhpUnitClassFix(Tokens $tokens, int $startIndex, int $endIndex): void
185
    {
186
        foreach (Token::getObjectOperatorKinds() as $objectOperator) {
15✔
187
            $this->applyPhpUnitClassFixWithObjectOperator($tokens, $startIndex, $endIndex, $objectOperator);
15✔
188
        }
189
    }
190

191
    private function applyPhpUnitClassFixWithObjectOperator(Tokens $tokens, int $startIndex, int $endIndex, int $objectOperator): void
192
    {
193
        $argumentsAnalyzer = new ArgumentsAnalyzer();
15✔
194

195
        $oldMethodSequence = [
15✔
196
            [\T_VARIABLE, '$this'],
15✔
197
            [$objectOperator],
15✔
198
            [\T_STRING],
15✔
199
        ];
15✔
200

201
        for ($index = $startIndex; $startIndex < $endIndex; ++$index) {
15✔
202
            $match = $tokens->findSequence($oldMethodSequence, $index);
15✔
203

204
            if (null === $match) {
15✔
205
                return;
15✔
206
            }
207

208
            [$thisIndex, , $index] = array_keys($match);
15✔
209

210
            if (!isset($this->methodMap[$tokens[$index]->getContent()])) {
15✔
211
                continue;
15✔
212
            }
213

214
            $openIndex = $tokens->getNextTokenOfKind($index, ['(']);
15✔
215
            $closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
15✔
216
            $commaIndex = $tokens->getPrevMeaningfulToken($closeIndex);
15✔
217
            if ($tokens[$commaIndex]->equals(',')) {
15✔
218
                $tokens->removeTrailingWhitespace($commaIndex);
2✔
219
                $tokens->clearAt($commaIndex);
2✔
220
            }
221

222
            $arguments = $argumentsAnalyzer->getArguments($tokens, $openIndex, $closeIndex);
15✔
223
            $argumentsCnt = \count($arguments);
15✔
224

225
            $argumentsReplacements = ['expectException', $this->methodMap[$tokens[$index]->getContent()], 'expectExceptionCode'];
15✔
226

227
            $indent = $this->whitespacesConfig->getLineEnding().WhitespacesAnalyzer::detectIndent($tokens, $thisIndex);
15✔
228

229
            $isMultilineWhitespace = false;
15✔
230

231
            for ($cnt = $argumentsCnt - 1; $cnt >= 1; --$cnt) {
15✔
232
                $argStart = array_keys($arguments)[$cnt];
13✔
233
                $argBefore = $tokens->getPrevMeaningfulToken($argStart);
13✔
234

235
                if (!isset($argumentsReplacements[$cnt])) {
13✔
236
                    throw new \LogicException(\sprintf('Unexpected index %d to find replacement method.', $cnt));
×
237
                }
238

239
                if ('expectExceptionMessage' === $argumentsReplacements[$cnt]) {
13✔
240
                    $paramIndicatorIndex = $tokens->getNextMeaningfulToken($argBefore);
12✔
241
                    $afterParamIndicatorIndex = $tokens->getNextMeaningfulToken($paramIndicatorIndex);
12✔
242

243
                    if (
244
                        $tokens[$paramIndicatorIndex]->equals([\T_STRING, 'null'], false)
12✔
245
                        && $tokens[$afterParamIndicatorIndex]->equals(')')
12✔
246
                    ) {
247
                        if ($tokens[$argBefore + 1]->isWhitespace()) {
2✔
248
                            $tokens->clearTokenAndMergeSurroundingWhitespace($argBefore + 1);
1✔
249
                        }
250
                        $tokens->clearTokenAndMergeSurroundingWhitespace($argBefore);
2✔
251
                        $tokens->clearTokenAndMergeSurroundingWhitespace($paramIndicatorIndex);
2✔
252

253
                        continue;
2✔
254
                    }
255
                }
256

257
                $isMultilineWhitespace = $isMultilineWhitespace || ($tokens[$argStart]->isWhitespace() && !$tokens[$argStart]->isWhitespace(" \t"));
13✔
258
                $tokensOverrideArgStart = [
13✔
259
                    new Token([\T_WHITESPACE, $indent]),
13✔
260
                    new Token([\T_VARIABLE, '$this']),
13✔
261
                    new Token([\T_OBJECT_OPERATOR, '->']),
13✔
262
                    new Token([\T_STRING, $argumentsReplacements[$cnt]]),
13✔
263
                    new Token('('),
13✔
264
                ];
13✔
265
                $tokensOverrideArgBefore = [
13✔
266
                    new Token(')'),
13✔
267
                    new Token(';'),
13✔
268
                ];
13✔
269

270
                if ($isMultilineWhitespace) {
13✔
271
                    $tokensOverrideArgStart[] = new Token([\T_WHITESPACE, $indent.$this->whitespacesConfig->getIndent()]);
1✔
272
                    array_unshift($tokensOverrideArgBefore, new Token([\T_WHITESPACE, $indent]));
1✔
273
                }
274

275
                if ($tokens[$argStart]->isWhitespace()) {
13✔
276
                    $tokens->overrideRange($argStart, $argStart, $tokensOverrideArgStart);
12✔
277
                } else {
278
                    $tokens->insertAt($argStart, $tokensOverrideArgStart);
1✔
279
                }
280

281
                $tokens->overrideRange($argBefore, $argBefore, $tokensOverrideArgBefore);
13✔
282
            }
283

284
            $methodName = 'expectException';
15✔
285
            if ('expectExceptionMessageRegExp' === $tokens[$index]->getContent()) {
15✔
286
                $methodName = $this->methodMap[$tokens[$index]->getContent()];
2✔
287
            }
288
            $tokens[$index] = new Token([\T_STRING, $methodName]);
15✔
289
        }
290
    }
291
}
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