Skip to content

Commit 6ccdd94

Browse files
authored
Enable "strict" compiler option (clangd#178)
* Enable "strict" compiler option * Address comments and change double-equals to triple-equals * Modify open-config.ts to adhere to strict type checking
1 parent 9af9a40 commit 6ccdd94

12 files changed

+103
-103
lines changed

src/ast.ts

+19-17
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ class ASTFeature implements vscodelc.StaticFeature {
3939
// clangd.ast.hasData controls the view visibility (package.json).
4040
adapter.onDidChangeTreeData((_) => {
4141
vscode.commands.executeCommand('setContext', 'clangd.ast.hasData',
42-
adapter.hasRoot())
43-
// Show the AST tree even if it's beet collapsed or closed.
42+
adapter.hasRoot());
43+
// Work around https://github.com/microsoft/vscode/issues/90005
44+
// Show the AST tree even if it's been collapsed or closed.
4445
// reveal(root) fails here: "Data tree node not found".
4546
if (adapter.hasRoot())
46-
tree.reveal(null);
47+
// @ts-ignore
48+
tree.reveal(null);
4749
}),
4850
vscode.window.registerTreeDataProvider('clangd.ast', adapter),
4951
// Create the "Show AST" command for the context menu.
@@ -61,12 +63,12 @@ class ASTFeature implements vscodelc.StaticFeature {
6163
if (!item)
6264
vscode.window.showInformationMessage(
6365
'No AST node at selection');
64-
adapter.setRoot(item, editor.document.uri);
66+
adapter.setRoot(item ?? undefined, editor.document.uri);
6567
}),
6668
// Clicking "close" will empty the adapter, which in turn hides the
6769
// view.
68-
vscode.commands.registerCommand('clangd.ast.close',
69-
() => adapter.setRoot(null, null)));
70+
vscode.commands.registerCommand(
71+
'clangd.ast.close', () => adapter.setRoot(undefined, undefined)));
7072
}
7173

