From 9c7d4daa2e201ce0865b62f6bbcd3c42db38d717 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 2 Sep 2026 11:02:20 +0200 Subject: [PATCH] [CodeQuality] Skip dynamic method call in AllowMockObjectsForDataProviderRector Fixes https://github.com/rectorphp/rector/issues/9877 A dynamic call like `$sut->$method()` was treated as a literal `->method()` call because the variable name resolved to "method". Skip when the call name is not an Identifier. Claude-Session: https://claude.ai/code/session_01JHq62QomBxARKhP7RxQ6gJ --- .../Fixture/skip_dynamic_method_call.php.inc | 26 +++++++++++++++++++ .../NodeAnalyser/MockObjectExprDetector.php | 5 ++++ 2 files changed, 31 insertions(+) create mode 100644 rules-tests/PHPUnit120/Rector/Class_/AllowMockObjectsForDataProviderRector/Fixture/skip_dynamic_method_call.php.inc diff --git a/rules-tests/PHPUnit120/Rector/Class_/AllowMockObjectsForDataProviderRector/Fixture/skip_dynamic_method_call.php.inc b/rules-tests/PHPUnit120/Rector/Class_/AllowMockObjectsForDataProviderRector/Fixture/skip_dynamic_method_call.php.inc new file mode 100644 index 00000000..70778272 --- /dev/null +++ b/rules-tests/PHPUnit120/Rector/Class_/AllowMockObjectsForDataProviderRector/Fixture/skip_dynamic_method_call.php.inc @@ -0,0 +1,26 @@ +expectException(\RuntimeException::class); + + $sut->$method(); + } + + public static function methodNameProvider(): iterable + { + yield ['realMethodName']; + } +} diff --git a/rules/CodeQuality/NodeAnalyser/MockObjectExprDetector.php b/rules/CodeQuality/NodeAnalyser/MockObjectExprDetector.php index 2a11faf8..5728845a 100644 --- a/rules/CodeQuality/NodeAnalyser/MockObjectExprDetector.php +++ b/rules/CodeQuality/NodeAnalyser/MockObjectExprDetector.php @@ -36,6 +36,11 @@ public function hasMethodCallWithoutExpects(ClassMethod $classMethod): bool $methodCalls = $this->betterNodeFinder->findInstancesOfScoped((array) $classMethod->stmts, [MethodCall::class]); foreach ($methodCalls as $methodCall) { + // dynamic method call, e.g. $sut->$method(), is not a literal ->method() call + if (! $methodCall->name instanceof Identifier) { + continue; + } + if (! $this->nodeNameResolver->isName($methodCall->name, 'method')) { continue; }