Repository: rust-analyzer/rust-analyzer
Pull request #10944 (2021-12-05 15:26:53)

internal: Prefer resolution of inert attributes
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/10942
bors r+


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 8a0d809a1b..df01cd74fe 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -525,6 +525,7 @@ dependencies = [
  "cfg",
  "cov-mark",
  "either",
+ "expect-test",
  "itertools",
  "la-arena",
  "limit",
@@ -638,10 +639,12 @@ dependencies = [
  "profile",
  "rustc-hash",
  "smallvec",
+ "sourcegen",
  "stdx",
  "syntax",
  "test_utils",
  "text_edit",
+ "xshell",
 ]
 
 [[package]]
@@ -899,6 +902,7 @@ name = "mbe"
 version = "0.0.0"
 dependencies = [
  "cov-mark",
+ "expect-test",
  "parser",
  "rustc-hash",
  "smallvec",
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 762f172c2a..880596b37c 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -335,17 +335,16 @@ impl SourceAnalyzer {
                 }
             }
         } else if is_path_of_attr {
-            let res = resolve_hir_path_as_macro(db, &self.resolver, &hir_path);
-            return match res {
-                Some(_) => res.map(PathResolution::Macro),
-                None => path.as_single_name_ref().and_then(|name_ref| {
-                    if let builtin @ Some(_) = BuiltinAttr::by_name(&name_ref.text()) {
-                        builtin.map(PathResolution::BuiltinAttr)
-                    } else if let tool @ Some(_) = ToolModule::by_name(&name_ref.text()) {
-                        tool.map(PathResolution::ToolModule)
-                    } else {
-                        None
-                    }
+            let name_ref = path.as_single_name_ref();
+            let builtin =
+                name_ref.as_ref().map(ast::NameRef::text).as_deref().and_then(BuiltinAttr::by_name);
+            if let builtin @ Some(_) = builtin {
+                return builtin.map(PathResolution::BuiltinAttr);
+            }
+            return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) {
+                res @ Some(m) if m.is_attr() => res.map(PathResolution::Macro),
+                _ => name_ref.and_then(|name_ref| {
+                    ToolModule::by_name(&name_ref.text()).map(PathResolution::ToolModule)
                 }),
             };
         }
diff --git a/crates/hir_expand/Cargo.toml b/crates/hir_expand/Cargo.toml
index 380b7ef877..f17e1e857b 100644
--- a/crates/hir_expand/Cargo.toml
+++ b/crates/hir_expand/Cargo.toml
@@ -24,3 +24,6 @@ profile = { path = "../profile", version = "0.0.0" }
 tt = { path = "../tt", version = "0.0.0" }
 mbe = { path = "../mbe", version = "0.0.0" }
 limit = { path = "../limit", version = "0.0.0" }
+
+[dev-dependencies]
+expect-test = "1.2.0-pre.1"
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml
index f9ce03bce2..8ade88fdd1 100644
--- a/crates/ide_completion/Cargo.toml
+++ b/crates/ide_completion/Cargo.toml
@@ -30,5 +30,7 @@ hir = { path = "../hir", version = "0.0.0" }
 
 [dev-dependencies]
 expect-test = "1.2.0-pre.1"
+xshell = "0.1"
 
 test_utils = { path = "../test_utils" }
+sourcegen = { path = "../sourcegen" }
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml
index 651ca409c6..cff7224e02 100644
--- a/crates/mbe/Cargo.toml
+++ b/crates/mbe/Cargo.toml
@@ -13,6 +13,7 @@ doctest = false
 cov-mark = "2.0.0-pre.1"
 rustc-hash = "1.1.0"
 smallvec = "1.2.0"
+expect-test = "1.2.0-pre.1"
 tracing = "0.1"
 
 syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 0863c40716..4ad91e8da8 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -22,6 +22,7 @@ anyhow = "1.0.26"
 crossbeam-channel = "0.5.0"
 dissimilar = "1.0.2"
 itertools = "0.10.0"
+jod-thread = "0.1.0"
 lsp-types = { version = "0.91", features = ["proposed"] }
 parking_lot = "0.11.0"
 xflags = "0.2.1"
@@ -73,7 +74,6 @@ jemallocator = { version = "0.4.1", package = "tikv-jemallocator", optional = tr
 
 [dev-dependencies]
 expect-test = "1.2.0-pre.1"
-jod-thread = "0.1.0"
 xshell = "0.1"
 
 test_utils = { path = "../test_utils" }
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 56b3afce98..2f63c26ce5 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -29,8 +29,7 @@ pub(crate) struct DiagnosticCollection {
 
 #[derive(Debug, Clone)]
 pub(crate) struct Fix {
-    // Fixes may be triggerable from multiple ranges.
-    pub(crate) ranges: Vec<lsp_types::Range>,
+    pub(crate) range: lsp_types::Range,
     pub(crate) action: lsp_ext::CodeAction,
 }
 
@@ -44,7 +43,7 @@ impl DiagnosticCollection {
         &mut self,
         file_id: FileId,
         diagnostic: lsp_types::Diagnostic,
-        fix: Option<Fix>,
+        fixes: Vec<lsp_ext::CodeAction>,
     ) {
         let diagnostics = self.check.entry(file_id).or_default();
         for existing_diagnostic in diagnostics.iter() {
@@ -54,7 +53,10 @@ impl DiagnosticCollection {
         }
 
         let check_fixes = Arc::make_mut(&mut self.check_fixes);
-        check_fixes.entry(file_id).or_default().extend(fix);
+        check_fixes
+            .entry(file_id)
+            .or_default()
+            .extend(fixes.into_iter().map(|action| Fix { range: diagnostic.range, action }));
         diagnostics.push(diagnostic);
         self.changes.insert(file_id);
     }
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 5f936ee086..bdc56b3196 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -9,7 +9,7 @@ use vfs::{AbsPath, AbsPathBuf};
 
 use crate::{lsp_ext, to_proto::url_from_abs_path};
 
-use super::{DiagnosticsMapConfig, Fix};
+use super::DiagnosticsMapConfig;
 
 /// Determines the LSP severity from a diagnostic
 fn diagnostic_severity(
@@ -124,7 +124,7 @@ fn resolve_path(
 
 struct SubDiagnostic {
     related: lsp_types::DiagnosticRelatedInformation,
-    suggested_fix: Option<Fix>,
+    suggested_fix: Option<lsp_ext::CodeAction>,
 }
 
 enum MappedRustChildDiagnostic {
@@ -181,24 +181,18 @@ fn map_rust_child_diagnostic(
                 location: location(config, workspace_root, spans[0]),
                 message: message.clone(),
             },
-            suggested_fix: Some(Fix {
-                ranges: spans
-                    .iter()
-                    .map(|&span| location(config, workspace_root, span).range)
-                    .collect(),
-                action: lsp_ext::CodeAction {
-                    title: message,
-                    group: None,
-                    kind: Some(lsp_types::CodeActionKind::QUICKFIX),
-                    edit: Some(lsp_ext::SnippetWorkspaceEdit {
-                        // FIXME: there's no good reason to use edit_map here....
-                        changes: Some(edit_map),
-                        document_changes: None,
-                        change_annotations: None,
-                    }),
-                    is_preferred: Some(true),
-                    data: None,
-                },
+            suggested_fix: Some(lsp_ext::CodeAction {
+                title: message,
+                group: None,
+                kind: Some(lsp_types::CodeActionKind::QUICKFIX),
+                edit: Some(lsp_ext::SnippetWorkspaceEdit {
+                    // FIXME: there's no good reason to use edit_map here....
+                    changes: Some(edit_map),
+                    document_changes: None,
+                    change_annotations: None,
+                }),
+                is_preferred: Some(true),
+                data: None,
             }),
         })
     }
@@ -208,7 +202,7 @@ fn map_rust_child_diagnostic(
 pub(crate) struct MappedRustDiagnostic {
     pub(crate) url: lsp_types::Url,
     pub(crate) diagnostic: lsp_types::Diagnostic,
-    pub(crate) fix: Option<Fix>,
+    pub(crate) fixes: Vec<lsp_ext::CodeAction>,
 }
 
 /// Converts a Rust root diagnostic to LSP form
@@ -365,7 +359,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
                 diagnostics.push(MappedRustDiagnostic {
                     url: secondary_location.uri,
                     diagnostic,
-                    fix: None,
+                    fixes: Vec::new(),
                 });
             }
 
@@ -394,7 +388,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
                     tags: if tags.is_empty() { None } else { Some(tags.clone()) },
                     data: None,
                 },
-                fix: None,
+                fixes: Vec::new(),
             });
 
             // Emit hint-level diagnostics for all `related_information` entries such as "help"s.
@@ -411,7 +405,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
                 }
                 diagnostics.push(MappedRustDiagnostic {
                     url: sub.related.location.uri.clone(),
-                    fix: sub.suggested_fix.clone(),
+                    fixes: sub.suggested_fix.iter().cloned().collect(),
                     diagnostic: lsp_types::Diagnostic {
                         range: sub.related.location.range,
                         severity: Some(lsp_types::DiagnosticSeverity::HINT),
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index bf153012d9..0ddddeca67 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -1081,13 +1081,8 @@ pub(crate) fn handle_code_action(
     for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() {
         // FIXME: this mapping is awkward and shouldn't exist. Refactor
         // `snap.check_fixes` to not convert to LSP prematurely.
-        let intersect_fix_range = fix
-            .ranges
-            .iter()
-            .copied()
-            .map(|range| from_proto::text_range(&line_index, range))
-            .any(|fix_range| fix_range.intersect(frange.range).is_some());
-        if intersect_fix_range {
+        let fix_range = from_proto::text_range(&line_index, fix.range);
+        if fix_range.intersect(frange.range).is_some() {
             res.push(fix.action.clone());
         }
     }
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 2d83cbee66..979b3b1522 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -370,7 +370,7 @@ impl GlobalState {
                                     Ok(file_id) => self.diagnostics.add_check_diagnostic(
                                         file_id,
                                         diag.diagnostic,
-                                        diag.fix,
+                                        diag.fixes,
                                     ),
                                     Err(err) => {
                                         tracing::error!(
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 686844132c..e260062167 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -9,7 +9,7 @@
             "version": "0.4.0-dev",
             "license": "MIT OR Apache-2.0",
             "dependencies": {
-                "d3": "^7.2.0",
+                "d3": "^7.1.0",
                 "d3-graphviz": "^4.0.0",
                 "https-proxy-agent": "^5.0.0",
                 "node-fetch": "^3.0.3",
@@ -792,9 +792,9 @@
             }
         },
         "node_modules/d3": {
-            "version": "7.2.0",
-            "resolved": "https://registry.npmjs.org/d3/-/d3-7.2.0.tgz",
-            "integrity": "sha512-0bh5lPzD6R/I/SjkcgyQlMaIsrZDgh40yMkdJaiZ/58YEbh4zz5EvtjaudzmI6ruwQNvxGm00trYUyPa992NSQ==",
+            "version": "7.1.1",
+            "resolved": "https://registry.npmjs.org/d3/-/d3-7.1.1.tgz",
+            "integrity": "sha512-8zkLMwSvUAnfN9pcJDfkuxU0Nvg4RLUD0A4BZN1KxJPtlnCGzMx3xM5cRl4m8fym/Vy8rlq52tl90UF3m91OnA==",
             "dependencies": {
                 "d3-array": "3",
                 "d3-axis": "3",
@@ -4503,9 +4503,9 @@
             "dev": true
         },
         "d3": {
-            "version": "7.2.0",
-            "resolved": "https://registry.npmjs.org/d3/-/d3-7.2.0.tgz",
-            "integrity": "sha512-0bh5lPzD6R/I/SjkcgyQlMaIsrZDgh40yMkdJaiZ/58YEbh4zz5EvtjaudzmI6ruwQNvxGm00trYUyPa992NSQ==",
+            "version": "7.1.1",
+            "resolved": "https://registry.npmjs.org/d3/-/d3-7.1.1.tgz",
+            "integrity": "sha512-8zkLMwSvUAnfN9pcJDfkuxU0Nvg4RLUD0A4BZN1KxJPtlnCGzMx3xM5cRl4m8fym/Vy8rlq52tl90UF3m91OnA==",
             "requires": {
                 "d3-array": "3",
                 "d3-axis": "3",
diff --git a/editors/code/package.json b/editors/code/package.json
index da4fa15b02..26f21f73d4 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -39,7 +39,7 @@
         "https-proxy-agent": "^5.0.0",
         "node-fetch": "^3.0.3",
         "vscode-languageclient": "8.0.0-next.2",
-        "d3": "^7.2.0",
+        "d3": "^7.1.0",
         "d3-graphviz": "^4.0.0"
     },
     "devDependencies": {
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
index bd1bb5f8a8..c295716add 100644
--- a/editors/code/src/net.ts
+++ b/editors/code/src/net.ts
@@ -9,6 +9,7 @@ import * as zlib from "zlib";
 import * as util from "util";
 import * as path from "path";
 import { log, assert } from "./util";
+import * as url from "url";
 import * as https from "https";
 import { ProxySettings } from "./config";
 
@@ -20,7 +21,7 @@ const REPO = "rust-analyzer";
 
 function makeHttpAgent(proxy: string | null | undefined, options?: https.AgentOptions) {
     if (proxy) {
-        return new HttpsProxyAgent({ ...options, ...new URL(proxy) });
+        return new HttpsProxyAgent(proxy, { ...options, ...url.parse(proxy) });
     } else {
         return new https.Agent(options);
     }


Test patch:
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
index 4610984acb..fd04163de9 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
@@ -114,7 +114,7 @@
             tags: None,
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
     MappedRustDiagnostic {
         url: Url {
@@ -205,7 +205,7 @@
             tags: None,
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
     MappedRustDiagnostic {
         url: Url {
@@ -296,69 +296,55 @@
             tags: None,
             data: None,
         },
-        fix: Some(
-            Fix {
-                ranges: [
-                    Range {
-                        start: Position {
-                            line: 41,
-                            character: 23,
-                        },
-                        end: Position {
-                            line: 41,
-                            character: 28,
-                        },
-                    },
-                ],
-                action: CodeAction {
-                    title: "consider passing by value instead: `self`",
-                    group: None,
-                    kind: Some(
-                        CodeActionKind(
-                            "quickfix",
-                        ),
+        fixes: [
+            CodeAction {
+                title: "consider passing by value instead: `self`",
+                group: None,
+                kind: Some(
+                    CodeActionKind(
+                        "quickfix",
                     ),
-                    edit: Some(
-                        SnippetWorkspaceEdit {
-                            changes: Some(
-                                {
-                                    Url {
-                                        scheme: "file",
-                                        cannot_be_a_base: false,
-                                        username: "",
-                                        password: None,
-                                        host: None,
-                                        port: None,
-                                        path: "/test/compiler/mir/tagset.rs",
-                                        query: None,
-                                        fragment: None,
-                                    }: [
-                                        TextEdit {
-                                            range: Range {
-                                                start: Position {
-                                                    line: 41,
-                                                    character: 23,
-                                                },
-                                                end: Position {
-                                                    line: 41,
-                                                    character: 28,
-                                                },
+                ),
+                edit: Some(
+                    SnippetWorkspaceEdit {
+                        changes: Some(
+                            {
+                                Url {
+                                    scheme: "file",
+                                    cannot_be_a_base: false,
+                                    username: "",
+                                    password: None,
+                                    host: None,
+                                    port: None,
+                                    path: "/test/compiler/mir/tagset.rs",
+                                    query: None,
+                                    fragment: None,
+                                }: [
+                                    TextEdit {
+                                        range: Range {
+                                            start: Position {
+                                                line: 41,
+                                                character: 23,
+                                            },
+                                            end: Position {
+                                                line: 41,
+                                                character: 28,
                                             },
-                                            new_text: "self",
                                         },
-                                    ],
-                                },
-                            ),
-                            document_changes: None,
-                            change_annotations: None,
-                        },
-                    ),
-                    is_preferred: Some(
-                        true,
-                    ),
-                    data: None,
-                },
+                                        new_text: "self",
+                                    },
+                                ],
+                            },
+                        ),
+                        document_changes: None,
+                        change_annotations: None,
+                    },
+                ),
+                is_preferred: Some(
+                    true,
+                ),
+                data: None,
             },
-        ),
+        ],
     },
 ]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
index 989e5cf66d..a7f936a703 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
@@ -59,6 +59,6 @@
             tags: None,
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
 ]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
index fe5cf9b3be..8bee4cfe10 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
@@ -64,7 +64,7 @@
             tags: None,
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
     MappedRustDiagnostic {
         url: Url {
@@ -131,7 +131,7 @@
             tags: None,
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
     MappedRustDiagnostic {
         url: Url {
@@ -224,6 +224,6 @@
             tags: None,
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
 ]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
index dc36aa761c..afc562a0e2 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
@@ -59,6 +59,6 @@
             tags: None,
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
 ]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
index d557196c2b..05074a914b 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
@@ -59,6 +59,6 @@
             tags: None,
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
 ]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
index 46d44192c5..3ded70411d 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
@@ -72,7 +72,7 @@
             ),
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
     MappedRustDiagnostic {
         url: Url {
@@ -143,69 +143,55 @@
             tags: None,
             data: None,
         },
-        fix: Some(
-            Fix {
-                ranges: [
-                    Range {
-                        start: Position {
-                            line: 290,
-                            character: 8,
-                        },
-                        end: Position {
-                            line: 290,
-                            character: 11,
-                        },
-                    },
-                ],
-                action: CodeAction {
-                    title: "consider prefixing with an underscore: `_foo`",
-                    group: None,
-                    kind: Some(
-                        CodeActionKind(
-                            "quickfix",
-                        ),
+        fixes: [
+            CodeAction {
+                title: "consider prefixing with an underscore: `_foo`",
+                group: None,
+                kind: Some(
+                    CodeActionKind(
+                        "quickfix",
                     ),
-                    edit: Some(
-                        SnippetWorkspaceEdit {
-                            changes: Some(
-                                {
-                                    Url {
-                                        scheme: "file",
-                                        cannot_be_a_base: false,
-                                        username: "",
-                                        password: None,
-                                        host: None,
-                                        port: None,
-                                        path: "/test/driver/subcommand/repl.rs",
-                                        query: None,
-                                        fragment: None,
-                                    }: [
-                                        TextEdit {
-                                            range: Range {
-                                                start: Position {
-                                                    line: 290,
-                                                    character: 8,
-                                                },
-                                                end: Position {
-                                                    line: 290,
-                                                    character: 11,
-                                                },
+                ),
+                edit: Some(
+                    SnippetWorkspaceEdit {
+                        changes: Some(
+                            {
+                                Url {
+                                    scheme: "file",
+                                    cannot_be_a_base: false,
+                                    username: "",
+                                    password: None,
+                                    host: None,
+                                    port: None,
+                                    path: "/test/driver/subcommand/repl.rs",
+                                    query: None,
+                                    fragment: None,
+                                }: [
+                                    TextEdit {
+                                        range: Range {
+                                            start: Position {
+                                                line: 290,
+                                                character: 8,
+                                            },
+                                            end: Position {
+                                                line: 290,
+                                                character: 11,
                                             },
-                                            new_text: "_foo",
                                         },
-                                    ],
-                                },
-                            ),
-                            document_changes: None,
-                            change_annotations: None,
-                        },
-                    ),
-                    is_preferred: Some(
-                        true,
-                    ),
-                    data: None,
-                },
+                                        new_text: "_foo",
+                                    },
+                                ],
+                            },
+                        ),
+                        document_changes: None,
+                        change_annotations: None,
+                    },
+                ),
+                is_preferred: Some(
+                    true,
+                ),
+                data: None,
             },
-        ),
+        ],
     },
 ]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
index b595487e8f..0993aa1af1 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
@@ -72,7 +72,7 @@
             ),
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
     MappedRustDiagnostic {
         url: Url {
@@ -143,69 +143,55 @@
             tags: None,
             data: None,
         },
-        fix: Some(
-            Fix {
-                ranges: [
-                    Range {
-                        start: Position {
-                            line: 290,
-                            character: 8,
-                        },
-                        end: Position {
-                            line: 290,
-                            character: 11,
-                        },
-                    },
-                ],
-                action: CodeAction {
-                    title: "consider prefixing with an underscore: `_foo`",
-                    group: None,
-                    kind: Some(
-                        CodeActionKind(
-                            "quickfix",
-                        ),
+        fixes: [
+            CodeAction {
+                title: "consider prefixing with an underscore: `_foo`",
+                group: None,
+                kind: Some(
+                    CodeActionKind(
+                        "quickfix",
                     ),
-                    edit: Some(
-                        SnippetWorkspaceEdit {
-                            changes: Some(
-                                {
-                                    Url {
-                                        scheme: "file",
-                                        cannot_be_a_base: false,
-                                        username: "",
-                                        password: None,
-                                        host: None,
-                                        port: None,
-                                        path: "/test/driver/subcommand/repl.rs",
-                                        query: None,
-                                        fragment: None,
-                                    }: [
-                                        TextEdit {
-                                            range: Range {
-                                                start: Position {
-                                                    line: 290,
-                                                    character: 8,
-                                                },
-                                                end: Position {
-                                                    line: 290,
-                                                    character: 11,
-                                                },
+                ),
+                edit: Some(
+                    SnippetWorkspaceEdit {
+                        changes: Some(
+                            {
+                                Url {
+                                    scheme: "file",
+                                    cannot_be_a_base: false,
+                                    username: "",
+                                    password: None,
+                                    host: None,
+                                    port: None,
+                                    path: "/test/driver/subcommand/repl.rs",
+                                    query: None,
+                                    fragment: None,
+                                }: [
+                                    TextEdit {
+                                        range: Range {
+                                            start: Position {
+                                                line: 290,
+                                                character: 8,
+                                            },
+                                            end: Position {
+                                                line: 290,
+                                                character: 11,
                                             },
-                                            new_text: "_foo",
                                         },
-                                    ],
-                                },
-                            ),
-                            document_changes: None,
-                            change_annotations: None,
-                        },
-                    ),
-                    is_preferred: Some(
-                        true,
-                    ),
-                    data: None,
-                },
+                                        new_text: "_foo",
+                                    },
+                                ],
+                            },
+                        ),
+                        document_changes: None,
+                        change_annotations: None,
+                    },
+                ),
+                is_preferred: Some(
+                    true,
+                ),
+                data: None,
             },
-        ),
+        ],
     },
 ]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
index 3ee50392a7..d7a974ae57 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
@@ -72,7 +72,7 @@
             ),
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
     MappedRustDiagnostic {
         url: Url {
@@ -143,69 +143,55 @@
             tags: None,
             data: None,
         },
-        fix: Some(
-            Fix {
-                ranges: [
-                    Range {
-                        start: Position {
-                            line: 290,
-                            character: 8,
-                        },
-                        end: Position {
-                            line: 290,
-                            character: 11,
-                        },
-                    },
-                ],
-                action: CodeAction {
-                    title: "consider prefixing with an underscore: `_foo`",
-                    group: None,
-                    kind: Some(
-                        CodeActionKind(
-                            "quickfix",
-                        ),
+        fixes: [
+            CodeAction {
+                title: "consider prefixing with an underscore: `_foo`",
+                group: None,
+                kind: Some(
+                    CodeActionKind(
+                        "quickfix",
                     ),
-                    edit: Some(
-                        SnippetWorkspaceEdit {
-                            changes: Some(
-                                {
-                                    Url {
-                                        scheme: "file",
-                                        cannot_be_a_base: false,
-                                        username: "",
-                                        password: None,
-                                        host: None,
-                                        port: None,
-                                        path: "/test/driver/subcommand/repl.rs",
-                                        query: None,
-                                        fragment: None,
-                                    }: [
-                                        TextEdit {
-                                            range: Range {
-                                                start: Position {
-                                                    line: 290,
-                                                    character: 8,
-                                                },
-                                                end: Position {
-                                                    line: 290,
-                                                    character: 11,
-                                                },
+                ),
+                edit: Some(
+                    SnippetWorkspaceEdit {
+                        changes: Some(
+                            {
+                                Url {
+                                    scheme: "file",
+                                    cannot_be_a_base: false,
+                                    username: "",
+                                    password: None,
+                                    host: None,
+                                    port: None,
+                                    path: "/test/driver/subcommand/repl.rs",
+                                    query: None,
+                                    fragment: None,
+                                }: [
+                                    TextEdit {
+                                        range: Range {
+                                            start: Position {
+                                                line: 290,
+                                                character: 8,
+                                            },
+                                            end: Position {
+                                                line: 290,
+                                                character: 11,
                                             },
-                                            new_text: "_foo",
                                         },
-                                    ],
-                                },
-                            ),
-                            document_changes: None,
-                            change_annotations: None,
-                        },
-                    ),
-                    is_preferred: Some(
-                        true,
-                    ),
-                    data: None,
-                },
+                                        new_text: "_foo",
+                                    },
+                                ],
+                            },
+                        ),
+                        document_changes: None,
+                        change_annotations: None,
+                    },
+                ),
+                is_preferred: Some(
+                    true,
+                ),
+                data: None,
             },
-        ),
+        ],
     },
 ]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
index 8ec92888ce..5ea27a1527 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
@@ -88,7 +88,7 @@
             tags: None,
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
     MappedRustDiagnostic {
         url: Url {
@@ -179,6 +179,6 @@
             tags: None,
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
 ]
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
index d7987f65ed..f77abde515 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
@@ -114,7 +114,7 @@
             tags: None,
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
     MappedRustDiagnostic {
         url: Url {
@@ -205,7 +205,7 @@
             tags: None,
             data: None,
         },
-        fix: None,
+        fixes: [],
     },
     MappedRustDiagnostic {
         url: Url {
@@ -296,92 +296,68 @@
             tags: None,
             data: None,
         },
-        fix: Some(
-            Fix {
-                ranges: [
-                    Range {
-                        start: Position {
-                            line: 2,
-                            character: 4,
-                        },
-                        end: Position {
-                            line: 2,
-                            character: 30,
-                        },
-                    },
-                    Range {
-                        start: Position {
-                            line: 3,
-                            character: 4,
-                        },
-                        end: Position {
-                            line: 3,
-                            character: 5,
-                        },
-                    },
-                ],
-                action: CodeAction {
-                    title: "return the expression directly: `(0..10).collect()`",
-                    group: None,
-                    kind: Some(
-                        CodeActionKind(
-                            "quickfix",
-                        ),
+        fixes: [
+            CodeAction {
+                title: "return the expression directly: `(0..10).collect()`",
+                group: None,
+                kind: Some(
+                    CodeActionKind(
+                        "quickfix",
                     ),
-                    edit: Some(
-                        SnippetWorkspaceEdit {
-                            changes: Some(
-                                {
-                                    Url {
-                                        scheme: "file",
-                                        cannot_be_a_base: false,
-                                        username: "",
-                                        password: None,
-                                        host: None,
-                                        port: None,
-                                        path: "/test/src/main.rs",
-                                        query: None,
-                                        fragment: None,
-                                    }: [
-                                        TextEdit {
-                                            range: Range {
-                                                start: Position {
-                                                    line: 2,
-                                                    character: 4,
-                                                },
-                                                end: Position {
-                                                    line: 2,
-                                                    character: 30,
-                                                },
+                ),
+                edit: Some(
+                    SnippetWorkspaceEdit {
+                        changes: Some(
+                            {
+                                Url {
+                                    scheme: "file",
+                                    cannot_be_a_base: false,
+                                    username: "",
+                                    password: None,
+                                    host: None,
+                                    port: None,
+                                    path: "/test/src/main.rs",
+                                    query: None,
+                                    fragment: None,
+                                }: [
+                                    TextEdit {
+                                        range: Range {
+                                            start: Position {
+                                                line: 2,
+                                                character: 4,
+                                            },
+                                            end: Position {
+                                                line: 2,
+                                                character: 30,
                                             },
-                                            new_text: "",
                                         },
-                                        TextEdit {
-                                            range: Range {
-                                                start: Position {
-                                                    line: 3,
-                                                    character: 4,
-                                                },
-                                                end: Position {
-                                                    line: 3,
-                                                    character: 5,
-                                                },
+                                        new_text: "",
+                                    },
+                                    TextEdit {
+                                        range: Range {
+                                            start: Position {
+                                                line: 3,
+                                                character: 4,
+                                            },
+                                            end: Position {
+                                                line: 3,
+                                                character: 5,
                                             },
-                                            new_text: "(0..10).collect()",
                                         },
-                                    ],
-                                },
-                            ),
-                            document_changes: None,
-                            change_annotations: None,
-                        },
-                    ),
-                    is_preferred: Some(
-                        true,
-                    ),
-                    data: None,
-                },
+                                        new_text: "(0..10).collect()",
+                                    },
+                                ],
+                            },
+                        ),
+                        document_changes: None,
+                        change_annotations: None,
+                    },
+                ),
+                is_preferred: Some(
+                    true,
+                ),
+                data: None,
             },
-        ),
+        ],
     },
 ]