diff --git a/composer.json b/composer.json
index 37b65509..38abfbb6 100644
--- a/composer.json
+++ b/composer.json
@@ -24,7 +24,7 @@
"doctrine/lexer": "^1.2.1",
"doctrine/mongodb-odm": "^1.3 || ^2.1",
"doctrine/orm": "^2.11.0",
- "doctrine/persistence": "^1.1 || ^2.0",
+ "doctrine/persistence": "^1.3.8 || ^2.2.1",
"nesbot/carbon": "^2.49",
"nikic/php-parser": "^4.13.2",
"php-parallel-lint/php-parallel-lint": "^1.2",
diff --git a/phpcs.xml b/phpcs.xml
index caa1df74..bb4f98b1 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -37,6 +37,8 @@
+
+
@@ -49,6 +51,7 @@
+
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
new file mode 100644
index 00000000..ea760d50
--- /dev/null
+++ b/phpstan-baseline.neon
@@ -0,0 +1,32 @@
+parameters:
+ ignoreErrors:
+ -
+ message: "#^Parameter \\#1 \\$drivers of class PHPStan\\\\Doctrine\\\\Mapping\\\\MappingDriverChain constructor expects array\\, array\\ given\\.$#"
+ count: 1
+ path: src/Doctrine/Mapping/ClassMetadataFactory.php
+
+ -
+ message: "#^Parameter \\#1 \\$entityName of class Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata constructor expects class\\-string\\, string given\\.$#"
+ count: 1
+ path: src/Doctrine/Mapping/ClassMetadataFactory.php
+
+ -
+ message: "#^Parameter \\#1 \\$className \\(class\\-string\\) of method PHPStan\\\\Doctrine\\\\Mapping\\\\MappingDriverChain\\:\\:isTransient\\(\\) should be contravariant with parameter \\$className \\(string\\) of method Doctrine\\\\Persistence\\\\Mapping\\\\Driver\\\\MappingDriver\\:\\:isTransient\\(\\)$#"
+ count: 1
+ path: src/Doctrine/Mapping/MappingDriverChain.php
+
+ -
+ message: "#^Parameter \\#1 \\$className \\(class\\-string\\) of method PHPStan\\\\Doctrine\\\\Mapping\\\\MappingDriverChain\\:\\:loadMetadataForClass\\(\\) should be contravariant with parameter \\$className \\(string\\) of method Doctrine\\\\Persistence\\\\Mapping\\\\Driver\\\\MappingDriver\\:\\:loadMetadataForClass\\(\\)$#"
+ count: 1
+ path: src/Doctrine/Mapping/MappingDriverChain.php
+
+ -
+ message: "#^PHPDoc tag @return contains generic type Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\ but interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory is not generic\\.$#"
+ count: 1
+ path: src/Type/Doctrine/ObjectMetadataResolver.php
+
+ -
+ message: "#^PHPDoc tag @var for property PHPStan\\\\Type\\\\Doctrine\\\\ObjectMetadataResolver\\:\\:\\$metadataFactory contains generic type Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\ but interface Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory is not generic\\.$#"
+ count: 1
+ path: src/Type/Doctrine/ObjectMetadataResolver.php
+
diff --git a/phpstan.neon b/phpstan.neon
index df5343a6..4f20d238 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,6 +1,7 @@
includes:
- extension.neon
- rules.neon
+ - phpstan-baseline.neon
- vendor/phpstan/phpstan-strict-rules/rules.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
@@ -13,6 +14,8 @@ parameters:
- tests/*/data-php-*/*
- tests/Rules/Doctrine/ORM/entity-manager.php
+ reportUnmatchedIgnoredErrors: false
+
ignoreErrors:
-
message: '~^Variable method call on Doctrine\\ORM\\QueryBuilder~'
diff --git a/src/Doctrine/Mapping/ClassMetadataFactory.php b/src/Doctrine/Mapping/ClassMetadataFactory.php
new file mode 100644
index 00000000..464b840e
--- /dev/null
+++ b/src/Doctrine/Mapping/ClassMetadataFactory.php
@@ -0,0 +1,46 @@
+getProperty('driver');
+ $driverProperty->setAccessible(true);
+
+ $drivers = [
+ new AnnotationDriver(new AnnotationReader()),
+ ];
+ if (class_exists(AttributeDriver::class) && PHP_VERSION_ID >= 80000) {
+ $drivers[] = new AttributeDriver([]);
+ }
+
+ $driverProperty->setValue($this, count($drivers) === 1 ? $drivers[0] : new MappingDriverChain($drivers));
+
+ $evmProperty = $parentReflection->getProperty('evm');
+ $evmProperty->setAccessible(true);
+ $evmProperty->setValue($this, new EventManager());
+ $this->initialized = true;
+
+ $targetPlatformProperty = $parentReflection->getProperty('targetPlatform');
+ $targetPlatformProperty->setAccessible(true);
+ $targetPlatformProperty->setValue($this, new MySqlPlatform());
+ }
+
+ protected function newClassMetadataInstance($className)
+ {
+ return new ClassMetadata($className);
+ }
+
+}
diff --git a/src/Doctrine/Mapping/MappingDriverChain.php b/src/Doctrine/Mapping/MappingDriverChain.php
new file mode 100644
index 00000000..6ed230a8
--- /dev/null
+++ b/src/Doctrine/Mapping/MappingDriverChain.php
@@ -0,0 +1,65 @@
+drivers = $drivers;
+ }
+
+ /**
+ * @param class-string $className
+ */
+ public function loadMetadataForClass($className, ClassMetadata $metadata): void
+ {
+ foreach ($this->drivers as $driver) {
+ if ($driver->isTransient($className)) {
+ continue;
+ }
+
+ $driver->loadMetadataForClass($className, $metadata);
+ return;
+ }
+ }
+
+ public function getAllClassNames()
+ {
+ $all = [];
+ foreach ($this->drivers as $driver) {
+ foreach ($driver->getAllClassNames() as $className) {
+ $all[] = $className;
+ }
+ }
+
+ return $all;
+ }
+
+ /**
+ * @param class-string $className
+ */
+ public function isTransient($className)
+ {
+ foreach ($this->drivers as $driver) {
+ if ($driver->isTransient($className)) {
+ continue;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/src/Type/Doctrine/ObjectMetadataResolver.php b/src/Type/Doctrine/ObjectMetadataResolver.php
index f2f851d3..da533f2b 100644
--- a/src/Type/Doctrine/ObjectMetadataResolver.php
+++ b/src/Type/Doctrine/ObjectMetadataResolver.php
@@ -2,7 +2,9 @@
namespace PHPStan\Type\Doctrine;
+use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
+use Doctrine\Persistence\Mapping\ClassMetadataFactory;
use Doctrine\Persistence\ObjectManager;
use PHPStan\Reflection\ReflectionProvider;
use function is_file;
@@ -26,6 +28,9 @@ final class ObjectMetadataResolver
/** @var string|null */
private $resolvedRepositoryClass;
+ /** @var ClassMetadataFactory|null */
+ private $metadataFactory;
+
public function __construct(
ReflectionProvider $reflectionProvider,
?string $objectManagerLoader,
@@ -66,17 +71,32 @@ public function getObjectManager(): ?ObjectManager
public function isTransient(string $className): bool
{
$objectManager = $this->getObjectManager();
- if ($objectManager === null) {
- return true;
- }
try {
+ if ($objectManager === null) {
+ return $this->getMetadataFactory()->isTransient($className);
+ }
+
return $objectManager->getMetadataFactory()->isTransient($className);
} catch (\ReflectionException $e) {
return true;
}
}
+ /**
+ * @return ClassMetadataFactory
+ */
+ private function getMetadataFactory(): ClassMetadataFactory
+ {
+ if ($this->metadataFactory !== null) {
+ return $this->metadataFactory;
+ }
+
+ $metadataFactory = new \PHPStan\Doctrine\Mapping\ClassMetadataFactory();
+
+ return $this->metadataFactory = $metadataFactory;
+ }
+
/**
* @template T of object
* @param class-string $className
@@ -84,17 +104,18 @@ public function isTransient(string $className): bool
*/
public function getClassMetadata(string $className): ?ClassMetadataInfo
{
- $objectManager = $this->getObjectManager();
- if ($objectManager === null) {
- return null;
- }
-
if ($this->isTransient($className)) {
return null;
}
+ $objectManager = $this->getObjectManager();
+
try {
- $metadata = $objectManager->getClassMetadata($className);
+ if ($objectManager === null) {
+ $metadata = $this->getMetadataFactory()->getMetadataFor($className);
+ } else {
+ $metadata = $objectManager->getClassMetadata($className);
+ }
} catch (\Doctrine\ORM\Mapping\MappingException $e) {
return null;
}
diff --git a/tests/DoctrineIntegration/ORM/EntityManagerWithoutObjectManagerLoaderIntegrationTest.php b/tests/DoctrineIntegration/ORM/EntityManagerWithoutObjectManagerLoaderIntegrationTest.php
new file mode 100644
index 00000000..39dff5eb
--- /dev/null
+++ b/tests/DoctrineIntegration/ORM/EntityManagerWithoutObjectManagerLoaderIntegrationTest.php
@@ -0,0 +1,38 @@
+createReflectionProvider(), __DIR__ . '/entity-manager.php', null),
+ new ObjectMetadataResolver($this->createReflectionProvider(), $this->objectManagerLoader, null),
new DescriptorRegistry([
new ArrayType(),
new BigIntType(),
@@ -77,9 +80,24 @@ protected function getRule(): Rule
);
}
- public function testRule(): void
+ /**
+ * @return array
+ */
+ public function dataObjectManagerLoader(): array
+ {
+ return [
+ [__DIR__ . '/entity-manager.php'],
+ [null],
+ ];
+ }
+
+ /**
+ * @dataProvider dataObjectManagerLoader
+ */
+ public function testRule(?string $objectManagerLoader): void
{
$this->allowNullablePropertyForRequiredField = false;
+ $this->objectManagerLoader = $objectManagerLoader;
$this->analyse([__DIR__ . '/data/MyBrokenEntity.php'], [
[
'Property PHPStan\Rules\Doctrine\ORM\MyBrokenEntity::$id type mapping mismatch: database can contain string but property expects int|null.',
@@ -140,9 +158,13 @@ public function testRule(): void
]);
}
- public function testRuleWithAllowedNullableProperty(): void
+ /**
+ * @dataProvider dataObjectManagerLoader
+ */
+ public function testRuleWithAllowedNullableProperty(?string $objectManagerLoader): void
{
$this->allowNullablePropertyForRequiredField = true;
+ $this->objectManagerLoader = $objectManagerLoader;
$this->analyse([__DIR__ . '/data/MyBrokenEntity.php'], [
[
'Property PHPStan\Rules\Doctrine\ORM\MyBrokenEntity::$id type mapping mismatch: database can contain string but property expects int|null.',
@@ -191,15 +213,23 @@ public function testRuleWithAllowedNullableProperty(): void
]);
}
- public function testRuleOnMyEntity(): void
+ /**
+ * @dataProvider dataObjectManagerLoader
+ */
+ public function testRuleOnMyEntity(?string $objectManagerLoader): void
{
$this->allowNullablePropertyForRequiredField = false;
+ $this->objectManagerLoader = $objectManagerLoader;
$this->analyse([__DIR__ . '/data/MyEntity.php'], []);
}
- public function testSuperclass(): void
+ /**
+ * @dataProvider dataObjectManagerLoader
+ */
+ public function testSuperclass(?string $objectManagerLoader): void
{
$this->allowNullablePropertyForRequiredField = false;
+ $this->objectManagerLoader = $objectManagerLoader;
$this->analyse([__DIR__ . '/data/MyBrokenSuperclass.php'], [
[
'Property PHPStan\Rules\Doctrine\ORM\MyBrokenSuperclass::$five type mapping mismatch: database can contain resource but property expects int.',
@@ -213,9 +243,10 @@ public function testSuperclass(): void
* @param string $file
* @param mixed[] $expectedErrors
*/
- public function testGeneratedIds(string $file, array $expectedErrors): void
+ public function testGeneratedIds(string $file, array $expectedErrors, ?string $objectManagerLoader): void
{
$this->allowNullablePropertyForRequiredField = false;
+ $this->objectManagerLoader = $objectManagerLoader;
$this->analyse([$file], $expectedErrors);
}
@@ -224,7 +255,8 @@ public function testGeneratedIds(string $file, array $expectedErrors): void
*/
public function generatedIdsProvider(): Iterator
{
- yield 'not nullable' => [__DIR__ . '/data/GeneratedIdEntity1.php', []];
+ yield 'not nullable' => [__DIR__ . '/data/GeneratedIdEntity1.php', [], __DIR__ . '/entity-manager.php'];
+ yield 'not nullable 2' => [__DIR__ . '/data/GeneratedIdEntity1.php', [], null];
yield 'nullable column' => [
__DIR__ . '/data/GeneratedIdEntity2.php',
[
@@ -233,22 +265,47 @@ public function generatedIdsProvider(): Iterator
19,
],
],
+ __DIR__ . '/entity-manager.php',
];
- yield 'nullable property' => [__DIR__ . '/data/GeneratedIdEntity3.php', []];
- yield 'nullable both' => [__DIR__ . '/data/GeneratedIdEntity4.php', []];
- yield 'composite' => [__DIR__ . '/data/CompositePrimaryKeyEntity1.php', []];
- yield 'no generated value 1' => [__DIR__ . '/data/GeneratedIdEntity5.php', []];
+ yield 'nullable column 2' => [
+ __DIR__ . '/data/GeneratedIdEntity2.php',
+ [
+ [
+ 'Property PHPStan\Rules\Doctrine\ORM\GeneratedIdEntity2::$id type mapping mismatch: database can contain string|null but property expects string.',
+ 19,
+ ],
+ ],
+ null,
+ ];
+ yield 'nullable property' => [__DIR__ . '/data/GeneratedIdEntity3.php', [], __DIR__ . '/entity-manager.php'];
+ yield 'nullable property 2' => [__DIR__ . '/data/GeneratedIdEntity3.php', [], null];
+ yield 'nullable both' => [__DIR__ . '/data/GeneratedIdEntity4.php', [], __DIR__ . '/entity-manager.php'];
+ yield 'nullable both 2' => [__DIR__ . '/data/GeneratedIdEntity4.php', [], null];
+ yield 'composite' => [__DIR__ . '/data/CompositePrimaryKeyEntity1.php', [], __DIR__ . '/entity-manager.php'];
+ yield 'composite 2' => [__DIR__ . '/data/CompositePrimaryKeyEntity1.php', [], null];
+ yield 'no generated value 1' => [__DIR__ . '/data/GeneratedIdEntity5.php', [], __DIR__ . '/entity-manager.php'];
+ yield 'no generated value 1 2' => [__DIR__ . '/data/GeneratedIdEntity5.php', [], null];
yield 'no generated value 2' => [__DIR__ . '/data/GeneratedIdEntity6.php', [
[
'Property PHPStan\Rules\Doctrine\ORM\GeneratedIdEntity6::$id type mapping mismatch: property can contain int|null but database expects int.',
18,
],
- ]];
+ ], __DIR__ . '/entity-manager.php'];
+ yield 'no generated value 2 2' => [__DIR__ . '/data/GeneratedIdEntity6.php', [
+ [
+ 'Property PHPStan\Rules\Doctrine\ORM\GeneratedIdEntity6::$id type mapping mismatch: property can contain int|null but database expects int.',
+ 18,
+ ],
+ ], null];
}
- public function testCustomType(): void
+ /**
+ * @dataProvider dataObjectManagerLoader
+ */
+ public function testCustomType(?string $objectManagerLoader): void
{
$this->allowNullablePropertyForRequiredField = false;
+ $this->objectManagerLoader = $objectManagerLoader;
$this->analyse([__DIR__ . '/data/EntityWithCustomType.php'], [
[
'Property PHPStan\Rules\Doctrine\ORM\EntityWithCustomType::$foo type mapping mismatch: database can contain DateTimeInterface but property expects int.',
@@ -265,9 +322,13 @@ public function testCustomType(): void
]);
}
- public function testUnknownType(): void
+ /**
+ * @dataProvider dataObjectManagerLoader
+ */
+ public function testUnknownType(?string $objectManagerLoader): void
{
$this->allowNullablePropertyForRequiredField = false;
+ $this->objectManagerLoader = $objectManagerLoader;
$this->analyse([__DIR__ . '/data/EntityWithUnknownType.php'], [
[
'Property PHPStan\Rules\Doctrine\ORM\EntityWithUnknownType::$foo: Doctrine type "unknown" does not have any registered descriptor.',
@@ -276,13 +337,17 @@ public function testUnknownType(): void
]);
}
- public function testEnumType(): void
+ /**
+ * @dataProvider dataObjectManagerLoader
+ */
+ public function testEnumType(?string $objectManagerLoader): void
{
if (PHP_VERSION_ID < 80100) {
self::markTestSkipped('Test requires PHP 8.1.');
}
$this->allowNullablePropertyForRequiredField = false;
+ $this->objectManagerLoader = $objectManagerLoader;
$this->analyse([__DIR__ . '/data-attributes/enum-type.php'], [
[
'Property PHPStan\Rules\Doctrine\ORMAttributes\Foo::$type2 type mapping mismatch: database can contain PHPStan\Rules\Doctrine\ORMAttributes\FooEnum but property expects PHPStan\Rules\Doctrine\ORMAttributes\BarEnum.',
diff --git a/tests/Rules/Doctrine/ORM/EntityNotFinalRuleTest.php b/tests/Rules/Doctrine/ORM/EntityNotFinalRuleTest.php
index feb775c9..bdddb44d 100644
--- a/tests/Rules/Doctrine/ORM/EntityNotFinalRuleTest.php
+++ b/tests/Rules/Doctrine/ORM/EntityNotFinalRuleTest.php
@@ -13,10 +13,13 @@
class EntityNotFinalRuleTest extends RuleTestCase
{
+ /** @var string|null */
+ private $objectManagerLoader;
+
protected function getRule(): Rule
{
return new EntityNotFinalRule(
- new ObjectMetadataResolver($this->createReflectionProvider(), __DIR__ . '/entity-manager.php', null)
+ new ObjectMetadataResolver($this->createReflectionProvider(), $this->objectManagerLoader, null)
);
}
@@ -27,6 +30,18 @@ protected function getRule(): Rule
*/
public function testRule(string $file, array $expectedErrors): void
{
+ $this->objectManagerLoader = __DIR__ . '/entity-manager.php';
+ $this->analyse([$file], $expectedErrors);
+ }
+
+ /**
+ * @dataProvider ruleProvider
+ * @param string $file
+ * @param mixed[] $expectedErrors
+ */
+ public function testRuleWithoutObjectManagerLoader(string $file, array $expectedErrors): void
+ {
+ $this->objectManagerLoader = null;
$this->analyse([$file], $expectedErrors);
}
diff --git a/tests/Rules/Doctrine/ORM/EntityRelationRuleTest.php b/tests/Rules/Doctrine/ORM/EntityRelationRuleTest.php
index 03a65513..15be62e2 100644
--- a/tests/Rules/Doctrine/ORM/EntityRelationRuleTest.php
+++ b/tests/Rules/Doctrine/ORM/EntityRelationRuleTest.php
@@ -16,10 +16,13 @@ class EntityRelationRuleTest extends RuleTestCase
/** @var bool */
private $allowNullablePropertyForRequiredField;
+ /** @var string|null */
+ private $objectManagerLoader;
+
protected function getRule(): Rule
{
return new EntityRelationRule(
- new ObjectMetadataResolver($this->createReflectionProvider(), __DIR__ . '/entity-manager.php', null),
+ new ObjectMetadataResolver($this->createReflectionProvider(), $this->objectManagerLoader, null),
$this->allowNullablePropertyForRequiredField
);
}
@@ -32,6 +35,19 @@ protected function getRule(): Rule
public function testRule(string $file, array $expectedErrors): void
{
$this->allowNullablePropertyForRequiredField = false;
+ $this->objectManagerLoader = __DIR__ . '/entity-manager.php';
+ $this->analyse([$file], $expectedErrors);
+ }
+
+ /**
+ * @dataProvider ruleProvider
+ * @param string $file
+ * @param mixed[] $expectedErrors
+ */
+ public function testRuleWithoutObjectManagerLoader(string $file, array $expectedErrors): void
+ {
+ $this->allowNullablePropertyForRequiredField = false;
+ $this->objectManagerLoader = null;
$this->analyse([$file], $expectedErrors);
}
@@ -167,6 +183,19 @@ public function ruleProvider(): Iterator
public function testRuleWithAllowedNullableProperty(string $file, array $expectedErrors): void
{
$this->allowNullablePropertyForRequiredField = true;
+ $this->objectManagerLoader = __DIR__ . '/entity-manager.php';
+ $this->analyse([$file], $expectedErrors);
+ }
+
+ /**
+ * @dataProvider ruleWithAllowedNullablePropertyProvider
+ * @param string $file
+ * @param mixed[] $expectedErrors
+ */
+ public function testRuleWithAllowedNullablePropertyWithoutObjectManagerLoader(string $file, array $expectedErrors): void
+ {
+ $this->allowNullablePropertyForRequiredField = true;
+ $this->objectManagerLoader = null;
$this->analyse([$file], $expectedErrors);
}
diff --git a/tests/Rules/Doctrine/ORM/RepositoryMethodCallRuleWithoutObjectManagerLoaderTest.php b/tests/Rules/Doctrine/ORM/RepositoryMethodCallRuleWithoutObjectManagerLoaderTest.php
new file mode 100644
index 00000000..1997453d
--- /dev/null
+++ b/tests/Rules/Doctrine/ORM/RepositoryMethodCallRuleWithoutObjectManagerLoaderTest.php
@@ -0,0 +1,82 @@
+
+ */
+class RepositoryMethodCallRuleWithoutObjectManagerLoaderTest extends RuleTestCase
+{
+
+ protected function getRule(): Rule
+ {
+ return new RepositoryMethodCallRule(new ObjectMetadataResolver($this->createReflectionProvider(), null, null));
+ }
+
+ /**
+ * @return string[]
+ */
+ public static function getAdditionalConfigFiles(): array
+ {
+ return [__DIR__ . '/magic-repository-no-object-manager-loader.neon'];
+ }
+
+ public function testRule(): void
+ {
+ $this->analyse([__DIR__ . '/data/repository-findBy-etc.php'], [
+ [
+ 'Call to method Doctrine\ORM\EntityRepository::findBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
+ 23,
+ ],
+ [
+ 'Call to method Doctrine\ORM\EntityRepository::findBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
+ 24,
+ ],
+ [
+ 'Call to method Doctrine\ORM\EntityRepository::findBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
+ 25,
+ ],
+ [
+ 'Call to method Doctrine\ORM\EntityRepository::findBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
+ 25,
+ ],
+ [
+ 'Call to method Doctrine\ORM\EntityRepository::findOneBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
+ 33,
+ ],
+ [
+ 'Call to method Doctrine\ORM\EntityRepository::findOneBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
+ 34,
+ ],
+ [
+ 'Call to method Doctrine\ORM\EntityRepository::findOneBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
+ 35,
+ ],
+ [
+ 'Call to method Doctrine\ORM\EntityRepository::findOneBy() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
+ 35,
+ ],
+ [
+ 'Call to method Doctrine\ORM\EntityRepository::count() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
+ 43,
+ ],
+ [
+ 'Call to method Doctrine\ORM\EntityRepository::count() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
+ 44,
+ ],
+ [
+ 'Call to method Doctrine\ORM\EntityRepository::count() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $nonexistent.',
+ 45,
+ ],
+ [
+ 'Call to method Doctrine\ORM\EntityRepository::count() - entity PHPStan\Rules\Doctrine\ORM\MyEntity does not have a field named $transient.',
+ 45,
+ ],
+ ]);
+ }
+
+}
diff --git a/tests/Rules/Doctrine/ORM/magic-repository-no-object-manager-loader.neon b/tests/Rules/Doctrine/ORM/magic-repository-no-object-manager-loader.neon
new file mode 100644
index 00000000..a2c8131d
--- /dev/null
+++ b/tests/Rules/Doctrine/ORM/magic-repository-no-object-manager-loader.neon
@@ -0,0 +1,2 @@
+includes:
+ - ../../../../extension.neon