<h2 dir="auto">Bug Report</h2>

<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I would like to work on a fix!</li>
</ul>

<p dir="auto"><strong>Current Behavior</strong><br>
<code class="notranslate">scope.crawl()</code> does not add references to bindings correctly in some cases. In particular I get the bug when referencing a class inside a class method.</p>
<p dir="auto"><strong>Input Code</strong><br>
<a class="commit-link" href="https://github.com/regiontog/babel/compare/master...bug/missing-references-after-crawl">regiontog/babel@<tt>master...bug/missing-references-after-crawl</tt></a></p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="const path = getPath(&quot;class a { build() { return new a(); } }&quot;);
path.scope.crawl();
const referencePaths = path.scope.bindings.a.referencePaths;
expect(referencePaths).toHaveLength(1);"><pre class="notranslate"><span class="pl-k">const</span> <span class="pl-s1">path</span> <span class="pl-c1">=</span> <span class="pl-en">getPath</span><span class="pl-kos">(</span><span class="pl-s">"class a { build() { return new a(); } }"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-s1">path</span><span class="pl-kos">.</span><span class="pl-c1">scope</span><span class="pl-kos">.</span><span class="pl-en">crawl</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-k">const</span> <span class="pl-s1">referencePaths</span> <span class="pl-c1">=</span> <span class="pl-s1">path</span><span class="pl-kos">.</span><span class="pl-c1">scope</span><span class="pl-kos">.</span><span class="pl-c1">bindings</span><span class="pl-kos">.</span><span class="pl-c1">a</span><span class="pl-kos">.</span><span class="pl-c1">referencePaths</span><span class="pl-kos">;</span>
<span class="pl-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">referencePaths</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">toHaveLength</span><span class="pl-kos">(</span><span class="pl-c1">1</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto"><strong>Expected behavior/code</strong><br>
I expect the binding <code class="notranslate">a</code> to have 1 reference inside the scope of the <code class="notranslate">build</code> class method after a <code class="notranslate">crawl</code>. The binding has 1 reference before the <code class="notranslate">crawl</code> call is made.</p>
<p dir="auto"><strong>Environment</strong></p>

<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="System:
    OS: Linux 5.4 Arch Linux
  Binaries:
    Node: 13.3.0 - /usr/bin/node
    Yarn: 1.21.0 - /usr/bin/yarn
    npm: 6.12.1 - /usr/bin/npm
  Monorepos:
    Lerna: 3.19.0
npmPackages:
    @babel/cli: ^7.7.0 =&gt; 7.7.5
    @babel/core: ^7.7.2 =&gt; 7.7.5
    @babel/eslint-plugin-development: ^1.0.1 =&gt; 1.0.1
    @babel/plugin-proposal-class-properties: ^7.7.0 =&gt; 7.7.4
    @babel/plugin-proposal-export-namespace-from: ^7.5.2 =&gt; 7.7.4
    @babel/plugin-proposal-nullish-coalescing-operator: ^7.4.4 =&gt; 7.7.4
    @babel/plugin-proposal-numeric-separator: ^7.2.0 =&gt; 7.7.4
    @babel/plugin-proposal-object-rest-spread: ^7.7.4 =&gt; 7.7.4
    @babel/plugin-proposal-optional-chaining: ^7.6.0 =&gt; 7.7.5
    @babel/plugin-transform-flow-strip-types: ^7.7.4 =&gt; 7.7.4
    @babel/plugin-transform-for-of: ^7.7.4 =&gt; 7.7.4
    @babel/plugin-transform-modules-commonjs: ^7.7.0 =&gt; 7.7.5
    @babel/plugin-transform-runtime: ^7.6.2 =&gt; 7.7.6
    @babel/preset-env: ^7.7.1 =&gt; 7.7.6
    @babel/preset-flow: ^7.0.0 =&gt; 7.7.4
    @babel/register: ^7.7.0 =&gt; 7.7.4
    @babel/runtime: ^7.7.2 =&gt; 7.7.6
    babel-eslint: ^11.0.0-beta.2 =&gt; 11.0.0-beta.2
    babel-jest: ^24.9.0 =&gt; 24.9.0
    babel-plugin-transform-charcodes: ^0.2.0 =&gt; 0.2.0
    eslint: ^6.0.1 =&gt; 6.7.2
    eslint-config-babel: ^9.0.0 =&gt; 9.0.0
    gulp-babel: ^8.0.0 =&gt; 8.0.0
    jest: ^24.9.0 =&gt; 24.9.0
    lerna: ^3.19.0 =&gt; 3.19.0
    rollup-plugin-babel: ^4.0.0 =&gt; 4.3.3"><pre class="notranslate"><code class="notranslate">System:
    OS: Linux 5.4 Arch Linux
  Binaries:
    Node: 13.3.0 - /usr/bin/node
    Yarn: 1.21.0 - /usr/bin/yarn
    npm: 6.12.1 - /usr/bin/npm
  Monorepos:
    Lerna: 3.19.0
