Skip to content

Commit 537f9b3

Browse files
committed
Auto merge of rust-lang#15160 - tetsuharuohzeki:enable-noUncheckedIndexedAccess, r=Veykril
editor/code: Enable `--noUncheckedIndexedAccess` & `--noPropertyAccessFromIndexSignature` ts option This enables typescript's these option: - [`--noUncheckedIndexedAccess`](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess) - This checks whether indexed access is not `null` (or `undefined`) strictly just as like checking by `std::option::Option::unwrap()`. - [`--noPropertyAccessFromIndexSignature`](https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature) - This disallows `bar.foo` access if the `bar` type is `{ [key: string]: someType; }`. ---- Additionally, to enable `--noUncheckedIndexedAccess` easily, this pull request introduces [option-t](https://www.npmjs.com/package/option-t) as a dependency instead of defining a function in this repository like `unwrapUndefinable()` . I'll remove it and define them byself if our dependency management policy is that to avoid to add a new package as possible.
2 parents aa91eda + f708453 commit 537f9b3

16 files changed

+132
-59
lines changed

editors/code/src/ast_inspector.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as vscode from "vscode";
22

33
import { Ctx, Disposable } from "./ctx";
44
import { RustEditor, isRustEditor } from "./util";
5+
import { unwrapUndefinable } from "./undefinable";
56

67
// FIXME: consider implementing this via the Tree View API?
78
// https://code.visualstudio.com/api/extension-guides/tree-view
@@ -164,8 +165,9 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
164165
if (!parsedRange) return;
165166

166167
const [begin, end] = parsedRange.slice(1).map((off) => this.positionAt(doc, +off));
167-
168-
return new vscode.Range(begin, end);
168+
const actualBegin = unwrapUndefinable(begin);
169+
const actualEnd = unwrapUndefinable(end);
170+
return new vscode.Range(actualBegin, actualEnd);
169171
}
170172

171173
// Memoize the last value, otherwise the CPU is at 100% single core

