<table role="table">
<thead>
<tr>
<th>Q</th>
<th>A</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bug report?</td>
<td>yes</td>
</tr>
<tr>
<td>Feature request?</td>
<td>no</td>
</tr>
<tr>
<td>BC Break report?</td>
<td>no</td>
</tr>
<tr>
<td>RFC?</td>
<td>no</td>
</tr>
<tr>
<td>Symfony version</td>
<td>3.3</td>
</tr>
</tbody>
</table>
<p dir="auto">When you define no arguments, autowiring will give you a very precise error. However, if you manually define arguments that come after that using named arguments, the error will become extremely cryptic.</p>
<p dir="auto">Given the following PHP:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="namespace Hostnet;

interface TestInterface {}

class TestOne implements TestInterface {}
class TestTwo implements TestInterface {}

class TestDependency {}

class TestClass
{
    public function __construct(
        TestOne $testOne, 
        TestInterface $testTwo, 
        TestDependency $testDependency
    ) {}
}"><pre class="notranslate"><span class="pl-k">namespace</span> <span class="pl-v">Hostnet</span>;

<span class="pl-k">interface</span> <span class="pl-v">TestInterface</span> {}

<span class="pl-k">class</span> <span class="pl-v">TestOne</span> <span class="pl-k">implements</span> <span class="pl-v">TestInterface</span> {}
<span class="pl-k">class</span> <span class="pl-v">TestTwo</span> <span class="pl-k">implements</span> <span class="pl-v">TestInterface</span> {}

<span class="pl-k">class</span> <span class="pl-v">TestDependency</span> {}

<span class="pl-k">class</span> <span class="pl-v">TestClass</span>
{
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">__construct</span>(
        <span class="pl-smi"><span class="pl-smi">TestOne</span></span> <span class="pl-s1"><span class="pl-c1">$</span>testOne</span>, 
        <span class="pl-smi"><span class="pl-smi">TestInterface</span></span> <span class="pl-s1"><span class="pl-c1">$</span>testTwo</span>, 
        <span class="pl-smi"><span class="pl-smi">TestDependency</span></span> <span class="pl-s1"><span class="pl-c1">$</span>testDependency</span>
    ) {}
}</pre></div>
<p dir="auto">Using this configuration (100% autowiring):</p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="services:
    Hostnet\TestClass: ~
    Hostnet\TestOne: ~
    Hostnet\TestTwo: ~
    Hostnet\TestDependency:
        public: true"><pre class="notranslate"><span class="pl-ent">services</span>:
    <span class="pl-ent">Hostnet\TestClass</span>: <span class="pl-c1">~</span>
    <span class="pl-ent">Hostnet\TestOne</span>: <span class="pl-c1">~</span>
    <span class="pl-ent">Hostnet\TestTwo</span>: <span class="pl-c1">~</span>
    <span class="pl-ent">Hostnet\TestDependency</span>:
        <span class="pl-ent">public</span>: <span class="pl-c1">true</span></pre></div>
<p dir="auto">I will get the descriptive error:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="AutowiringFailedException

Cannot autowire service &quot;Hostnet\TestClass&quot;: argument &quot;$testTwo&quot; of 
method &quot;__construct()&quot; references interface &quot;Hostnet\TestInterface&quot; but 
no such service exists. You should maybe alias this interface to one of 
these existing services: &quot;Hostnet\TestOne&quot;, &quot;Hostnet\TestTwo&quot;."><pre class="notranslate"><code class="notranslate">AutowiringFailedException

Cannot autowire service "Hostnet\TestClass": argument "$testTwo" of 
method "__construct()" references interface "Hostnet\TestInterface" but 
no such service exists. You should maybe alias this interface to one of 
these existing services: "Hostnet\TestOne", "Hostnet\TestTwo".
</code></pre></div>
<p dir="auto">When I manually wire 1 argument that's positioned <em>after</em> the dependency that will fail (last argument in this case):</p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="services:
    Hostnet\TestClass:
        $testDependency: '@Hostnet\TestDependency'
    Hostnet\TestOne: ~
    Hostnet\TestTwo: ~
    Hostnet\TestDependency:
        public: true"><pre class="notranslate"><span class="pl-ent">services</span>:
    <span class="pl-ent">Hostnet\TestClass</span>:
        <span class="pl-ent">$testDependency</span>: <span class="pl-s"><span class="pl-pds">'</span>@Hostnet\TestDependency<span class="pl-pds">'</span></span>
    <span class="pl-ent">Hostnet\TestOne</span>: <span class="pl-c1">~</span>
    <span class="pl-ent">Hostnet\TestTwo</span>: <span class="pl-c1">~</span>
    <span class="pl-ent">Hostnet\TestDependency</span>:
        <span class="pl-ent">public</span>: <span class="pl-c1">true</span></pre></div>