npmPackages:
    @babel/cli: ^7.7.0 =&gt; 7.7.5
    @babel/core: ^7.7.2 =&gt; 7.7.5
    @babel/eslint-plugin-development: ^1.0.1 =&gt; 1.0.1
    @babel/plugin-proposal-class-properties: ^7.7.0 =&gt; 7.7.4
    @babel/plugin-proposal-export-namespace-from: ^7.5.2 =&gt; 7.7.4
    @babel/plugin-proposal-nullish-coalescing-operator: ^7.4.4 =&gt; 7.7.4
    @babel/plugin-proposal-numeric-separator: ^7.2.0 =&gt; 7.7.4
    @babel/plugin-proposal-object-rest-spread: ^7.7.4 =&gt; 7.7.4
    @babel/plugin-proposal-optional-chaining: ^7.6.0 =&gt; 7.7.5
    @babel/plugin-transform-flow-strip-types: ^7.7.4 =&gt; 7.7.4
    @babel/plugin-transform-for-of: ^7.7.4 =&gt; 7.7.4
    @babel/plugin-transform-modules-commonjs: ^7.7.0 =&gt; 7.7.5
    @babel/plugin-transform-runtime: ^7.6.2 =&gt; 7.7.6
    @babel/preset-env: ^7.7.1 =&gt; 7.7.6
    @babel/preset-flow: ^7.0.0 =&gt; 7.7.4
    @babel/register: ^7.7.0 =&gt; 7.7.4
    @babel/runtime: ^7.7.2 =&gt; 7.7.6
    babel-eslint: ^11.0.0-beta.2 =&gt; 11.0.0-beta.2
    babel-jest: ^24.9.0 =&gt; 24.9.0
    babel-plugin-transform-charcodes: ^0.2.0 =&gt; 0.2.0
    eslint: ^6.0.1 =&gt; 6.7.2
    eslint-config-babel: ^9.0.0 =&gt; 9.0.0
    gulp-babel: ^8.0.0 =&gt; 8.0.0
    jest: ^24.9.0 =&gt; 24.9.0
    lerna: ^3.19.0 =&gt; 3.19.0
    rollup-plugin-babel: ^4.0.0 =&gt; 4.3.3
</code></pre></div>
<ul dir="auto">
<li>How you are using Babel: API</li>
</ul>
<p dir="auto"><strong>Possible Solution</strong></p>

<p dir="auto">It seems suspect to me to add the reference to a binding in the references own scope <a href="https://github.com/babel/babel/blob/d389b702e65bfb0a3cc8258907db76560491547a/packages/babel-traverse/src/scope/index.js#L756">here</a></p>
<p dir="auto"><strong>Additional context/Screenshots</strong><br>
Output of test linked above:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="scope › binding paths › reference paths after crawl

    expect(received).toHaveLength(expected)

    Expected length: 1
    Received length: 0
    Received array:  []

      276 |       path.scope.crawl();
      277 |       const referencePaths = path.scope.bindings.a.referencePaths;
    &gt; 278 |       expect(referencePaths).toHaveLength(1);
          |                              ^
      279 |     });
      280 |   });
      281 |

      at Object.&lt;anonymous&gt; (packages/babel-traverse/test/scope.js:278:30)"><pre class="notranslate"><code class="notranslate">scope › binding paths › reference paths after crawl

    expect(received).toHaveLength(expected)

    Expected length: 1
    Received length: 0
    Received array:  []

      276 |       path.scope.crawl();
      277 |       const referencePaths = path.scope.bindings.a.referencePaths;
    &gt; 278 |       expect(referencePaths).toHaveLength(1);
          |                              ^
      279 |     });
      280 |   });
      281 |

      at Object.&lt;anonymous&gt; (packages/babel-traverse/test/scope.js:278:30)
