diff --git a/editors/code/package.json b/editors/code/package.json
index f36e34b6a1b..5e2a1c69e94 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -284,6 +284,14 @@
                 "command": "rust-analyzer.clearFlycheck",
                 "title": "Clear flycheck diagnostics",
                 "category": "rust-analyzer"
+            },
+            {
+                "command": "rust-analyzer.openFile",
+                "title": "Open File"
+            },
+            {
+                "command": "rust-analyzer.revealDependency",
+                "title": "Reveal File"
             }
         ],
         "keybindings": [
@@ -1956,6 +1964,14 @@
                 }
             ]
         },
+        "views": {
+            "explorer": [
+                {
+                    "id": "rustDependencies",
+                    "name": "Rust Dependencies"
+                }
+            ]
+        },
         "jsonValidation": [
             {
                 "fileMatch": "rust-project.json",
@@ -1963,4 +1979,4 @@
             }
         ]
     }
-}
+}
\ No newline at end of file
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 2d5272d199d..e5aa06025b2 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -8,10 +8,11 @@ import { applySnippetWorkspaceEdit, applySnippetTextEdits } from "./snippets";
 import { spawnSync } from "child_process";
 import { RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run";
 import { AstInspector } from "./ast_inspector";
-import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor } from "./util";
+import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor, RustEditor } from './util';
 import { startDebugSession, makeDebugConfig } from "./debug";
 import { LanguageClient } from "vscode-languageclient/node";
 import { LINKED_COMMANDS } from "./client";
+import { DependencyId } from './dependencies_provider';
 
 export * from "./ast_inspector";
 export * from "./run";
@@ -266,6 +267,44 @@ export function openCargoToml(ctx: CtxInit): Cmd {
     };
 }
 
