Skip to content

Commit efdc75b

Browse files
committed
minor #18691 [DX][DI] Make failed autowiring error messages more explicit (lemoinem)
This PR was merged into the 2.8 branch. Discussion ---------- [DX][DI] Make failed autowiring error messages more explicit | Q | A | ------------- | --- | Branch? | 2.8 | Bug fix? | no (better DX integration) | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #18658 | License | MIT | Doc PR | N/A This is the PR improving the auto wiring error messages. Two errors messages have augmented: If a type-hint does not match any existing type and a service for this type cannot be automatically created, the error message now says so, instead of simply saying the type cannot be autowired. If a type-hint matches multiple services and none of them provides an autowiringType for it, the error message now says so and list the candidate services, instead of simply saying the type cannot be autowired. Commits ------- 2ac81f9 Make failed autowiring error messages more explicit
2 parents 5f08bdf + d97df97 commit efdc75b

File tree

2 files changed

+45
-15
lines changed

2 files changed

+45
-15
lines changed

Compiler/AutowirePass.php

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ private function completeDefinition($id, Definition $definition)
105105
$this->populateAvailableTypes();
106106
}
107107

108-
if (isset($this->types[$typeHint->name])) {
108+
if (isset($this->types[$typeHint->name]) && !isset($this->notGuessableTypes[$typeHint->name])) {
109109
$value = new Reference($this->types[$typeHint->name]);
110110
} else {
111111
try {
@@ -190,22 +190,26 @@ private function populateAvailableType($id, Definition $definition)
190190
*/
191191
private function set($type, $id)
192192
{
193-
if (isset($this->definedTypes[$type]) || isset($this->notGuessableTypes[$type])) {
193+
if (isset($this->definedTypes[$type])) {
194194
return;
195195
}
196196

197-
if (isset($this->types[$type])) {
198-
if ($this->types[$type] === $id) {
199-
return;
200-
}
197+
if (!isset($this->types[$type])) {
198+
$this->types[$type] = $id;
201199

202-
unset($this->types[$type]);
203-
$this->notGuessableTypes[$type] = true;
200+
return;
201+
}
204202

203+
if ($this->types[$type] === $id) {
205204
return;
206205
}
207206

208-
$this->types[$type] = $id;
207+
if (!isset($this->notGuessableTypes[$type])) {
208+
$this->notGuessableTypes[$type] = true;
209+
$this->types[$type] = (array) $this->types[$type];
210+
}
211+
212+
$this->types[$type][] = $id;
209213
}
210214

211215
/**
@@ -220,8 +224,14 @@ private function set($type, $id)
220224
*/
221225
private function createAutowiredDefinition(\ReflectionClass $typeHint, $id)
222226
{
223-
if (isset($this->notGuessableTypes[$typeHint->name]) || !$typeHint->isInstantiable()) {
224-
throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s".', $typeHint->name, $id));
227+
if (isset($this->notGuessableTypes[$typeHint->name])) {
228+
throw new RuntimeException(sprintf('Unable to autowire argument of type "%s" for the service "%s". Several services implementing this type have been declared: "%s".', $typeHint->name, $id, implode('", "', $this->types[$typeHint->name])));
229+
}
230+
231+
$noAvailableDefinitionMessage = sprintf('Unable to autowire argument of type "%s" for the service "%s". This type cannot be instantiated automatically and no service implementing this type is declared.', $typeHint->name, $id);
232+
233+
if (!$typeHint->isInstantiable()) {
234+
throw new RuntimeException($noAvailableDefinitionMessage);
225235
}
226236

227237
$argumentId = sprintf('autowired.%s', $typeHint->name);
@@ -230,7 +240,12 @@ private function createAutowiredDefinition(\ReflectionClass $typeHint, $id)
230240
$argumentDefinition->setPublic(false);
231241

232242
$this->populateAvailableType($argumentId, $argumentDefinition);
233-
$this->completeDefinition($argumentId, $argumentDefinition);
243+
244+
try {
245+
$this->completeDefinition($argumentId, $argumentDefinition);
246+
} catch (RuntimeException $e) {
247+
throw new RuntimeException($noAvailableDefinitionMessage, 0, $e);
248+
}
234249

235250
return new Reference($argumentId);
236251
}

Tests/Compiler/AutowirePassTest.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public function testCompleteExistingDefinitionWithNotDefinedArguments()
103103

104104
/**
105105
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
106-
* @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a".
106+
* @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". Several services implementing this type have been declared: "c1", "c2".
107107
*/
108108
public function testTypeCollision()
109109
{
@@ -120,7 +120,7 @@ public function testTypeCollision()
120120

121121
/**
122122
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
123-
* @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" for the service "a".
123+
* @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" for the service "a". Several services implementing this type have been declared: "a1", "a2".
124124
*/
125125
public function testTypeNotGuessable()
126126
{
@@ -137,7 +137,7 @@ public function testTypeNotGuessable()
137137

138138
/**
139139
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
140-
* @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\A" for the service "a".
140+
* @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\A" for the service "a". Several services implementing this type have been declared: "a1", "a2".
141141
*/
142142
public function testTypeNotGuessableWithSubclass()
143143
{
@@ -207,6 +207,21 @@ public function testCreateDefinition()
207207
$this->assertEquals(__NAMESPACE__.'\Lille', $lilleDefinition->getClass());
208208
}
209209

210+
/**
211+
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
212+
* @expectedExceptionMessage Unable to autowire argument of type "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" for the service "a". This type cannot be instantiated automatically and no service implementing this type is declared.
213+
*/
214+
public function testCreateNonInstanciable()
215+
{
216+
$container = new ContainerBuilder();
217+
218+
$aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired');
219+
$aDefinition->setAutowired(true);
220+
221+
$pass = new AutowirePass();
222+
$pass->process($container);
223+
}
224+
210225
public function testResolveParameter()
211226
{
212227
$container = new ContainerBuilder();

0 commit comments

Comments
 (0)