</code></pre></div>
<blockquote>
<p dir="auto">Copying from <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="220430459" data-permission-text="Title is private" data-url="https://github.com/babel/notes/issues/19" data-hovercard-type="pull_request" data-hovercard-url="/babel/notes/pull/19/hovercard" href="https://github.com/babel/notes/pull/19">babel/notes#19</a>, many other issues linked there</p>
</blockquote>
<h3 dir="auto">Issue</h3>
<p dir="auto">The order that you specify plugins/presets in a config matters in Babel. User's shouldn't have to change their config to make it compile correctly (or get a weird error message about how a plugin isn't enabled when it's just in the "wrong" order).</p>
<blockquote>
<p dir="auto">From the <a href="https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy">https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy</a> repo</p>
</blockquote>
<p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://cloud.githubusercontent.com/assets/588473/24915926/95fcc932-1ea6-11e7-9a0a-002f2950517e.png"><img src="https://cloud.githubusercontent.com/assets/588473/24915926/95fcc932-1ea6-11e7-9a0a-002f2950517e.png" alt="screen shot 2017-04-11 at 11 04 05 am" style="max-width: 100%;"></a></p>
<p dir="auto">Simpler example: if you specify a plugin only modifies ES2015 classes and you make that run after the plugin that transforms classes, then it won't run at all because at that point the classes will have been transformed into a function already.</p>
<p dir="auto"><code class="notranslate">{ plugins: ["modify-es2015-classes", "transform-es2015-classes"] }</code> // works<br>
<code class="notranslate">{ plugins: ["transform-es2015-classes", "modify-es2015-classes"] }</code> // fails</p>
<p dir="auto">How can we make Babel automatically know how to order the plugins for the user, or at least give a clearer error message? Or how can we let the user opt-in to a specific ordering?</p>
<h4 dir="auto">Duplicate plugins in a config</h4>
<p dir="auto">What if a user includes "es2015", "env", "latest", and "transform-arrow-functions"? That includes the arrow function transform 4 times! It would be nice to have a nice error message to explain that there's duplicate functionality or that you have a plugin that isn't already on.</p>
<h3 dir="auto">Possible Solution: Capabilities Discovery</h3>
<ul dir="auto">
<li>Plugins provide a function to state the capabilities it provides and which it needs.</li>
<li>This includes both syntax and built-ins</li>
<li>This is important to fix plugin ordering automatically in Babel itself. If the classes plugin transpiles classes and decorators run over classes, then with this information Babel will know to make decorators run first.</li>
</ul>
<p dir="auto">This could mean exposing a method in plugins called <code class="notranslate">hasCapability('capability-here')</code></p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// minify-plugin.js
// return true or false based on if the target environment natively supports arrows
// or if the plugin that compiles arrow functions is enabled
if (hasCapability('es2015-arrow-function') {
  // output an arrow function
} else {
  // output a function expression
}"><pre class="notranslate"><span class="pl-c">// minify-plugin.js</span>
<span class="pl-c">// return true or false based on if the target environment natively supports arrows</span>
<span class="pl-c">// or if the plugin that compiles arrow functions is enabled</span>
<span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-en">hasCapability</span><span class="pl-kos">(</span><span class="pl-s">'es2015-arrow-function'</span><span class="pl-kos">)</span><span class="pl-kos"></span> <span class="pl-kos">{</span>
  <span class="pl-c">// output an arrow function</span>
<span class="pl-kos">}</span> <span class="pl-k">else</span> <span class="pl-kos">{</span>
  <span class="pl-c">// output a function expression</span>
<span class="pl-kos">}</span></pre></div>
<h3 dir="auto">Other ideas</h3>
<ul dir="auto">
<li>Allow plugins to have multiple priorities (and a lower priority can error out to signal that a plugin should move before or after)</li>
<li>Explicit ordering of before/after by users (sounds like passPerPreset) (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="175597273" data-permission-text="Title is private" data-url="https://github.com/babel/babel/issues/3793" data-hovercard-type="issue" data-hovercard-url="/babel/babel/issues/3793/hovercard" href="https://github.com/babel/babel/issues/3793">#3793</a>, <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="127366420" data-permission-text="Title is private" data-url="https://github.com/babel/babel/issues/3281" data-hovercard-type="pull_request" data-hovercard-url="/babel/babel/pull/3281/hovercard" href="https://github.com/babel/babel/pull/3281">#3281</a>).</li>
</ul>
<h3 dir="auto">Testing</h3>
<p dir="auto">A test can be to randomly sort the plugins in preset-env/es2015/presets and check if the output is the same. Also tests for specific issues we know about (decorators and class properties)</p>
<h3 dir="auto">Other Benefits</h3>
<ul dir="auto">
<li>Users shouldn't need to care about what order plugins are specified</li>
<li>This would also provide better error messages: "your native environment doesn't support this syntax and you haven't added the correct plugin to transform it: here are some options and how to install"</li>
<li>Minify can also take advantage of this by having multiple outputs based on the target environment (output arrow functions (ES6) if available)</li>
</ul>