+export function openFile(_ctx: CtxInit): Cmd {
+    return async (uri: vscode.Uri) => {
+        try {
+            await vscode.window.showTextDocument(uri);
+        } catch (err) {
+            await vscode.window.showErrorMessage(err.message);
+        }
+    };
+}
+
+export function revealDependency(ctx: CtxInit): Cmd {
+    return async (editor: RustEditor) => {
+        const rootPath = vscode.workspace.workspaceFolders![0].uri.fsPath;
+        const documentPath = editor.document.uri.fsPath;
+        if (documentPath.startsWith(rootPath)) return;
+        const dep = ctx.dependencies.getDependency(documentPath);
+        if (dep) {
+            await ctx.treeView.reveal(dep, { select: true, expand: true });
+        } else {
+            let documentPath = editor.document.uri.fsPath;
+            const parentChain: DependencyId[] = [{ id: documentPath.toLowerCase() }];
+            do {
+                documentPath = path.dirname(documentPath);
+                parentChain.push({ id: documentPath.toLowerCase() });
+            }
+            while (!ctx.dependencies.contains(documentPath));
+            parentChain.reverse();
+            for (const idx in parentChain) {
+                await ctx.treeView.reveal(parentChain[idx], { select: true, expand: true });
+            }
+        }
+    };
+}
+
+export async function execRevealDependency(e: RustEditor): Promise<void> {
+    await vscode.commands.executeCommand('rust-analyzer.revealDependency', e);
+}
+
 export function ssr(ctx: CtxInit): Cmd {
     return async () => {
         const editor = vscode.window.activeTextEditor;
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 567b9216bc1..e6829ac4b9e 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -3,8 +3,8 @@ import * as lc from "vscode-languageclient/node";
 import * as ra from "./lsp_ext";
 import * as path from "path";
 
-import { Config, prepareVSCodeConfig } from "./config";
-import { createClient } from "./client";
+import {Config, prepareVSCodeConfig} from './config';
+import {createClient} from './client';
 import {
     executeDiscoverProject,
     isRustDocument,
@@ -12,11 +12,13 @@ import {
     LazyOutputChannel,
     log,
     RustEditor,
-} from "./util";
-import { ServerStatusParams } from "./lsp_ext";
-import { PersistentState } from "./persistent_state";
-import { bootstrap } from "./bootstrap";
-import { ExecOptions } from "child_process";
+} from './util';
+import {ServerStatusParams} from './lsp_ext';
+import {Dependency, DependencyFile, RustDependenciesProvider, DependencyId} from './dependencies_provider';
+import {execRevealDependency} from './commands';
+import {PersistentState} from "./persistent_state";
+import {bootstrap} from "./bootstrap";
+import {ExecOptions} from "child_process";
 
 // We only support local folders, not eg. Live Share (`vlsl:` scheme), so don't activate if
 // only those are in use. We use "Empty" to represent these scenarios
@@ -25,12 +27,12 @@ import { ExecOptions } from "child_process";
 export type Workspace =
     | { kind: "Empty" }
     | {
-          kind: "Workspace Folder";
-      }
+    kind: "Workspace Folder";
+}
     | {
-          kind: "Detached Files";
-          files: vscode.TextDocument[];
-      };
+    kind: "Detached Files";
+    files: vscode.TextDocument[];
+};
 
 export function fetchWorkspace(): Workspace {
     const folders = (vscode.workspace.workspaceFolders || []).filter(
@@ -42,12 +44,12 @@ export function fetchWorkspace(): Workspace {
 
     return folders.length === 0
         ? rustDocuments.length === 0
-            ? { kind: "Empty" }
+            ? {kind: "Empty"}
             : {
-                  kind: "Detached Files",
-                  files: rustDocuments,
-              }
-        : { kind: "Workspace Folder" };
+                kind: "Detached Files",
+                files: rustDocuments,
+            }
+        : {kind: "Workspace Folder"};
 }
 
 export async function discoverWorkspace(
@@ -84,6 +86,8 @@ export class Ctx {
     private commandFactories: Record<string, CommandFactory>;
     private commandDisposables: Disposable[];
     private unlinkedFiles: vscode.Uri[];
+    readonly dependencies: RustDependenciesProvider;
+    readonly treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId>;
 
     get client() {
         return this._client;
@@ -92,7 +96,9 @@ export class Ctx {
     constructor(
         readonly extCtx: vscode.ExtensionContext,
         commandFactories: Record<string, CommandFactory>,
-        workspace: Workspace
+        workspace: Workspace,
+        dependencies: RustDependenciesProvider,
+        treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId>
     ) {
         extCtx.subscriptions.push(this);
         this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
@@ -101,6 +107,8 @@ export class Ctx {
         this.commandDisposables = [];
         this.commandFactories = commandFactories;
         this.unlinkedFiles = [];
+        this.dependencies = dependencies;
+        this.treeView = treeView;
 
         this.state = new PersistentState(extCtx.globalState);
         this.config = new Config(extCtx);
@@ -109,6 +117,13 @@ export class Ctx {
         this.setServerStatus({
             health: "stopped",
         });
+        vscode.window.onDidChangeActiveTextEditor(e => {
+            if (e && isRustEditor(e)) {
+                execRevealDependency(e).catch(reason => {
+                    void vscode.window.showErrorMessage(`Dependency error: ${reason}`);
+                });
+            }
+        });
     }
 
     dispose() {
@@ -174,7 +189,7 @@ export class Ctx {
             const newEnv = Object.assign({}, process.env, this.config.serverExtraEnv);
             const run: lc.Executable = {
                 command: this._serverPath,
-                options: { env: newEnv },
+                options: {env: newEnv},
             };
             const serverOptions = {
                 run,
@@ -348,6 +363,7 @@ export class Ctx {
                 statusBar.color = undefined;
                 statusBar.backgroundColor = undefined;
                 statusBar.command = "rust-analyzer.stopServer";
+                this.dependencies.refresh();
                 break;
             case "warning":
                 if (status.message) {
@@ -410,4 +426,5 @@ export class Ctx {
 export interface Disposable {
     dispose(): void;
 }
+
 export type Cmd = (...args: any[]) => unknown;
diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts
new file mode 100644
index 00000000000..0f2e5e5ea07
--- /dev/null
+++ b/editors/code/src/dependencies_provider.ts
@@ -0,0 +1,151 @@
+import * as vscode from 'vscode';
+import * as fspath from 'path';
+import * as fs from 'fs';
+import * as os from 'os';
+import { activeToolchain, Cargo, Crate, getRustcVersion } from './toolchain';
+
+const debugOutput = vscode.window.createOutputChannel("Debug");
+
+export class RustDependenciesProvider implements vscode.TreeDataProvider<Dependency | DependencyFile>{
+  cargo: Cargo;
+  dependenciesMap: { [id: string]: Dependency | DependencyFile };
+
+  constructor(
+    private readonly workspaceRoot: string,
+  ) {
+    this.cargo = new Cargo(this.workspaceRoot || '.', debugOutput);
+    this.dependenciesMap = {};
+  }
+
+  private _onDidChangeTreeData: vscode.EventEmitter<Dependency | DependencyFile | undefined | null | void> = new vscode.EventEmitter<Dependency | undefined | null | void>();
+
+  readonly onDidChangeTreeData: vscode.Event<Dependency | DependencyFile | undefined | null | void> = this._onDidChangeTreeData.event;
+
+
+  getDependency(filePath: string): Dependency | DependencyFile | undefined {
+    return this.dependenciesMap[filePath.toLowerCase()];
+  }
+
+  contains(filePath: string): boolean {
+    return filePath.toLowerCase() in this.dependenciesMap;
+  }
+
+  refresh(): void {
+    this._onDidChangeTreeData.fire();
+  }
+
+  getParent?(element: Dependency | DependencyFile): vscode.ProviderResult<Dependency | DependencyFile> {
+    if (element instanceof Dependency) return undefined;
+    return element.parent;
+  }
+
+  getTreeItem(element: Dependency | DependencyFile): vscode.TreeItem | Thenable<vscode.TreeItem> {
+    if (element.id! in this.dependenciesMap) return this.dependenciesMap[element.id!];
+    return element;
+  }
+
+  getChildren(element?: Dependency | DependencyFile): vscode.ProviderResult<Dependency[] | DependencyFile[]> {
+    return new Promise((resolve, _reject) => {
+      if (!this.workspaceRoot) {
+        void vscode.window.showInformationMessage('No dependency in empty workspace');
+        return Promise.resolve([]);
+      }
+
+      if (element) {
+        const files = fs.readdirSync(element.dependencyPath).map(fileName => {
+          const filePath = fspath.join(element.dependencyPath, fileName);
+          const collapsibleState = fs.lstatSync(filePath).isDirectory() ?
+            vscode.TreeItemCollapsibleState.Collapsed :
+            vscode.TreeItemCollapsibleState.None;
+          const dep = new DependencyFile(
+            fileName,
+            filePath,
+            element,
+            collapsibleState
+          );
+          this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep;
+          return dep;
+        });
+        return resolve(
+          files
+        );
+      } else {
+        return resolve(this.getRootDependencies());
+      }
+    });
+  }
+
+  private async getRootDependencies(): Promise<Dependency[]> {
+    const registryDir = fspath.join(os.homedir(), '.cargo', 'registry', 'src');
+    const basePath = fspath.join(registryDir, fs.readdirSync(registryDir)[0]);
+    const deps = await this.getDepsInCartoTree(basePath);
+    const stdlib = await this.getStdLib();
+    return [stdlib].concat(deps);
+  }
+
+  private async getStdLib(): Promise<Dependency> {
+    const toolchain = await activeToolchain();
+    const rustVersion = await getRustcVersion(os.homedir());
+    const stdlibPath = fspath.join(os.homedir(), '.rustup', 'toolchains', toolchain, 'lib', 'rustlib', 'src', 'rust', 'library');
+    return new Dependency(
+      "stdlib",
+      rustVersion,
+      stdlibPath,
+      vscode.TreeItemCollapsibleState.Collapsed
+    );
+  }
+
+  private async getDepsInCartoTree(basePath: string): Promise<Dependency[]> {
+    const crates: Crate[] = await this.cargo.crates();
+    const toDep = (moduleName: string, version: string): Dependency => {
+      const cratePath = fspath.join(basePath, `${moduleName}-${version}`);
+      return new Dependency(
+        moduleName,
+        version,
+        cratePath,
+        vscode.TreeItemCollapsibleState.Collapsed
+      );
+    };
+
+    const deps = crates.map(crate => {
+      const dep = toDep(crate.name, crate.version);
+      this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep;
+      return dep;
+    });
+    return deps;
+  }
+}
+
+
+export class Dependency extends vscode.TreeItem {
+  constructor(
+    public readonly label: string,
+    private version: string,
+    readonly dependencyPath: string,
+    public readonly collapsibleState: vscode.TreeItemCollapsibleState
+  ) {
+    super(label, collapsibleState);
+    this.tooltip = `${this.label}-${this.version}`;
+    this.description = this.version;
+    this.resourceUri = vscode.Uri.file(dependencyPath);
+  }
+}
+
+export class DependencyFile extends vscode.TreeItem {
+
+  constructor(
+    readonly label: string,
+    readonly dependencyPath: string,
+    readonly parent: Dependency | DependencyFile,
+    public readonly collapsibleState: vscode.TreeItemCollapsibleState
+  ) {
+    super(vscode.Uri.file(dependencyPath), collapsibleState);
+    const isDir = fs.lstatSync(this.dependencyPath).isDirectory();
+    this.id = this.dependencyPath.toLowerCase();
+    if (!isDir) {
+      this.command = { command: 'rust-analyzer.openFile', title: "Open File", arguments: [vscode.Uri.file(this.dependencyPath)], };
+    }
+  }
+}
+
+export type DependencyId = { id: string };
\ No newline at end of file
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 7ae8fa8ca28..62b2e7a2771 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -2,10 +2,10 @@ import * as vscode from "vscode";
 import * as lc from "vscode-languageclient/node";
 
 import * as commands from "./commands";
-import { CommandFactory, Ctx, fetchWorkspace } from "./ctx";
+import {CommandFactory, Ctx, fetchWorkspace} from "./ctx";
 import * as diagnostics from "./diagnostics";
-import { activateTaskProvider } from "./tasks";
-import { setContextValue } from "./util";
+import {activateTaskProvider} from "./tasks";
+import {setContextValue} from "./util";
 
 const RUST_PROJECT_CONTEXT_NAME = "inRustProject";
 
@@ -24,11 +24,12 @@ export async function activate(
         vscode.window
             .showWarningMessage(
                 `You have both the rust-analyzer (rust-lang.rust-analyzer) and Rust (rust-lang.rust) ` +
-                    "plugins enabled. These are known to conflict and cause various functions of " +
-                    "both plugins to not work correctly. You should disable one of them.",
+                "plugins enabled. These are known to conflict and cause various functions of " +
+                "both plugins to not work correctly. You should disable one of them.",
                 "Got it"
             )
-            .then(() => {}, console.error);
+            .then(() => {
+            }, console.error);
     }
 
     const ctx = new Ctx(context, createCommands(), fetchWorkspace());
@@ -118,7 +119,7 @@ function createCommands(): Record<string, CommandFactory> {
     return {
         onEnter: {
             enabled: commands.onEnter,
-            disabled: (_) => () => vscode.commands.executeCommand("default:type", { text: "\n" }),
+            disabled: (_) => () => vscode.commands.executeCommand("default:type", {text: "\n"}),
         },
         restartServer: {
             enabled: (ctx) => async () => {
@@ -144,51 +145,54 @@ function createCommands(): Record<string, CommandFactory> {
                     health: "stopped",
                 });
             },
-            disabled: (_) => async () => {},
+            disabled: (_) => async () => {
+            },
         },
 
-        analyzerStatus: { enabled: commands.analyzerStatus },
-        memoryUsage: { enabled: commands.memoryUsage },
-        shuffleCrateGraph: { enabled: commands.shuffleCrateGraph },
-        reloadWorkspace: { enabled: commands.reloadWorkspace },
-        rebuildProcMacros: { enabled: commands.rebuildProcMacros },
-        addProject: { enabled: commands.addProject },
-        matchingBrace: { enabled: commands.matchingBrace },
-        joinLines: { enabled: commands.joinLines },
-        parentModule: { enabled: commands.parentModule },
-        syntaxTree: { enabled: commands.syntaxTree },
-        viewHir: { enabled: commands.viewHir },
-        viewMir: { enabled: commands.viewMir },
+        analyzerStatus: {enabled: commands.analyzerStatus},
+        memoryUsage: {enabled: commands.memoryUsage},
+        shuffleCrateGraph: {enabled: commands.shuffleCrateGraph},
+        reloadWorkspace: {enabled: commands.reloadWorkspace},
+        rebuildProcMacros: {enabled: commands.rebuildProcMacros},
+        addProject: {enabled: commands.addProject},
+        matchingBrace: {enabled: commands.matchingBrace},
+        joinLines: {enabled: commands.joinLines},
+        parentModule: {enabled: commands.parentModule},
+        syntaxTree: {enabled: commands.syntaxTree},
+        viewHir: {enabled: commands.viewHir},
+        viewMir: {enabled: commands.viewMir},
         interpretFunction: { enabled: commands.interpretFunction },
-        viewFileText: { enabled: commands.viewFileText },
-        viewItemTree: { enabled: commands.viewItemTree },
-        viewCrateGraph: { enabled: commands.viewCrateGraph },
-        viewFullCrateGraph: { enabled: commands.viewFullCrateGraph },
-        expandMacro: { enabled: commands.expandMacro },
-        run: { enabled: commands.run },
-        copyRunCommandLine: { enabled: commands.copyRunCommandLine },
-        debug: { enabled: commands.debug },
-        newDebugConfig: { enabled: commands.newDebugConfig },
-        openDocs: { enabled: commands.openDocs },
-        openCargoToml: { enabled: commands.openCargoToml },
-        peekTests: { enabled: commands.peekTests },
-        moveItemUp: { enabled: commands.moveItemUp },
-        moveItemDown: { enabled: commands.moveItemDown },
-        cancelFlycheck: { enabled: commands.cancelFlycheck },
-        clearFlycheck: { enabled: commands.clearFlycheck },
-        runFlycheck: { enabled: commands.runFlycheck },
-        ssr: { enabled: commands.ssr },
-        serverVersion: { enabled: commands.serverVersion },
+        viewFileText: {enabled: commands.viewFileText},
+        viewItemTree: {enabled: commands.viewItemTree},
+        viewCrateGraph: {enabled: commands.viewCrateGraph},
+        viewFullCrateGraph: {enabled: commands.viewFullCrateGraph},
+        expandMacro: {enabled: commands.expandMacro},
+        run: {enabled: commands.run},
+        copyRunCommandLine: {enabled: commands.copyRunCommandLine},
+        debug: {enabled: commands.debug},
+        newDebugConfig: {enabled: commands.newDebugConfig},
+        openDocs: {enabled: commands.openDocs},
+        openCargoToml: {enabled: commands.openCargoToml},
+        peekTests: {enabled: commands.peekTests},
+        moveItemUp: {enabled: commands.moveItemUp},
+        moveItemDown: {enabled: commands.moveItemDown},
+        cancelFlycheck: {enabled: commands.cancelFlycheck},
+        clearFlycheck: {enabled: commands.clearFlycheck},
+        runFlycheck: {enabled: commands.runFlycheck},
+        ssr: {enabled: commands.ssr},
+        serverVersion: {enabled: commands.serverVersion},
         // Internal commands which are invoked by the server.
-        applyActionGroup: { enabled: commands.applyActionGroup },
-        applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand },
-        debugSingle: { enabled: commands.debugSingle },
-        gotoLocation: { enabled: commands.gotoLocation },
-        linkToCommand: { enabled: commands.linkToCommand },
-        resolveCodeAction: { enabled: commands.resolveCodeAction },
-        runSingle: { enabled: commands.runSingle },
-        showReferences: { enabled: commands.showReferences },
-        triggerParameterHints: { enabled: commands.triggerParameterHints },
-        openLogs: { enabled: commands.openLogs },
+        applyActionGroup: {enabled: commands.applyActionGroup},
+        applySnippetWorkspaceEdit: {enabled: commands.applySnippetWorkspaceEditCommand},
+        debugSingle: {enabled: commands.debugSingle},
+        gotoLocation: {enabled: commands.gotoLocation},
+        linkToCommand: {enabled: commands.linkToCommand},
+        resolveCodeAction: {enabled: commands.resolveCodeAction},
+        runSingle: {enabled: commands.runSingle},
+        showReferences: {enabled: commands.showReferences},
+        triggerParameterHints: {enabled: commands.triggerParameterHints},
+        openLogs: {enabled: commands.openLogs},
+        openFile: {enabled: commands.openFile},
+        revealDependency: {enabled: commands.revealDependency}
     };
 }
diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts
index 917a1d6b099..6f37451edd2 100644
--- a/editors/code/src/toolchain.ts
+++ b/editors/code/src/toolchain.ts
@@ -5,6 +5,15 @@ import * as readline from "readline";
 import * as vscode from "vscode";
 import { execute, log, memoizeAsync } from "./util";
 
+
+const TREE_LINE_PATTERN = new RegExp(/(.+)\sv(\d+\.\d+\.\d+)(?:\s\((.+)\))?/);
+const TOOLCHAIN_PATTERN = new RegExp(/(.*)\s\(.*\)/);
+
+export interface Crate {
+    name: string;
+    version: string;
+}
+
 interface CompilationArtifact {
     fileName: string;
     name: string;
@@ -96,6 +105,43 @@ export class Cargo {
         return artifacts[0].fileName;
     }
 
+    async crates(): Promise<Crate[]> {
+        const pathToCargo = await cargoPath();
+        return await new Promise((resolve, reject) => {
+            const crates: Crate[] = [];
+
+            const cargo = cp.spawn(pathToCargo, ['tree', '--prefix', 'none'], {
+                stdio: ['ignore', 'pipe', 'pipe'],
+                cwd: this.rootFolder
+            });
+            const rl = readline.createInterface({ input: cargo.stdout });
+            rl.on('line', line => {
+                const match = line.match(TREE_LINE_PATTERN);
+                if (match) {
+                    const name = match[1];
+                    const version = match[2];
+                    const extraInfo = match[3];
+                    // ignore duplicates '(*)' and path dependencies
+                    if (this.shouldIgnore(extraInfo)) {
+                        return;
+                    }
+                    crates.push({ name, version });
+                }
+            });
+            cargo.on('exit', (exitCode, _) => {
+                if (exitCode === 0)
+                    resolve(crates);
+                else
+                    reject(new Error(`exit code: ${exitCode}.`));
+            });
+
+        });
+    }
+
+    private shouldIgnore(extraInfo: string): boolean {
+        return extraInfo !== undefined && (extraInfo === '*' || path.isAbsolute(extraInfo));
+    }
+
     private async runCargo(
         cargoArgs: string[],
         onStdoutJson: (obj: any) => void,
@@ -127,6 +173,58 @@ export class Cargo {
     }
 }
 
+export async function activeToolchain(): Promise<string> {
+    const pathToRustup = await rustupPath();
+    return await new Promise((resolve, reject) => {
+        const execution = cp.spawn(pathToRustup, ['show', 'active-toolchain'], {
+            stdio: ['ignore', 'pipe', 'pipe'],
+            cwd: os.homedir()
+        });
+        const rl = readline.createInterface({ input: execution.stdout });
+
+        let currToolchain: string | undefined = undefined;
+        rl.on('line', line => {
+            const match = line.match(TOOLCHAIN_PATTERN);
+            if (match) {
+                currToolchain = match[1];
+            }
+        });
+        execution.on('exit', (exitCode, _) => {
+            if (exitCode === 0 && currToolchain)
+                resolve(currToolchain);
+            else
+                reject(new Error(`exit code: ${exitCode}.`));
+        });
+
+    });
+}
+
+export async function rustVersion(): Promise<string> {
+    const pathToRustup = await rustupPath();
+    return await new Promise((resolve, reject) => {
+        const execution = cp.spawn(pathToRustup, ['show', 'active-toolchain'], {
+            stdio: ['ignore', 'pipe', 'pipe'],
+            cwd: os.homedir()
+        });
+        const rl = readline.createInterface({ input: execution.stdout });
+
+        let currToolchain: string | undefined = undefined;
+        rl.on('line', line => {
+            const match = line.match(TOOLCHAIN_PATTERN);
+            if (match) {
+                currToolchain = match[1];
+            }
+        });
+        execution.on('exit', (exitCode, _) => {
+            if (exitCode === 1 && currToolchain)
+                resolve(currToolchain);
+            else
+                reject(new Error(`exit code: ${exitCode}.`));
+        });
+
+    });
+}
+
 /** Mirrors `project_model::sysroot::discover_sysroot_dir()` implementation*/
 export async function getSysroot(dir: string): Promise<string> {
     const rustcPath = await getPathForExecutable("rustc");
@@ -145,11 +243,26 @@ export async function getRustcId(dir: string): Promise<string> {
     return rx.exec(data)![1];
 }
 
+export async function getRustcVersion(dir: string): Promise<string> {
+    const rustcPath = await getPathForExecutable("rustc");
+
+    // do not memoize the result because the toolchain may change between runs
+    const data = await execute(`${rustcPath} -V`, { cwd: dir });
+    const rx = /(\d\.\d+\.\d+)/;
+
+    return rx.exec(data)![1];
+}
+
 /** Mirrors `toolchain::cargo()` implementation */
 export function cargoPath(): Promise<string> {
     return getPathForExecutable("cargo");
 }
 
+/** Mirrors `toolchain::cargo()` implementation */
+export function rustupPath(): Promise<string> {
+    return getPathForExecutable("rustup");
+}
+
 /** Mirrors `toolchain::get_path_for_executable()` implementation */
 export const getPathForExecutable = memoizeAsync(
     // We apply caching to decrease file-system interactions