editors/code/src/bootstrap.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ async function getServer(
3636
config: Config,
3737
state: PersistentState
3838
): Promise<string | undefined> {
39-
const explicitPath = process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath;
39+
const explicitPath = process.env["__RA_LSP_SERVER_DEBUG"] ?? config.serverPath;
4040
if (explicitPath) {
4141
if (explicitPath.startsWith("~/")) {
4242
return os.homedir() + explicitPath.slice("~".length);

editors/code/src/client.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { WorkspaceEdit } from "vscode";
99
import { Config, prepareVSCodeConfig } from "./config";
1010
import { randomUUID } from "crypto";
1111
import { sep as pathSeparator } from "path";
12+
import { unwrapUndefinable } from "./undefinable";
1213

1314
export interface Env {
1415
[name: string]: string;
@@ -323,10 +324,12 @@ export async function createClient(
323324
}
324325
for (const [group, { index, items }] of groups) {
325326
if (items.length === 1) {
326-
result[index] = items[0];
327+
const item = unwrapUndefinable(items[0]);
328+
result[index] = item;
327329
} else {
328330
const action = new vscode.CodeAction(group);
329-
action.kind = items[0].kind;
331+
const item = unwrapUndefinable(items[0]);
332+
action.kind = item.kind;
330333
action.command = {
331334
command: "rust-analyzer.applyActionGroup",
332335
title: "",

editors/code/src/commands.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { startDebugSession, makeDebugConfig } from "./debug";
2020
import { LanguageClient } from "vscode-languageclient/node";
2121
import { LINKED_COMMANDS } from "./client";
2222
import { DependencyId } from "./dependencies_provider";
23+
import { unwrapUndefinable } from "./undefinable";
2324

2425
export * from "./ast_inspector";
2526
export * from "./run";
@@ -129,7 +130,8 @@ export function matchingBrace(ctx: CtxInit): Cmd {
129130
),
130131
});
131132
editor.selections = editor.selections.map((sel, idx) => {
132-
const active = client.protocol2CodeConverter.asPosition(response[idx]);
133+
const position = unwrapUndefinable(response[idx]);
134+
const active = client.protocol2CodeConverter.asPosition(position);
133135
const anchor = sel.isEmpty ? active : sel.anchor;
134136
return new vscode.Selection(anchor, active);
135137
});
@@ -231,7 +233,7 @@ export function parentModule(ctx: CtxInit): Cmd {
231233
if (!locations) return;
232234

233235
if (locations.length === 1) {
234-
const loc = locations[0];
236+
const loc = unwrapUndefinable(locations[0]);
235237

236238
const uri = client.protocol2CodeConverter.asUri(loc.targetUri);
237239
const range = client.protocol2CodeConverter.asRange(loc.targetRange);
@@ -331,7 +333,13 @@ async function revealParentChain(document: RustDocument, ctx: CtxInit) {
331333
} while (!ctx.dependencies?.contains(documentPath));
332334
parentChain.reverse();
333335
for (const idx in parentChain) {
334-
await ctx.treeView?.reveal(parentChain[idx], { select: true, expand: true });
336+
const treeView = ctx.treeView;
337+
if (!treeView) {
338+
continue;
339+
}
340+
341+
const dependency = unwrapUndefinable(parentChain[idx]);
342+
await treeView.reveal(dependency, { select: true, expand: true });
335343
}
336344
}
337345

editors/code/src/config.ts

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as path from "path";
44
import * as vscode from "vscode";
55
import { Env } from "./client";
66
import { log } from "./util";
7+
import { expectNotUndefined, unwrapUndefinable } from "./undefinable";
78

89
export type RunnableEnvCfg =
910
| undefined
@@ -338,7 +339,7 @@ export function substituteVariablesInEnv(env: Env): Env {
338339
const depRe = new RegExp(/\${(?<depName>.+?)}/g);
339340
let match = undefined;
340341
while ((match = depRe.exec(value))) {
341-
const depName = match.groups!.depName;
342+
const depName = unwrapUndefinable(match.groups?.["depName"]);
342343
deps.add(depName);
343344
// `depName` at this point can have a form of `expression` or
344345
// `prefix:expression`
@@ -356,7 +357,7 @@ export function substituteVariablesInEnv(env: Env): Env {
356357
if (match) {
357358
const { prefix, body } = match.groups!;
358359
if (prefix === "env") {
359-
const envName = body;
360+
const envName = unwrapUndefinable(body);
360361
envWithDeps[dep] = {
361362
value: process.env[envName] ?? "",
362363
deps: [],
@@ -384,13 +385,12 @@ export function substituteVariablesInEnv(env: Env): Env {
384385
do {
385386
leftToResolveSize = toResolve.size;
386387
for (const key of toResolve) {
387-
if (envWithDeps[key].deps.every((dep) => resolved.has(dep))) {
388-
envWithDeps[key].value = envWithDeps[key].value.replace(
389-
/\${(?<depName>.+?)}/g,
390-
(_wholeMatch, depName) => {
391-
return envWithDeps[depName].value;
392-
}
393-
);
388+
const item = unwrapUndefinable(envWithDeps[key]);
389+
if (item.deps.every((dep) => resolved.has(dep))) {
390+
item.value = item.value.replace(/\${(?<depName>.+?)}/g, (_wholeMatch, depName) => {
391+
const item = unwrapUndefinable(envWithDeps[depName]);
392+
return item.value;
393+
});
394394
resolved.add(key);
395395
toResolve.delete(key);
396396
}
@@ -399,7 +399,8 @@ export function substituteVariablesInEnv(env: Env): Env {
399399

400400
const resolvedEnv: Env = {};
401401
for (const key of Object.keys(env)) {
402-
resolvedEnv[key] = envWithDeps[`env:${key}`].value;
402+
const item = unwrapUndefinable(envWithDeps[`env:${key}`]);
403+
resolvedEnv[key] = item.value;
403404
}
404405
return resolvedEnv;
405406
}
@@ -418,20 +419,19 @@ function substituteVSCodeVariableInString(val: string): string {
418419
function computeVscodeVar(varName: string): string | null {
419420
const workspaceFolder = () => {
420421
const folders = vscode.workspace.workspaceFolders ?? [];
421-
if (folders.length === 1) {
422-
// TODO: support for remote workspaces?
423-
return folders[0].uri.fsPath;
424-
} else if (folders.length > 1) {
425-
// could use currently opened document to detect the correct
426-
// workspace. However, that would be determined by the document
427-
// user has opened on Editor startup. Could lead to
428-
// unpredictable workspace selection in practice.
429-
// It's better to pick the first one
430-
return folders[0].uri.fsPath;
431-
} else {
432-
// no workspace opened
433-
return "";
434-
}
422+
const folder = folders[0];
423+
// TODO: support for remote workspaces?
424+
const fsPath: string =
425+
folder === undefined
426+
? // no workspace opened
427+
""
428+
: // could use currently opened document to detect the correct
429+
// workspace. However, that would be determined by the document
430+
// user has opened on Editor startup. Could lead to
431+
// unpredictable workspace selection in practice.
432+
// It's better to pick the first one
433+
folder.uri.fsPath;
434+
return fsPath;
435435
};
436436
// https://code.visualstudio.com/docs/editor/variables-reference
437437
const supportedVariables: { [k: string]: () => string } = {
@@ -448,13 +448,17 @@ function computeVscodeVar(varName: string): string | null {
448448
// https://github.com/microsoft/vscode/blob/08ac1bb67ca2459496b272d8f4a908757f24f56f/src/vs/workbench/api/common/extHostVariableResolverService.ts#L81
449449
// or
450450
// https://github.com/microsoft/vscode/blob/29eb316bb9f154b7870eb5204ec7f2e7cf649bec/src/vs/server/node/remoteTerminalChannel.ts#L56
451-
execPath: () => process.env.VSCODE_EXEC_PATH ?? process.execPath,
451+
execPath: () => process.env["VSCODE_EXEC_PATH"] ?? process.execPath,
452452

453453
pathSeparator: () => path.sep,
454454
};
455455

456456
if (varName in supportedVariables) {
457-
return supportedVariables[varName]();
457+
const fn = expectNotUndefined(
458+
supportedVariables[varName],
459+
`${varName} should not be undefined here`
460+
);
461+
return fn();
458462
} else {
459463
// return "${" + varName + "}";
460464
return null;

editors/code/src/debug.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as ra from "./lsp_ext";
66
import { Cargo, getRustcId, getSysroot } from "./toolchain";
77
import { Ctx } from "./ctx";
88
import { prepareEnv } from "./run";
9+
import { unwrapUndefinable } from "./undefinable";
910

1011
const debugOutput = vscode.window.createOutputChannel("Debug");
1112
type DebugConfigProvider = (
@@ -105,12 +106,13 @@ async function getDebugConfiguration(
105106
const workspaceFolders = vscode.workspace.workspaceFolders!;
106107
const isMultiFolderWorkspace = workspaceFolders.length > 1;
107108
const firstWorkspace = workspaceFolders[0];
108-
const workspace =
109+
const maybeWorkspace =
109110
!isMultiFolderWorkspace || !runnable.args.workspaceRoot
110111
? firstWorkspace
111112
: workspaceFolders.find((w) => runnable.args.workspaceRoot?.includes(w.uri.fsPath)) ||
112113
firstWorkspace;
113114

115+
const workspace = unwrapUndefinable(maybeWorkspace);
114116
const wsFolder = path.normalize(workspace.uri.fsPath);
115117
const workspaceQualifier = isMultiFolderWorkspace ? `:${workspace.name}` : "";
116118
function simplifyPath(p: string): string {
@@ -130,12 +132,8 @@ async function getDebugConfiguration(
130132
sourceFileMap[`/rustc/${commitHash}/`] = rustlib;
131133
}
132134

133-
const debugConfig = knownEngines[debugEngine.id](
134-
runnable,
135-
simplifyPath(executable),
136-
env,
137-
sourceFileMap
138-
);
135+
const provider = unwrapUndefinable(knownEngines[debugEngine.id]);
136+
const debugConfig = provider(runnable, simplifyPath(executable), env, sourceFileMap);
139137
if (debugConfig.type in debugOptions.engineSettings) {
140138
const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
141139
for (var key in settingsMap) {
@@ -149,8 +147,9 @@ async function getDebugConfiguration(
149147
debugConfig.name = `run ${path.basename(executable)}`;
150148
}
151149

152-
if (debugConfig.cwd) {
153-
debugConfig.cwd = simplifyPath(debugConfig.cwd);
150+
const cwd = debugConfig["cwd"];
151+
if (cwd) {
152+
debugConfig["cwd"] = simplifyPath(cwd);
154153
}
155154

156155
return debugConfig;

editors/code/src/dependencies_provider.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as fs from "fs";
44
import { CtxInit } from "./ctx";
55
import * as ra from "./lsp_ext";
66
import { FetchDependencyListResult } from "./lsp_ext";
7+
import { unwrapUndefinable } from "./undefinable";
78

89
export class RustDependenciesProvider
910
implements vscode.TreeDataProvider<Dependency | DependencyFile>
@@ -49,7 +50,12 @@ export class RustDependenciesProvider
4950
}
5051

5152
getTreeItem(element: Dependency | DependencyFile): vscode.TreeItem | Thenable<vscode.TreeItem> {
52-
if (element.id! in this.dependenciesMap) return this.dependenciesMap[element.id!];
53+
const dependenciesMap = this.dependenciesMap;
54+
const elementId = element.id!;
55+
if (elementId in dependenciesMap) {
56+
const dependency = unwrapUndefinable(dependenciesMap[elementId]);
57+
return dependency;
58+
}
5359
return element;
5460
}
5561

editors/code/src/diagnostics.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as anser from "anser";
22
import * as vscode from "vscode";
33
import { ProviderResult, Range, TextEditorDecorationType, ThemeColor, window } from "vscode";
44
import { Ctx } from "./ctx";
5+
import { unwrapUndefinable } from "./undefinable";
56

67
export const URI_SCHEME = "rust-analyzer-diagnostics-view";
78

@@ -195,7 +196,8 @@ export class AnsiDecorationProvider implements vscode.Disposable {
195196
// anser won't return both the RGB and the color name at the same time,
196197
// so just fake a single foreground control char with the palette number:
197198
const spans = anser.ansiToJson(`\x1b[38;5;${paletteColor}m`);
198-
const rgb = spans[1].fg;
199+
const span = unwrapUndefinable(spans[1]);
200+
const rgb = span.fg;
199201

200202
if (rgb) {
201203
return `rgb(${rgb})`;

editors/code/src/nullable.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
export type NotNull<T> = T extends null ? never : T;
2+
3+
export type Nullable<T> = T | null;
4+
5+
function isNotNull<T>(input: Nullable<T>): input is NotNull<T> {
6+
return input !== null;
7+
}
8+
9+
function expectNotNull<T>(input: Nullable<T>, msg: string): NotNull<T> {
10+
if (isNotNull(input)) {
11+
return input;
12+
}
13+
14+
throw new TypeError(msg);
15+
}
16+
17+
export function unwrapNullable<T>(input: Nullable<T>): NotNull<T> {
18+
return expectNotNull(input, `unwrapping \`null\``);
19+
}

editors/code/src/run.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as tasks from "./tasks";
66
import { CtxInit } from "./ctx";
77
import { makeDebugConfig } from "./debug";
88
import { Config, RunnableEnvCfg } from "./config";
9+
import { unwrapUndefinable } from "./undefinable";
910

1011
const quickPickButtons = [
1112
{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configuration." },
@@ -68,12 +69,14 @@ export async function selectRunnable(
6869
quickPick.onDidHide(() => close()),
6970
quickPick.onDidAccept(() => close(quickPick.selectedItems[0])),
7071
quickPick.onDidTriggerButton(async (_button) => {
71-
await makeDebugConfig(ctx, quickPick.activeItems[0].runnable);
72+
const runnable = unwrapUndefinable(quickPick.activeItems[0]).runnable;
73+
await makeDebugConfig(ctx, runnable);
7274
close();
7375
}),
74-
quickPick.onDidChangeActive((active) => {
75-
if (showButtons && active.length > 0) {
76-
if (active[0].label.startsWith("cargo")) {
76+
quickPick.onDidChangeActive((activeList) => {
77+
if (showButtons && activeList.length > 0) {
78+
const active = unwrapUndefinable(activeList[0]);
79+
if (active.label.startsWith("cargo")) {
7780
// save button makes no sense for `cargo test` or `cargo check`
7881
quickPick.buttons = [];
7982
} else if (quickPick.buttons.length === 0) {

editors/code/src/snippets.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import * as vscode from "vscode";
22

33
import { assert } from "./util";
4+
import { unwrapUndefinable } from "./undefinable";
45

56
export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
67
if (edit.entries().length === 1) {
7-
const [uri, edits] = edit.entries()[0];
8+
const [uri, edits] = unwrapUndefinable(edit.entries()[0]);
89
const editor = await editorFromUri(uri);
910
if (editor) await applySnippetTextEdits(editor, edits);
1011
return;
@@ -68,7 +69,8 @@ export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vs
6869
});
6970
if (selections.length > 0) editor.selections = selections;
7071
if (selections.length === 1) {
71-
editor.revealRange(selections[0], vscode.TextEditorRevealType.InCenterIfOutsideViewport);
72+
const selection = unwrapUndefinable(selections[0]);
73+
editor.revealRange(selection, vscode.TextEditorRevealType.InCenterIfOutsideViewport);
7274
}
7375
}
7476

editors/code/src/tasks.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as vscode from "vscode";
22
import * as toolchain from "./toolchain";
33
import { Config } from "./config";
44
import { log } from "./util";
5+
import { unwrapUndefinable } from "./undefinable";
56

67
// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
78
// our configuration should be compatible with it so use the same key.
@@ -120,7 +121,8 @@ export async function buildCargoTask(
120121

121122
const fullCommand = [...cargoCommand, ...args];
122123

123-
exec = new vscode.ProcessExecution(fullCommand[0], fullCommand.slice(1), definition);
124+
const processName = unwrapUndefinable(fullCommand[0]);
125+
exec = new vscode.ProcessExecution(processName, fullCommand.slice(1), definition);
124126
}
125127

126128
return new vscode.Task(

0 commit comments

Comments
 (0)