Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable "strict" compiler option #178

Merged
merged 4 commits into from
Apr 26, 2021
Merged

Conversation

BigBahss
Copy link
Contributor

Closes #61.

Changes have been made to adhere to strict type checking:

  • Non-null assertions and definite-assignment assertions (! post-fix operator) have been added to please the compiler (these do not affect the compiled output). These assertions are not always strictly "safe", but are not any less safe than not having strict type checking at all. Plus, their presence in the code can be a signal that maybe the design should be changed to safely ensure that the types are not null/undefined.
  • Some optional chaining (?.) has been been added when properties can be null/undefined.
  • Some incorrect uses of null have been replaced with undefined.
  • Add defaultValue arguments to WorkspaceConfiguration.get() so that it will not return undefined. The default values being passed are the default values defined in the package.json. Realistically, the function should not return undefined when default values are defined in the package.json, but this pleases the compiler.

The changes I've made were aimed at not changing the behavior of the extension, and weren't necessarily aimed at fixing any potential bugs. Strict type checking will make it less likely to introduce new bugs in future changes.

src/ast.ts Outdated Show resolved Hide resolved
src/semantic-highlighting.ts Show resolved Hide resolved
Copy link
Member

@sam-mccall sam-mccall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this cleanup! And sorry if some of these comments are dumb, I'm not deeply familiar with TS's type system. I do think if we're going to enable strict mode then I should understand it though...

src/ast.ts Outdated Show resolved Hide resolved
src/ast.ts Outdated Show resolved Hide resolved
@@ -163,16 +164,16 @@ class TreeAdapter implements vscode.TreeDataProvider<ASTNode> {
return element.children || [];
}

public getParent(node: ASTNode): ASTNode {
public getParent(node: ASTNode): ASTNode|undefined {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a bunch of places in this class where you've changed null to undefined - is there some fundamental reason why null is incorrect? (It makes sense to be consistent within a module of course.

My understanding is this is mostly a stylistic choice, and my preference would be to use null for "deliberately" null values, but this isn't my primary language...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if I came across as pompous saying that null was "incorrect", that really wasn't the right word. There were inconsistent usages of null vs undefined which were causing type errors. I leaned towards undefined because generally it seems to be the preferred "bottom value" for typescript, or at least in terms of working with vscode's API's (Plus null has some weird properties like having typeof 'object'. But, it's definitely a preference kind-of-thing whether to use it or not). You're right though, null is used deliberately/explicitly. But, I would make the argument that unless the logic makes a distinction between a variable being undefined vs null, then it's probably a better idea to use one or the other consistently, rather than specifying that something can be SomeType | undefined | null.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries at all. I think if undefined is privileged by the ?. operators etc then ergonomics is a good reason to prefer it.
(And it seems so, playground says (null as any)?.foo is undefined)
And this seems to be MS's preference, and it's mostly their APIs we're dealing with.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. So just to confirm, are deciding to use undefined for values stored by the extension and accepting null for incoming values (when necessary)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me!

@@ -58,7 +58,7 @@ export class ClangdContext implements vscode.Disposable {
await config.getSecureOrPrompt<string[]>('arguments', workspaceState),
options: {cwd: vscode.workspace.rootPath || process.cwd()}
};
const traceFile = config.get<string>('trace');
const traceFile = config.get<string>('trace', '');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that echoing defaults already given in the package.json is unacceptable duplication (and causes practical problems e.g. someone changing the default in the wrong place).

Can we add a ! assertion in config.get() instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair. I believe the defaults can be retrieved through WorkspaceConfiguration.inspect(), but that may be more trouble than it's worth. Realistically adding asserts here would only cause a problem if a default value was removed from package.json, but I expect that's quite unlikely.

src/config.ts Outdated Show resolved Hide resolved
src/install.ts Outdated Show resolved Hide resolved
@@ -80,8 +80,8 @@ class MemoryUsageFeature implements vscodelc.StaticFeature {
class TreeAdapter implements vscode.TreeDataProvider<InternalTree> {
private root_?: InternalTree;

get root(): InternalTree|null { return this.root_; }
set root(n: InternalTree|null) {
get root(): InternalTree|undefined { return this.root_; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again why these null->undefined changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in a previous comment, but it's because private root_?: InternalTree is already declared as being InternalTree | undefined.

@@ -16,7 +16,7 @@
],
"sourceMap": true,
"rootDir": ".",
"alwaysStrict": true,
"strict": true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's surprising that "strict" is more strict than "alwaysStrict" :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it really seems like a "gcc -Wall" scenario haha.

Copy link
Contributor Author

@BigBahss BigBahss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed comments (further consensus is needed on the null vs undefined issue).

src/ast.ts Outdated Show resolved Hide resolved
src/ast.ts Outdated Show resolved Hide resolved
@@ -163,16 +164,16 @@ class TreeAdapter implements vscode.TreeDataProvider<ASTNode> {
return element.children || [];
}

public getParent(node: ASTNode): ASTNode {
public getParent(node: ASTNode): ASTNode|undefined {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if I came across as pompous saying that null was "incorrect", that really wasn't the right word. There were inconsistent usages of null vs undefined which were causing type errors. I leaned towards undefined because generally it seems to be the preferred "bottom value" for typescript, or at least in terms of working with vscode's API's (Plus null has some weird properties like having typeof 'object'. But, it's definitely a preference kind-of-thing whether to use it or not). You're right though, null is used deliberately/explicitly. But, I would make the argument that unless the logic makes a distinction between a variable being undefined vs null, then it's probably a better idea to use one or the other consistently, rather than specifying that something can be SomeType | undefined | null.

@@ -58,7 +58,7 @@ export class ClangdContext implements vscode.Disposable {
await config.getSecureOrPrompt<string[]>('arguments', workspaceState),
options: {cwd: vscode.workspace.rootPath || process.cwd()}
};
const traceFile = config.get<string>('trace');
const traceFile = config.get<string>('trace', '');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair. I believe the defaults can be retrieved through WorkspaceConfiguration.inspect(), but that may be more trouble than it's worth. Realistically adding asserts here would only cause a problem if a default value was removed from package.json, but I expect that's quite unlikely.

src/install.ts Outdated Show resolved Hide resolved
@@ -80,8 +80,8 @@ class MemoryUsageFeature implements vscodelc.StaticFeature {
class TreeAdapter implements vscode.TreeDataProvider<InternalTree> {
private root_?: InternalTree;

get root(): InternalTree|null { return this.root_; }
set root(n: InternalTree|null) {
get root(): InternalTree|undefined { return this.root_; }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in a previous comment, but it's because private root_?: InternalTree is already declared as being InternalTree | undefined.

@@ -16,7 +16,7 @@
],
"sourceMap": true,
"rootDir": ".",
"alwaysStrict": true,
"strict": true,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it really seems like a "gcc -Wall" scenario haha.

@BigBahss
Copy link
Contributor Author

I was going crazy there for a minute wondering how the CI was failing on a non-existent file when the tests passed on my machine 😅. Anyways I merged and modified the new file for strict type checking.

I fixed all the things that were mentioned. Additionally I changed all == and != to === and !== since that's very closely related to strict type checking. Let me know if I missed anything or whether you have further concerns.

Copy link
Member

@sam-mccall sam-mccall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot!

@sam-mccall sam-mccall merged commit 6ccdd94 into clangd:master Apr 26, 2021
@BigBahss
Copy link
Contributor Author

Happy to help 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Should build with --strict
2 participants