<p dir="auto">This results in a crpytic error:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="RuntimeException

Invalid constructor argument 3 for service &quot;Hostnet\TestClass&quot;: argument 1 
must be defined before. Check your service definition."><pre class="notranslate"><code class="notranslate">RuntimeException

Invalid constructor argument 3 for service "Hostnet\TestClass": argument 1 
must be defined before. Check your service definition.
</code></pre></div>
<table role="table">
<thead>
<tr>
<th>Q</th>
<th>A</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bug report?</td>
<td>yes</td>
</tr>
<tr>
<td>Feature request?</td>
<td>no</td>
</tr>
<tr>
<td>BC Break report?</td>
<td>no</td>
</tr>
<tr>
<td>RFC?</td>
<td>no</td>
</tr>
<tr>
<td>Symfony version</td>
<td>3.3.*</td>
</tr>
</tbody>
</table>
<p dir="auto">From a training today :). Suppose this class:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class MyCoolClass
{
    public function __construct(Logger $logger, $isDebug)
    {

    }
}"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-v">MyCoolClass</span>
{
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">__construct</span>(<span class="pl-smi"><span class="pl-smi">Logger</span></span> <span class="pl-s1"><span class="pl-c1">$</span>logger</span>, <span class="pl-s1"><span class="pl-c1">$</span>isDebug</span>)
    {

    }
}</pre></div>
<p dir="auto">Because <code class="notranslate">Logger</code> is the wrong type-hint, I should receive this error:</p>
<blockquote>
<p dir="auto">Cannot autowire service "AppBundle\EventListener\MyCoolClass": argument "$logger" of method "__construct()" references class "Monolog\Logger" but no such service exists...</p>
</blockquote>
<p dir="auto">And at first, I do! But suppose I specify the <code class="notranslate">$isDebug</code> argument:</p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="services:
    AppBundle\Service\MyCoolClass:
        arguments:
            $isDebug: true"><pre class="notranslate"><span class="pl-ent">services</span>:
    <span class="pl-ent">AppBundle\Service\MyCoolClass</span>:
        <span class="pl-ent">arguments</span>:
            <span class="pl-ent">$isDebug</span>: <span class="pl-c1">true</span></pre></div>
<p dir="auto">As <em>soon</em> as this arg is specified the error changes:</p>
<blockquote>
<p dir="auto">Invalid constructor argument 2 for service "AppBundle\EventListener\MyCoolClass": argument 1 must be defined before.</p>
</blockquote>
<p dir="auto">The problem is:</p>
<p dir="auto">A) AutowirePass correctly throws an <code class="notranslate">AutowiringFailedException</code> due to the bad <code class="notranslate">Logger</code> type-hint. But, exceptions are not thrown in this class, but stored for later<br>
B) Because argument 1 appears to be missing, <code class="notranslate">CheckArgumentsValidityPass</code> throws the above exception<br>
C) The <code class="notranslate">AutowireExceptionPass</code> - which would normally throw the exception from (A), is never called - because (B) already threw its exception.</p>
<p dir="auto">The correct behavior is to always throw the first exception about the <code class="notranslate">Logger</code> class. But, I'm not sure how to do this. <code class="notranslate">AutowirePass</code> purposefully does not throw exceptions... because it waits to make sure the service wasn't removed from the container. But in this case, <code class="notranslate">CheckArgumentsValidityPass</code> steps in early and throws this other, less clear exception. It has no idea that the <em>true</em> exception is just waiting for the "ok" that the service wasn't removed.</p>
<p dir="auto">I'm open to ideas... :). In general, the passes are becoming a bit more dependent on each other, and that's complicating things.</p>
<p dir="auto">Cheers!</p>