Repository: intellij-rust/intellij-rust
Issue: False positive "Variable is never used"


## Environment

* **IntelliJ Rust plugin version:** 0.2.116.2163-193
* **Rust toolchain version:** Rust 1.41.0
* **IDE name and version:** CLion 2019.3.4
* **Operating system:** OSX

## Problem description
False positive "Variable is never used" warning with declarative macros.
## Steps to reproduce
The IDE will give the warning and grey out `bar` in the following code:
```rust
fn foo() -> TokenStream {
    let bar = "";
    quote!(bar)
}
```



Changed files:
src/main/kotlin/org/rust/lang/core/psi/RsWithMacroExpansionsRecursiveElementWalkingVisitor.kt
src/test/kotlin/org/rust/ide/inspections/RsLivenessInspectionTest.kt
src/main/kotlin/org/rust/openapiext/psi.kt

diff --git a/src/main/kotlin/org/rust/lang/core/psi/RsWithMacroExpansionsRecursiveElementWalkingVisitor.kt b/src/main/kotlin/org/rust/lang/core/psi/RsWithMacroExpansionsRecursiveElementWalkingVisitor.kt
deleted file mode 100644
index 42338c9a7..000000000
--- a/src/main/kotlin/org/rust/lang/core/psi/RsWithMacroExpansionsRecursiveElementWalkingVisitor.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Use of this source code is governed by the MIT license that can be
- * found in the LICENSE file.
- */
-
-package org.rust.lang.core.psi
-
-import com.intellij.psi.PsiElement
-import com.intellij.psi.PsiRecursiveElementWalkingVisitor
-import org.rust.lang.core.psi.ext.expansion
-
-abstract class RsWithMacroExpansionsRecursiveElementWalkingVisitor : PsiRecursiveElementWalkingVisitor() {
-    override fun visitElement(element: PsiElement) {
-        if (element is RsMacroCall && element.macroArgument != null) {
-            val expansion = element.expansion ?: return
-            for (expandedElement in expansion.elements) {
-                visitElement(expandedElement)
-            }
-        } else {
-            super.visitElement(element)
-        }
-    }
-}
diff --git a/src/main/kotlin/org/rust/openapiext/psi.kt b/src/main/kotlin/org/rust/openapiext/psi.kt
index f0f02afb2..fac051d7c 100644
--- a/src/main/kotlin/org/rust/openapiext/psi.kt
+++ b/src/main/kotlin/org/rust/openapiext/psi.kt
@@ -7,13 +7,14 @@ package org.rust.openapiext
 
 import com.intellij.psi.PsiCompiledElement
 import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiRecursiveElementWalkingVisitor
 import com.intellij.psi.impl.source.tree.CompositeElement
 import com.intellij.psi.search.PsiElementProcessor
 import com.intellij.psi.util.PsiTreeUtil
 import com.intellij.util.containers.ContainerUtil
 import org.rust.lang.core.psi.RsMacroCall
-import org.rust.lang.core.psi.RsWithMacroExpansionsRecursiveElementWalkingVisitor
 import org.rust.lang.core.psi.ext.expansion
+import org.rust.lang.core.resolve.DEFAULT_RECURSION_LIMIT
 
 
 /**
@@ -30,7 +31,7 @@ inline fun PsiElement.forEachChild(action: (PsiElement) -> Unit) {
     }
 }
 
-/** Behaves like [PsiTreeUtil.findChildOfAnyType], but also collects elements expanded from macros */
+/** Behaves like [PsiTreeUtil.findChildrenOfAnyType], but also collects elements expanded from macros */
 fun <T : PsiElement> findDescendantsWithMacrosOfAnyType(
     element: PsiElement?,
     strict: Boolean,
@@ -68,17 +69,41 @@ fun processElementsWithMacros(element: PsiElement, processor: PsiElementProcesso
         return true
     }
 
-    var result = true
-    element.accept(object : RsWithMacroExpansionsRecursiveElementWalkingVisitor() {
-        override fun visitElement(element: PsiElement) {
-            if (processor.execute(element)) {
-                super.visitElement(element)
+    val visitor = RsWithMacrosRecursiveElementWalkingVisitor(processor)
+    element.accept(visitor)
+
+    return visitor.result
+}
+
+private class RsWithMacrosRecursiveElementWalkingVisitor(
+    private val processor: PsiElementProcessor<PsiElement>,
+    private val nestedMacroDepth: Int = 0
+) : PsiRecursiveElementWalkingVisitor() {
+
+    var result: Boolean = true
+        private set
+
+    override fun visitElement(element: PsiElement) {
+        if (processor.execute(element)) {
+            if (element is RsMacroCall && element.macroArgument != null) {
+                processMacro(element)
             } else {
-                stopWalking()
-                result = false
+                super.visitElement(element)
             }
+        } else {
+            stopWalking()
+            result = false
         }
-    })
+    }
 
-    return result
+    private fun processMacro(element: RsMacroCall) {
+        if (nestedMacroDepth > DEFAULT_RECURSION_LIMIT) {
+            return
+        }
+        val expansion = element.expansion ?: return
+        for (expandedElement in expansion.elements) {
+            val visitor = RsWithMacrosRecursiveElementWalkingVisitor(processor, nestedMacroDepth + 1)
+            expandedElement.accept(visitor)
+        }
+    }
 }
diff --git a/src/test/kotlin/org/rust/ide/inspections/RsLivenessInspectionTest.kt b/src/test/kotlin/org/rust/ide/inspections/RsLivenessInspectionTest.kt
index eea86deb9..b31643b35 100644
--- a/src/test/kotlin/org/rust/ide/inspections/RsLivenessInspectionTest.kt
+++ b/src/test/kotlin/org/rust/ide/inspections/RsLivenessInspectionTest.kt
@@ -484,4 +484,14 @@ class RsLivenessInspectionTest : RsInspectionsTestBase(RsLivenessInspection::cla
         }
     """)
     }
+
+    fun `test unresolved nested macro`() = checkByText("""
+        macro_rules! my_macro {
+            ($ e:expr) => { unresolved!(e) };
+        }
+        
+        fn foo(x: i32) {
+            my_macro!(x);
+        }
+    """)
 }