about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-04-27 20:41:35 +0000
committerGitHub <noreply@github.com>2021-04-27 20:41:35 +0000
commitfb45d2adeccfc6732b702cd8fa2911b385bc15b7 (patch)
tree41ab22e69528ccb346bc01b913f9251a24925960
parente2b87735cc4b54ca530e7a99070da585d480b1c3 (diff)
parent1b4197cb3520e4a71f118aac61a83bab1a6f5931 (diff)
downloadrust-fb45d2adeccfc6732b702cd8fa2911b385bc15b7.tar.gz
rust-fb45d2adeccfc6732b702cd8fa2911b385bc15b7.zip
Merge #8624
8624: Automatically detect rust library source file map  r=vsrs a=vsrs

This PR adds a new possible `rust-analyzer.debug.sourceFileMap` value:
```json
{
    "rust-analyzer.debug.sourceFileMap": "auto"
}
```

I did not make it the default because it uses two shell calls (`rustc --print sysroot` and `rustc -V -v`). First one can be slow (https://github.com/rust-lang/rustup/issues/783)

Fixes #8619

Co-authored-by: vsrs <vit@conrlab.com>
-rw-r--r--editors/code/package.json5
-rw-r--r--editors/code/src/config.ts8
-rw-r--r--editors/code/src/debug.ts14
-rw-r--r--editors/code/src/toolchain.ts20
-rw-r--r--editors/code/src/util.ts21
5 files changed, 60 insertions, 8 deletions
diff --git a/editors/code/package.json b/editors/code/package.json
index 97d92e43c82..35b50e669d3 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -353,8 +353,9 @@
                         "Use [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools)"
                     ]
                 },
-                "rust-analyzer.debug.sourceFileMap": {
-                    "type": "object",
+                "rust-analyzer.debug.sourceFileMap": {                    
+                    "type": ["object", "string"],
+                    "const": "auto",
                     "description": "Optional source file mappings passed to the debug engine.",
                     "default": {
                         "/rustc/<id>": "${env:USERPROFILE}/.rustup/toolchains/<toolchain-id>/lib/rustlib/src/rust"
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 03f7d7cc348..e858f80bcc4 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -135,8 +135,12 @@ export class Config {
     }
 
     get debug() {
-        // "/rustc/<id>" used by suggestions only.
-        const { ["/rustc/<id>"]: _, ...sourceFileMap } = this.get<Record<string, string>>("debug.sourceFileMap");
+        let sourceFileMap = this.get<Record<string, string> | "auto">("debug.sourceFileMap");
+        if (sourceFileMap !== "auto") {
+            // "/rustc/<id>" used by suggestions only.
+            const { ["/rustc/<id>"]: _, ...trimmed } = this.get<Record<string, string>>("debug.sourceFileMap");
+            sourceFileMap = trimmed;
+        }
 
         return {
             engine: this.get<string>("debug.engine"),
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index 3889a277304..830980f968c 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -3,7 +3,7 @@ import * as vscode from 'vscode';
 import * as path from 'path';
 import * as ra from './lsp_ext';
 
-import { Cargo } from './toolchain';
+import { Cargo, getRustcId, getSysroot } from './toolchain';
 import { Ctx } from "./ctx";
 import { prepareEnv } from "./run";
 
@@ -104,7 +104,17 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v
 
     const executable = await getDebugExecutable(runnable);
     const env = prepareEnv(runnable, ctx.config.runnableEnv);
-    const debugConfig = knownEngines[debugEngine.id](runnable, simplifyPath(executable), env, debugOptions.sourceFileMap);
+    let sourceFileMap = debugOptions.sourceFileMap;
+    if (sourceFileMap === "auto") {
+        // let's try to use the default toolchain
+        const commitHash = await getRustcId(wsFolder);
+        const sysroot = await getSysroot(wsFolder);
+        const rustlib = path.normalize(sysroot + "/lib/rustlib/src/rust");
+        sourceFileMap = {};
+        sourceFileMap[`/rustc/${commitHash}/`] = rustlib;
+    }
+
+    const debugConfig = knownEngines[debugEngine.id](runnable, simplifyPath(executable), env, sourceFileMap);
     if (debugConfig.type in debugOptions.engineSettings) {
         const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
         for (var key in settingsMap) {
diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts
index a5dc3cf0ccf..68826c478d3 100644
--- a/editors/code/src/toolchain.ts
+++ b/editors/code/src/toolchain.ts
@@ -4,7 +4,7 @@ import * as path from 'path';
 import * as fs from 'fs';
 import * as readline from 'readline';
 import { OutputChannel } from 'vscode';
-import { log, memoize } from './util';
+import { execute, log, memoize } from './util';
 
 interface CompilationArtifact {
     fileName: string;
@@ -121,6 +121,24 @@ export class Cargo {
     }
 }
 
+/** Mirrors `project_model::sysroot::discover_sysroot_dir()` implementation*/
+export function getSysroot(dir: string): Promise<string> {
+    const rustcPath = getPathForExecutable("rustc");
+
+    // do not memoize the result because the toolchain may change between runs
+    return execute(`${rustcPath} --print sysroot`, { cwd: dir });
+}
+
+export async function getRustcId(dir: string): Promise<string> {
+    const rustcPath = getPathForExecutable("rustc");
+
+    // do not memoize the result because the toolchain may change between runs
+    const data = await execute(`${rustcPath} -V -v`, { cwd: dir });
+    const rx = /commit-hash:\s(.*)$/m.compile();
+
+    return rx.exec(data)![1];
+}
+
 /** Mirrors `toolchain::cargo()` implementation */
 export function cargoPath(): string {
     return getPathForExecutable("cargo");
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 53492a445c4..56e0e439e98 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -1,7 +1,7 @@
 import * as lc from "vscode-languageclient/node";
 import * as vscode from "vscode";
 import { strict as nativeAssert } from "assert";
-import { spawnSync } from "child_process";
+import { exec, ExecOptions, spawnSync } from "child_process";
 import { inspect } from "util";
 
 export function assert(condition: boolean, explanation: string): asserts condition {
@@ -141,3 +141,22 @@ export function memoize<Ret, TThis, Param extends string>(func: (this: TThis, ar
         return result;
     };
 }
+
+/** Awaitable wrapper around `child_process.exec` */
+export function execute(command: string, options: ExecOptions): Promise<string> {
+    return new Promise((resolve, reject) => {
+        exec(command, options, (err, stdout, stderr) => {
+            if (err) {
+                reject(err);
+                return;
+            }
+
+            if (stderr) {
+                reject(new Error(stderr));
+                return;
+            }
+
+            resolve(stdout.trimEnd());
+        });
+    });
+}