Repository: intellij-rust/intellij-rust
Issue: Exception `The file content has already been fetched java.io.FileNotFoundException`

<!--
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.4.175.SNAPSHOT-221-dev
* **Rust toolchain version:** null
* **IDE name and version:** IntelliJ IDEA 2022.1.3 Ultimate Edition (IU-221.5921.22)
* **Operating system:** Linux 5.15.0-40-generic
* **Macro expansion engine:** new
* **Name resolution engine:** new
* **Additional experimental features:** org.rust.cargo.emulate.terminal, org.rust.cargo.evaluate.build.scripts, org.rust.macros.proc

## Problem description

```
The file content has already been fetched
java.io.FileNotFoundException: /rust_expanded_macros/e3c3swxg/45841/5/3/531b4eb17b8d13185e98215a7f31d6e4759a50a5_1_24.rs (Content is not provided)
	at org.rust.lang.core.macros.MacroExpansionFileSystem.contentsToByteArray(MacroExpansionFileSystem.kt:222)
	at com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl.contentsToByteArray(PersistentFSImpl.java:609)
	at com.intellij.openapi.vfs.newvfs.impl.VirtualFileImpl.contentsToByteArray(VirtualFileImpl.java:126)
...
```

## Steps to reproduce

If you're a programmer:
1. Run the IDE, await indexing and macro expansion to complete, close IDE
2. Bump both `ProcMacroExpander.EXPANDER_VERSION` and `RsFileStub.STUB_VERSION` 
3. Run the IDE again, see exceptions in the log

If not:
1. Install some old plugin version, like 160
2. Run the IDE, await indexing and macro expansion to complete, close IDE
3. Update the plugin to 174
3. Run the IDE again, see exceptions in the log

Changed files:
src/main/kotlin/org/rust/lang/core/macros/MacroExpansionTask.kt, src/main/kotlin/org/rust/lang/core/macros/ExpandedMacroStorage.kt, src/main/kotlin/org/rust/lang/core/macros/MacroExpansionFileSystem.kt, src/main/kotlin/org/rust/lang/core/macros/MacroExpansionStubsProvider.kt

diff --git a/src/main/kotlin/org/rust/lang/core/macros/ExpandedMacroStorage.kt b/src/main/kotlin/org/rust/lang/core/macros/ExpandedMacroStorage.kt
index ca440214d..0d699e55a 100644
--- a/src/main/kotlin/org/rust/lang/core/macros/ExpandedMacroStorage.kt
+++ b/src/main/kotlin/org/rust/lang/core/macros/ExpandedMacroStorage.kt
@@ -55,9 +55,17 @@ fun VirtualFile.loadRangeMap(): RangeMap? {
     return ranges
 }
 
-fun VirtualFile.loadMixHash(): HashCode? {
+/** The second part is a stored [MACRO_STORAGE_VERSION] value */
+fun VirtualFile.extractMixHashAndMacroStorageVersion(): Pair<HashCode, Int>? {
     val name = name
-    val underscoreIndex = name.indexOf('_')
-    if (underscoreIndex == -1) return null
-    return HashCode.fromHexString(name.substring(0, underscoreIndex))
+    val firstUnderscoreIndex = name.indexOf('_')
+    if (firstUnderscoreIndex == -1) return null
+    val lastUnderscoreIndex = name.indexOf('_', firstUnderscoreIndex + 1)
+    if (lastUnderscoreIndex == -1) return null
+    val mixHash = HashCode.fromHexString(name.substring(0, firstUnderscoreIndex))
+    val version = name.substring(
+        lastUnderscoreIndex + 1,
+        name.length - 3 // ".rs".length
+    ).toIntOrNull() ?: -1
+    return mixHash to version
 }
diff --git a/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionFileSystem.kt b/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionFileSystem.kt
index f9e5a11f5..5183ec4b8 100644
--- a/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionFileSystem.kt
+++ b/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionFileSystem.kt
@@ -207,16 +207,21 @@ class MacroExpansionFileSystem : NewVirtualFileSystem() {
 
     override fun setWritable(file: VirtualFile, writableFlag: Boolean) {}
 
+    @Suppress("IfThenToElvis")
     @Throws(IOException::class)
     override fun contentsToByteArray(file: VirtualFile): ByteArray {
         val fsItem = convert(file) ?: throw FileNotFoundException(file.path + " (No such file or directory)")
         if (fsItem !is FSFile) throw FileNotFoundException(file.path + " (Is a directory)")
         return fsItem.fetchAndRemoveContent() ?: run {
-            val cachedExpansion = file.loadMixHash()?.let {
-                MacroExpansionSharedCache.getInstance().getExpansionIfCached(it)?.ok()
-            }
+            val (mixHash, storedVersion) = file.extractMixHashAndMacroStorageVersion() ?: (null to -1)
+            val cachedExpansion = mixHash
+                ?.takeIf { storedVersion == MACRO_STORAGE_VERSION }
+                ?.let { MacroExpansionSharedCache.getInstance().getExpansionIfCached(it)?.ok() }
             if (cachedExpansion != null) {
                 cachedExpansion.text.toByteArray()
+            } else if (storedVersion != MACRO_STORAGE_VERSION) {
+                // Found old version -> the file will be deleted by `MacroExpansionTask`
+                ArrayUtil.EMPTY_BYTE_ARRAY
             } else {
                 fsItem.delete()
                 val e = FileNotFoundException(file.path + " (Content is not provided)")
diff --git a/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionStubsProvider.kt b/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionStubsProvider.kt
index 72c29e489..9a24d5adb 100644
--- a/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionStubsProvider.kt
+++ b/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionStubsProvider.kt
@@ -20,7 +20,10 @@ class MacroExpansionStubsProvider : PrebuiltStubsProvider {
         val file = fileContent.file
         if (!MacroExpansionManager.isExpansionFile(file)) return null
         if (!MacroExpansionSharedCache.getInstance().isEnabled) return null
-        val hash = file.loadMixHash() ?: return null
+        val hash = file.extractMixHashAndMacroStorageVersion()
+            ?.takeIf { it.second == MACRO_STORAGE_VERSION }
+            ?.first
+            ?: return null
         return MacroExpansionSharedCache.getInstance().cachedBuildStub(fileContent, hash)
     }
 }
diff --git a/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionTask.kt b/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionTask.kt
index 7e3d6f9d0..835f3c568 100644
--- a/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionTask.kt
+++ b/src/main/kotlin/org/rust/lang/core/macros/MacroExpansionTask.kt
@@ -46,7 +46,7 @@ import java.io.IOException
  * List of methods that rely on such path format:
  * - [DefCollector.recordExpansionFileName]
  * - [expansionNameToPath]
- * - [VirtualFile.loadMixHash]
+ * - [VirtualFile.extractMixHashAndMacroStorageVersion]
  * - [MacroExpansionServiceImplInner.getDefMapForExpansionFile]
  * - [MacroExpansionTask.collectExistingFiles]
  */