Repository: intellij-rust/intellij-rust
Issue: Invalid `Elide Lifetimes` suggestion

## Environment

* **Intellij-Rust plugin version:** 0.2.0.2104-182
* **Rust toolchain version:** stable rustc 1.29.0 (aa3ca1994 2018-09-11)
* **IDE name and version:**
    IntelliJ IDEA 2018.2.2 (Ultimate Edition)
    Build #IU-182.4129.33, built on August 21, 2018
    JRE: 1.8.0_152-release-1248-b8 amd64
    JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
    Windows 10 10.0

## Problem description

```rust
fn f<'a>(_: impl IntoIterator<Item = &'a ()>) {}
```

Suggestion provided "Explicit lifetimes given in parameter types where they could be elided",
but applying the suggestion results in incorrect code (written code is correct).
The culprit is that the lifetime in the generic arguments to the `impl Trait` cannot be elided.


Changed files:
src/test/kotlin/org/rust/ide/inspections/RsNeedlessLifetimesInspectionTest.kt, src/main/kotlin/org/rust/ide/inspections/RsNeedlessLifetimesInspection.kt

diff --git a/src/main/kotlin/org/rust/ide/inspections/RsNeedlessLifetimesInspection.kt b/src/main/kotlin/org/rust/ide/inspections/RsNeedlessLifetimesInspection.kt
index 123c4500a..17766f758 100644
--- a/src/main/kotlin/org/rust/ide/inspections/RsNeedlessLifetimesInspection.kt
+++ b/src/main/kotlin/org/rust/ide/inspections/RsNeedlessLifetimesInspection.kt
@@ -12,6 +12,10 @@ import org.rust.ide.inspections.ReferenceLifetime.*
 import org.rust.ide.inspections.fixes.ElideLifetimesFix
 import org.rust.lang.core.psi.*
 import org.rust.lang.core.psi.ext.*
+import org.rust.lang.core.types.regions.ReEarlyBound
+import org.rust.lang.core.types.regions.ReStatic
+import org.rust.lang.core.types.ty.TyTraitObject
+import org.rust.lang.core.types.type
 import org.rust.openapiext.forEachChild
 import org.rust.stdext.chain
 
@@ -76,24 +80,40 @@ private fun couldUseElision(fn: RsFunction): Boolean {
     }
 }
 
-private class LifetimesCollector : RsVisitor() {
+private class LifetimesCollector(val isForInputParams: Boolean = false) : RsVisitor() {
     var abort: Boolean = false
     val lifetimes = mutableListOf<ReferenceLifetime>()
 
     override fun visitSelfParameter(selfParameter: RsSelfParameter) {
         selfParameter.lifetime?.let { visitLifetime(it) }
-        if (selfParameter.isRef && selfParameter.lifetime.isElided) record(null)
+        if (selfParameter.isRef && selfParameter.lifetime.isElided) {
+            record(null)
+        }
         selfParameter.typeReference?.let { visitTypeReference(it) }
     }
 
     override fun visitRefLikeType(refLike: RsRefLikeType) {
-        if (refLike.isRef && refLike.lifetime.isElided) record(null)
+        if (refLike.isRef && refLike.lifetime.isElided) {
+            record(null)
+        }
         super.visitRefLikeType(refLike)
     }
 
+    override fun visitTypeReference(ref: RsTypeReference) {
+        val type = ref.type
+        if (type is TyTraitObject && (type.region is ReEarlyBound || type.region is ReStatic)) {
+            abort = true
+        }
+        super.visitTypeReference(ref)
+    }
+
     override fun visitTraitType(trait: RsTraitType) {
-        val lifetimeBounds = trait.polyboundList.mapNotNull { it.bound.lifetime }
-        if (lifetimeBounds.isNotEmpty()) abort = true
+        for (polybound in trait.polyboundList) {
+            polybound.bound.lifetime?.let { record(null) }
+            if (isForInputParams) {
+                abort = abort || hasNamedReferenceLifetime(polybound.bound)
+            }
+        }
         super.visitTraitType(trait)
     }
 
@@ -114,13 +134,14 @@ private class LifetimesCollector : RsVisitor() {
     }
 
     private fun collectAnonymousLifetimes(path: RsPath) {
-        if (path.typeArgumentList != null) return
+        if (path.typeArgumentList?.lifetimeList.orEmpty().isNotEmpty()) return
         val resolved = path.reference.resolve()
         when (resolved) {
             is RsStructItem, is RsTraitItem, is RsTypeAlias -> {
                 val declaration = resolved as RsGenericDeclaration
-                val genericsCount = declaration.lifetimeParameters.size + declaration.typeParameters.size
-                repeat(genericsCount) { record(null) }
+                repeat(declaration.lifetimeParameters.size) {
+                    record(null)
+                }
             }
         }
     }
@@ -144,7 +165,7 @@ private class BodyLifetimeChecker : RsVisitor() {
 
 private fun collectLifetimesFromFnSignature(fn: RsFunction): Pair<List<ReferenceLifetime>, List<ReferenceLifetime>>? {
     // these will collect all the lifetimes for references in arg/return types
-    val inputCollector = LifetimesCollector()
+    val inputCollector = LifetimesCollector(true)
     val outputCollector = LifetimesCollector()
 
     // extract lifetimes in input argument types
diff --git a/src/test/kotlin/org/rust/ide/inspections/RsNeedlessLifetimesInspectionTest.kt b/src/test/kotlin/org/rust/ide/inspections/RsNeedlessLifetimesInspectionTest.kt
index 4fc6befb6..be443cc7e 100644
--- a/src/test/kotlin/org/rust/ide/inspections/RsNeedlessLifetimesInspectionTest.kt
+++ b/src/test/kotlin/org/rust/ide/inspections/RsNeedlessLifetimesInspectionTest.kt
@@ -6,6 +6,8 @@
 package org.rust.ide.inspections
 
 import org.intellij.lang.annotations.Language
+import org.rust.ProjectDescriptor
+import org.rust.WithStdlibRustProjectDescriptor
 
 class RsNeedlessLifetimesInspectionTest : RsInspectionsTestBase(RsNeedlessLifetimesInspection()) {
 
@@ -281,6 +283,22 @@ class RsNeedlessLifetimesInspectionTest : RsInspectionsTestBase(RsNeedlessLifeti
         }
     """)
 
+    @ProjectDescriptor(WithStdlibRustProjectDescriptor::class)
+    fun `test no elision for impl 1`() = doTest("""
+        fn foo<'a>(_: impl Into<&'a ()>) {}
+    """)
+
+    @ProjectDescriptor(WithStdlibRustProjectDescriptor::class)
+    fun `test no elision for impl 2`() = doTest("""
+        struct S<'a>(&'a str);
+        fn _foo<'a>(_: impl Into<S<'a>>) {}
+    """)
+
+    @ProjectDescriptor(WithStdlibRustProjectDescriptor::class)
+    fun `test no elision for impl 3`() = doTest("""
+        fn foo<'a>(_: impl Iterator<Item = &'a ()>) {}
+    """)
+
     private fun doTest(
         @Language("Rust") text: String
     ) = checkFixIsUnavailable(FIX_NAME, text, checkWeakWarn = true)