7274
fillClientCapabilities(capabilities: vscodelc.ClientCapabilities) {}
@@ -109,8 +111,8 @@ const KindIcons: {[type: string]: string} = {
109111
function describe(role: string, kind: string): string {
110112
// For common roles where the kind is fairly self-explanatory, we don't
111113
// include it. e.g. "Call" rather than "Call expression".
112-
if (role == 'expression' || role == 'statement' || role == 'declaration' ||
113-
role == 'template name')
114+
if (role === 'expression' || role === 'statement' || role === 'declaration' ||
115+
role === 'template name')
114116
return kind;
115117
return kind + ' ' + role;
116118
}
@@ -120,9 +122,9 @@ class TreeAdapter implements vscode.TreeDataProvider<ASTNode> {
120122
private root?: ASTNode;
121123
private doc?: vscode.Uri;
122124

123-
hasRoot(): boolean { return this.root != null; }
125+
hasRoot(): boolean { return this.root !== undefined; }
124126

125-
setRoot(newRoot: ASTNode|null, newDoc: vscode.Uri|null) {
127+
setRoot(newRoot: ASTNode|undefined, newDoc: vscode.Uri|undefined) {
126128
this.root = newRoot;
127129
this.doc = newDoc;
128130
this._onDidChangeTreeData.fire(/*root changed*/ null);
@@ -163,16 +165,16 @@ class TreeAdapter implements vscode.TreeDataProvider<ASTNode> {
163165
return element.children || [];
164166
}
165167

166-
public getParent(node: ASTNode): ASTNode {
167-
if (node == this.root)
168-
return null;
169-
function findUnder(parent: ASTNode): ASTNode|null {
170-
for (const child of parent.children || []) {
171-
const result = (node == child) ? parent : findUnder(child);
168+
public getParent(node: ASTNode): ASTNode|undefined {
169+
if (node === this.root)
170+
return undefined;
171+
function findUnder(parent: ASTNode|undefined): ASTNode|undefined {
172+
for (const child of parent?.children ?? []) {
173+
const result = (node === child) ? parent : findUnder(child);
172174
if (result)
173175
return result;
174176
}
175-
return null;
177+
return undefined;
176178
}
177179
return findUnder(this.root);
178180
}

src/clangd-context.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@ class EnableEditsNearCursorFeature implements vscodelc.StaticFeature {
3636
initialize() {}
3737
fillClientCapabilities(capabilities: vscodelc.ClientCapabilities): void {
3838
const extendedCompletionCapabilities: any =
39-
capabilities.textDocument.completion;
39+
capabilities.textDocument?.completion;
4040
extendedCompletionCapabilities.editsNearCursor = true;
4141
}
4242
dispose() {}
4343
}
4444

4545
export class ClangdContext implements vscode.Disposable {
4646
subscriptions: vscode.Disposable[] = [];
47-
client: ClangdLanguageClient;
47+
client!: ClangdLanguageClient;
4848

4949
async activate(globalStoragePath: string, outputChannel: vscode.OutputChannel,
5050
workspaceState: vscode.Memento) {
@@ -104,7 +104,7 @@ export class ClangdContext implements vscode.Disposable {
104104
let list = await next(document, position, context, token);
105105
if (!config.get<boolean>('serverCompletionRanking'))
106106
return list;
107-
let items = (Array.isArray(list) ? list : list.items).map(item => {
107+
let items = (Array.isArray(list) ? list : list!.items).map(item => {
108108
// Gets the prefix used by VSCode when doing fuzzymatch.
109109
let prefix = document.getText(
110110
new vscode.Range((item.range as vscode.Range).start, position))
@@ -121,7 +121,7 @@ export class ClangdContext implements vscode.Disposable {
121121
// qualified symbols.
122122
provideWorkspaceSymbols: async (query, token, next) => {
123123
let symbols = await next(query, token);
124-
return symbols.map(symbol => {
124+
return symbols?.map(symbol => {
125125
// Only make this adjustment if the query is in fact qualified.
126126
// Otherwise, we get a suboptimal ordering of results because
127127
// including the name's qualifier (if it has one) in symbol.name

src/config-file-watcher.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {ClangdContext} from './clangd-context';
55
import * as config from './config';
66

77
export function activate(context: ClangdContext) {
8-
if (config.get<string>('onConfigChanged') != 'ignore') {
8+
if (config.get<string>('onConfigChanged') !== 'ignore') {
99
context.client.registerFeature(new ConfigFileWatcherFeature(context));
1010
}
1111
}
@@ -30,8 +30,8 @@ class ConfigFileWatcherFeature implements vscodelc.StaticFeature {
3030
}
3131

3232
class ConfigFileWatcher implements vscode.Disposable {
33-
private databaseWatcher: vscode.FileSystemWatcher = undefined;
34-
private debounceTimer: NodeJS.Timer = undefined;
33+
private databaseWatcher?: vscode.FileSystemWatcher;
34+
private debounceTimer?: NodeJS.Timer;
3535

3636
dispose() {
3737
if (this.databaseWatcher)

src/config.ts

+22-22
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as vscode from 'vscode';
33

44
// Gets the config value `clangd.<key>`. Applies ${variable} substitutions.
55
export function get<T>(key: string): T {
6-
return substitute(vscode.workspace.getConfiguration('clangd').get(key));
6+
return substitute(vscode.workspace.getConfiguration('clangd').get<T>(key)!);
77
}
88

99
// Like get(), but won't load settings from workspace config unless the user has
@@ -21,11 +21,11 @@ export async function getSecureOrPrompt<T>(
2121
const prop = new SecureProperty<T>(key, workspaceState);
2222
// Common case: value not overridden in workspace.
2323
if (!prop.mismatched)
24-
return prop.get(false);
24+
return prop.get(false)!;
2525
// Check whether user has blessed or blocked this value.
2626
const blessed = prop.blessed;
27-
if (blessed !== null)
28-
return prop.get(blessed);
27+
if (blessed !== undefined)
28+
return prop.get(blessed)!;
2929
// No cached decision for this value, ask the user.
3030
const Yes = 'Yes, use this setting', No = 'No, use my default',
3131
Info = 'More Info'
@@ -40,11 +40,11 @@ export async function getSecureOrPrompt<T>(
4040
break;
4141
case Yes:
4242
await prop.bless(true);
43-
return prop.get(true);
43+
return prop.get(true)!;
4444
case No:
4545
await prop.bless(false);
4646
}
47-
return prop.get(false);
47+
return prop.get(false)!;
4848
}
4949

5050
// Sets the config value `clangd.<key>`. Does not apply substitutions.
@@ -55,15 +55,14 @@ export function update<T>(key: string, value: T,
5555

5656
// Traverse a JSON value, replacing placeholders in all strings.
5757
function substitute<T>(val: T): T {
58-
if (typeof val == 'string') {
58+
if (typeof val === 'string') {
5959
val = val.replace(/\$\{(.*?)\}/g, (match, name) => {
60-
const rep = replacement(name);
6160
// If there's no replacement available, keep the placeholder.
62-
return (rep === null) ? match : rep;
61+
return replacement(name) ?? match;
6362
}) as unknown as T;
6463
} else if (Array.isArray(val))
6564
val = val.map((x) => substitute(x)) as unknown as T;
66-
else if (typeof val == 'object') {
65+
else if (typeof val === 'object') {
6766
// Substitute values but not keys, so we don't deal with collisions.
6867
const result = {} as {[k: string]: any};
6968
for (let [k, v] of Object.entries(val))
@@ -75,8 +74,9 @@ function substitute<T>(val: T): T {
7574

7675
// Subset of substitution variables that are most likely to be useful.
7776
// https://code.visualstudio.com/docs/editor/variables-reference
78-
function replacement(name: string): string|null {
79-
if (name == 'workspaceRoot' || name == 'workspaceFolder' || name == 'cwd') {
77+
function replacement(name: string): string|undefined {
78+
if (name === 'workspaceRoot' || name === 'workspaceFolder' ||
79+
name === 'cwd') {
8080
if (vscode.workspace.rootPath !== undefined)
8181
return vscode.workspace.rootPath;
8282
if (vscode.window.activeTextEditor !== undefined)
@@ -85,15 +85,15 @@ function replacement(name: string): string|null {
8585
}
8686
const envPrefix = 'env:';
8787
if (name.startsWith(envPrefix))
88-
return process.env[name.substr(envPrefix.length)] || '';
88+
return process.env[name.substr(envPrefix.length)] ?? '';
8989
const configPrefix = 'config:';
9090
if (name.startsWith(configPrefix)) {
9191
const config = vscode.workspace.getConfiguration().get(
9292
name.substr(configPrefix.length));
93-
return (typeof config == 'string') ? config : null;
93+
return (typeof config === 'string') ? config : undefined;
9494
}
9595

96-
return null;
96+
return undefined;
9797
}
9898

9999
// Caches a user's decision about whether to respect a workspace override of a
@@ -106,32 +106,32 @@ interface BlessCache {
106106

107107
// Common logic between getSecure and getSecureOrPrompt.
108108
class SecureProperty<T> {
109-
secure: T|undefined;
110-
insecure: T|undefined;
109+
secure?: T;
110+
insecure?: T;
111111
public secureJSON: string;
112112
public insecureJSON: string;
113113
blessKey: string;
114114

115115
constructor(key: string, private workspaceState: vscode.Memento) {
116116
const cfg = vscode.workspace.getConfiguration('clangd');
117-
const inspect = cfg.inspect<T>(key);
117+
const inspect = cfg.inspect<T>(key)!;
118118
this.secure = inspect.globalValue ?? inspect.defaultValue;
119119
this.insecure = cfg.get<T>(key);
120120
this.secureJSON = JSON.stringify(this.secure);
121121
this.insecureJSON = JSON.stringify(this.insecure);
122122
this.blessKey = 'bless.' + key;
123123
}
124124

125-
get mismatched(): boolean { return this.secureJSON != this.insecureJSON; }
125+
get mismatched(): boolean { return this.secureJSON !== this.insecureJSON; }
126126

127127
get(trusted: boolean): T|undefined {
128128
return substitute(trusted ? this.insecure : this.secure);
129129
}
130130

131-
get blessed(): boolean|null {
131+
get blessed(): boolean|undefined {
132132
let cache = this.workspaceState.get<BlessCache>(this.blessKey);
133-
if (!cache || cache.json != this.insecureJSON)
134-
return null;
133+
if (!cache || cache.json !== this.insecureJSON)
134+
return undefined;
135135
return cache.allowed;
136136
}
137137

src/file-status.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ export function activate(context: ClangdContext) {
1111
context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(
1212
() => { status.updateStatus(); }));
1313
context.subscriptions.push(context.client.onDidChangeState(({newState}) => {
14-
if (newState == vscodelc.State.Running) {
14+
if (newState === vscodelc.State.Running) {
1515
// clangd starts or restarts after crash.
1616
context.client.onNotification(
1717
'textDocument/clangd.fileStatus',
1818
(fileStatus) => { status.onFileUpdated(fileStatus); });
19-
} else if (newState == vscodelc.State.Stopped) {
19+
} else if (newState === vscodelc.State.Stopped) {
2020
// Clear all cached statuses when clangd crashes.
2121
status.clear();
2222
}
@@ -39,11 +39,11 @@ class FileStatus {
3939
}
4040

4141
updateStatus() {
42-
const activeDoc = vscode.window.activeTextEditor.document;
42+
const activeDoc = vscode.window.activeTextEditor?.document;
4343
// Work around https://github.com/microsoft/vscode/issues/58869
4444
// Don't hide the status when activeTextEditor is output panel.
4545
// This aligns with the behavior of other panels, e.g. problems.
46-
if (activeDoc.uri.scheme == 'output')
46+
if (!activeDoc || activeDoc.uri.scheme === 'output')
4747
return;
4848
const status = this.statuses.get(activeDoc.fileName);
4949
if (!status) {

src/install.ts

+10-11
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import * as config from './config';
1212
// Returns the clangd path to be used, or null if clangd is not installed.
1313
export async function activate(
1414
context: ClangdContext, globalStoragePath: string,
15-
workspaceState: vscode.Memento): Promise<string> {
15+
workspaceState: vscode.Memento): Promise<string|null> {
1616
// If the workspace overrides clangd.path, give the user a chance to bless it.
1717
await config.getSecureOrPrompt<string>('path', workspaceState);
1818

@@ -46,7 +46,7 @@ class UI {
4646
const opts = {
4747
location: vscode.ProgressLocation.Notification,
4848
title: title,
49-
cancellable: cancel != null,
49+
cancellable: cancel !== null,
5050
};
5151
const result = vscode.window.withProgress(opts, async (progress, canc) => {
5252
if (cancel)
@@ -68,16 +68,16 @@ class UI {
6868
vscode.commands.registerCommand(name, body));
6969
}
7070

71-
async shouldReuse(release: string): Promise<boolean> {
71+
async shouldReuse(release: string): Promise<boolean|undefined> {
7272
const message = `clangd ${release} is already installed!`;
7373
const use = 'Use the installed version';
7474
const reinstall = 'Delete it and reinstall';
7575
const response =
7676
await vscode.window.showInformationMessage(message, use, reinstall);
77-
if (response == use) {
77+
if (response === use) {
7878
// Find clangd within the existing directory.
7979
return true;
80-
} else if (response == reinstall) {
80+
} else if (response === reinstall) {
8181
// Remove the existing installation.
8282
return false;
8383
} else {
@@ -103,11 +103,10 @@ class UI {
103103
const update = `Install clangd ${newVersion}`;
104104
const dontCheck = 'Don\'t ask again';
105105
const response =
106-
await vscode.window.showInformationMessage(message, update, dontCheck)
107-
if (response == update) {
106+
await vscode.window.showInformationMessage(message, update, dontCheck);
107+
if (response === update) {
108108
common.installLatest(this);
109-
}
110-
else if (response == dontCheck) {
109+
} else if (response === dontCheck) {
111110
config.update('checkUpdates', false, vscode.ConfigurationTarget.Global);
112111
}
113112
}
@@ -126,10 +125,10 @@ class UI {
126125
}
127126

128127
get clangdPath(): string {
129-
let p = config.getSecure<string>('path', this.workspaceState);
128+
let p = config.getSecure<string>('path', this.workspaceState)!;
130129
// Backwards compatibility: if it's a relative path with a slash, interpret
131130
// relative to project root.
132-
if (!path.isAbsolute(p) && p.indexOf(path.sep) != -1 &&
131+
if (!path.isAbsolute(p) && p.includes(path.sep) &&
133132
vscode.workspace.rootPath !== undefined)
134133
p = path.join(vscode.workspace.rootPath, p);
135134
return p;

src/memory-usage.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ function convert(m: WireTree, title: string): InternalTree {
5151
class MemoryUsageFeature implements vscodelc.StaticFeature {
5252
constructor(private context: ClangdContext) {
5353
const adapter = new TreeAdapter();
54-
adapter.onDidChangeTreeData(
55-
(e) => vscode.commands.executeCommand(
56-
'setContext', 'clangd.memoryUsage.hasData', adapter.root != null));
54+
adapter.onDidChangeTreeData((e) => vscode.commands.executeCommand(
55+
'setContext', 'clangd.memoryUsage.hasData',
56+
adapter.root !== undefined));
5757
this.context.subscriptions.push(
5858
vscode.window.registerTreeDataProvider('clangd.memoryUsage', adapter));
5959
this.context.subscriptions.push(
@@ -63,7 +63,7 @@ class MemoryUsageFeature implements vscodelc.StaticFeature {
6363
adapter.root = convert(usage, '<root>');
6464
}));
6565
this.context.subscriptions.push(vscode.commands.registerCommand(
66-
'clangd.memoryUsage.close', () => adapter.root = null));
66+
'clangd.memoryUsage.close', () => adapter.root = undefined));
6767
}
6868

6969
fillClientCapabilities(capabilities: vscodelc.ClientCapabilities) {}
@@ -80,8 +80,8 @@ class MemoryUsageFeature implements vscodelc.StaticFeature {
8080
class TreeAdapter implements vscode.TreeDataProvider<InternalTree> {
8181
private root_?: InternalTree;
8282

83-
get root(): InternalTree|null { return this.root_; }
84-
set root(n: InternalTree|null) {
83+
get root(): InternalTree|undefined { return this.root_; }
84+
set root(n: InternalTree|undefined) {
8585
this.root_ = n;
8686
this._onDidChangeTreeData.fire(/*root changed*/ null);
8787
}

0 commit comments

Comments
 (0)