Repository: intellij-rust/intellij-rust
Issue: Multiple implementations for extern crate + use in 2018 edition

<!--
Hello and thank you for the issue!
If you would like to report a bug, we have added some points below that you can fill out.
Feel free to remove all the irrelevant text to request a new feature.
-->

## Environment

* **Intellij-Rust plugin version:** 0.2.0.SNAPSHOT-182
* **Rust toolchain version:** rustc 1.30.0-nightly (887690686 2018-09-27)
* **IDE name and version:** IntelliJ IDEA Community 2018.2
* **Operating system:** OSX High Sierra

## Problem description

When using Rust in 2018 edition mode, `extern crate` declarations conflict with `use` declarations for the same modules. For example:

```rust
extern crate hex;

use hex::encode;

fn main() {
    let d = encode(vec![1u8, 2u8]);
    println!("Result: {:?}", &d);
}
```

The IDE would not be able to resolve the `encode()` call in main(), with tooltip "Unresolved reference: 'encode'".

This seems to be a similar problem to #2877: the IDE reports "Multiple implementations" for the `hex` module. I built the branch `fix-std-crate-resolution` and it fixes the issue for `use std::` patterns, but not for this case.

The obvious workaround is removing the `extern crate` declaration which is no longer necessary in Rust 2018, but this makes it awkward to switch between editions. Furthermore, having the `extern crate` declaration is correct, even if not required; the IDE should probably accept it.

## Steps to reproduce

<!--
Please include as much of your codebase as needed to reproduce the error.
If the relevant files are large, please provide a link to a public repository or a [Gist](https://gist.github.com/).
-->

Open the provided module in the IDE with `edition = "2018"` enabled in Cargo.toml.

The module compiles fine, but the IDE will not be able to resolve `encode`.


Changed files:
src/main/kotlin/org/rust/lang/core/resolve/ItemResolution.kt, src/test/kotlin/org/rust/lang/core/resolve/RsPackageLibraryResolveTest.kt

diff --git a/src/main/kotlin/org/rust/lang/core/resolve/ItemResolution.kt b/src/main/kotlin/org/rust/lang/core/resolve/ItemResolution.kt
index 772b4f2b7..1806fe9db 100644
--- a/src/main/kotlin/org/rust/lang/core/resolve/ItemResolution.kt
+++ b/src/main/kotlin/org/rust/lang/core/resolve/ItemResolution.kt
@@ -5,11 +5,15 @@
 
 package org.rust.lang.core.resolve
 
+import org.rust.cargo.project.workspace.CargoWorkspace.Edition.EDITION_2018
 import org.rust.cargo.util.AutoInjectedCrates.CORE
 import org.rust.cargo.util.AutoInjectedCrates.STD
 import org.rust.lang.core.psi.*
 import org.rust.lang.core.psi.ext.*
+import org.rust.lang.core.resolve.ItemResolutionTestmarks.externCrateItemWithoutAlias
+import org.rust.lang.core.resolve.ItemResolutionTestmarks.externCrateItemAliasWithSameName
 import org.rust.lang.core.resolve.ref.RsReference
+import org.rust.openapiext.Testmark
 import java.util.*
 
 fun processItemOrEnumVariantDeclarations(
@@ -56,7 +60,7 @@ fun processItemDeclarations(
                     }
                 }
 
-        // Unit like structs are both types and values
+            // Unit like structs are both types and values
             is RsStructItem ->
                 if (item.namespaces.intersect(ns).isNotEmpty() && processor(item)) return true
 
@@ -77,7 +81,24 @@ fun processItemDeclarations(
 
             is RsExternCrateItem -> {
                 if (item.isPublic || withPrivateImports) {
-                    val name = item.alias?.name ?: item.name ?: return false
+                    val itemName = item.name
+                    val aliasName = item.alias?.name
+                    val name = aliasName ?: itemName ?: return false
+                    val edition = item.containingCargoTarget?.edition
+
+                    if (edition == EDITION_2018) {
+                        // For edition 2018 we should process only extern crate item
+                        // which brings new name into the scope, i.e. with alias,
+                        // because otherwise this item is already processed in `NameResolutionKt#processPathResolveVariants`
+                        if (aliasName == null) {
+                            externCrateItemWithoutAlias.hit()
+                            return false
+                        }
+                        if (aliasName == itemName) {
+                            externCrateItemAliasWithSameName.hit()
+                            return false
+                        }
+                    }
                     val mod = item.reference.resolve() ?: return false
                     if (processor(name, mod)) return true
                 }
@@ -173,3 +194,8 @@ private fun processMultiResolveWithNs(name: String, ns: Set<Namespace>, ref: RsR
     }
     return false
 }
+
+object ItemResolutionTestmarks {
+    val externCrateItemWithoutAlias = Testmark("externCrateItemWithoutAlias")
+    val externCrateItemAliasWithSameName = Testmark("externCrateItemAliasWithSameName")
+}
diff --git a/src/test/kotlin/org/rust/lang/core/resolve/RsPackageLibraryResolveTest.kt b/src/test/kotlin/org/rust/lang/core/resolve/RsPackageLibraryResolveTest.kt
index 9ee94c06f..5c7252914 100644
--- a/src/test/kotlin/org/rust/lang/core/resolve/RsPackageLibraryResolveTest.kt
+++ b/src/test/kotlin/org/rust/lang/core/resolve/RsPackageLibraryResolveTest.kt
@@ -263,4 +263,48 @@ class RsPackageLibraryResolveTest : RsResolveTestBase() {
         fn foo() -> dep_lib_target::Foo { unimplemented!() }
                                    //^ dep-lib/lib.rs
     """)
+
+    @MockEdition(CargoWorkspace.Edition.EDITION_2018)
+    fun `test extern crate item (edition 2018)`() = stubOnlyResolve("""
+    //- dep-lib/lib.rs
+        pub struct Foo;
+    //- lib.rs
+        extern crate dep_lib_target;
+
+        fn foo() -> dep_lib_target::Foo { unimplemented!() }
+                                   //^ dep-lib/lib.rs
+    """, ItemResolutionTestmarks.externCrateItemWithoutAlias)
+
+    @MockEdition(CargoWorkspace.Edition.EDITION_2018)
+    fun `test extern crate item alias 1 (edition 2018)`() = stubOnlyResolve("""
+    //- dep-lib/lib.rs
+        pub struct Foo;
+    //- lib.rs
+        extern crate dep_lib_target as dep_lib;
+
+        fn foo() -> dep_lib::Foo { unimplemented!() }
+                            //^ dep-lib/lib.rs
+    """)
+
+    @MockEdition(CargoWorkspace.Edition.EDITION_2018)
+    fun `test extern crate item alias 2 (edition 2018)`() = stubOnlyResolve("""
+    //- dep-lib/lib.rs
+        pub struct Foo;
+    //- lib.rs
+        extern crate dep_lib_target as dep_lib;
+
+        fn foo() -> dep_lib_target::Foo { unimplemented!() }
+                                   //^ dep-lib/lib.rs
+    """)
+
+    @MockEdition(CargoWorkspace.Edition.EDITION_2018)
+    fun `test extern crate item alias with same name (edition 2018)`() = stubOnlyResolve("""
+    //- dep-lib/lib.rs
+        pub struct Foo;
+    //- lib.rs
+        extern crate dep_lib_target as dep_lib_target;
+
+        fn foo() -> dep_lib_target::Foo { unimplemented!() }
+                                   //^ dep-lib/lib.rs
+    """, ItemResolutionTestmarks.externCrateItemAliasWithSameName)
 }