-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Error when assertion function calls aren't CFA'd #33622
Conversation
@typescript-bot run dt |
Heya @ahejlsberg, I've started to run the parallelized Definitely Typed test suite on this PR at a13f862. You can monitor the build here. It should now contribute to this PR's status checks. |
Heya @ahejlsberg, I've started to run the extended test suite on this PR at a13f862. You can monitor the build here. It should now contribute to this PR's status checks. |
@typescript-bot user test this |
Heya @DanielRosenwasser, I've started to run the tarball bundle task on this PR at a13f862. You can monitor the build here. It should now contribute to this PR's status checks. |
Heya @DanielRosenwasser, I've started to run the parallelized community code test suite on this PR at a13f862. You can monitor the build here. It should now contribute to this PR's status checks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these error messages are very confusing, and we need to do as much as we can to keep it short and make it actionable.
src/compiler/diagnosticMessages.json
Outdated
@@ -2726,6 +2726,14 @@ | |||
"category": "Error", | |||
"code": 2774 | |||
}, | |||
"Control flow effects of calls to assertion and never-returning functions are reflected only when every variable or property referenced in the function expression is declared with an explicit type annotation.": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know what this is telling a user. Is it something like the following?
This assertion call cannot be properly analyzed because not every variable and property used in the current function has an explicit type annotation.
This 'never'-returning function call cannot be properly analyzed because not every variable and property used in the current function has an explicit type annotation.
Along with
This assertion call cannot be properly analyzed because the function being called is not a qualified name.
This 'never'-returning function call cannot be properly analyzed because the function being called is not a qualified name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I think that "explicitly never-returning function" is really just another kind of assertion. We could introduce the concept of guard = "explicitly never-returning function" if it's too ambiguous.
- Mentioning control flow isn't necessary for fixing the error -- and presumably most people understand the concept of assertion better than control flow anyway.
- As for @DanielRosenwasser's wording: "This assertion call" is implied by the error span. The fact that it's an assertion is implied by the error span+error wording. So neither of those is necessary. Wording this categorically also allows us to flip the message back to positive.
Assertions must be an identifier or qualified name.
Assertions must reference a declaration with an explicit type annotation.
Related: This is the declaration that should be annotated.
- We know that the expression has type annotations because
hasTypePredicateOrNeverReturnType(signature)
is true. - If we want to handle the case where there is more than one declaration, the message can be
Assertions must reference declaration(s) with an explicit type annotation.
Related: This is one declaration that must be annotated.
Related: This is one declaration that must be annotated.
:
:
const assert = (value: unknown): asserts value => {} | ||
assert(typeof x === "string"); // Error | ||
~~~~~~ | ||
!!! error TS2775: Control flow effects of calls to assertion and never-returning functions are reflected only when every variable or property referenced in the function expression is declared with an explicit type annotation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which variable doesn't have an annotation? Can you give a related span?
The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master. |
A couple of candidate messages. assert1(typeof x === "string"); // Error
// Assertion calls must be explicitly typed.
// Identifier 'assert1' lacks an explicit type annotation and is not subject to control flow analysis. getAssert()(typeof x === "string"); // Error
// Assertion calls must be explicitly resolved.
// Function expression is not an identifier or qualified-name and is not subject to control flow analysis. |
src/compiler/diagnosticMessages.json
Outdated
@@ -2726,6 +2726,14 @@ | |||
"category": "Error", | |||
"code": 2774 | |||
}, | |||
"Control flow effects of calls to assertion and never-returning functions are reflected only when every variable or property referenced in the function expression is declared with an explicit type annotation.": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I think that "explicitly never-returning function" is really just another kind of assertion. We could introduce the concept of guard = "explicitly never-returning function" if it's too ambiguous.
- Mentioning control flow isn't necessary for fixing the error -- and presumably most people understand the concept of assertion better than control flow anyway.
- As for @DanielRosenwasser's wording: "This assertion call" is implied by the error span. The fact that it's an assertion is implied by the error span+error wording. So neither of those is necessary. Wording this categorically also allows us to flip the message back to positive.
Assertions must be an identifier or qualified name.
Assertions must reference a declaration with an explicit type annotation.
Related: This is the declaration that should be annotated.
- We know that the expression has type annotations because
hasTypePredicateOrNeverReturnType(signature)
is true. - If we want to handle the case where there is more than one declaration, the message can be
Assertions must reference declaration(s) with an explicit type annotation.
Related: This is one declaration that must be annotated.
Related: This is one declaration that must be annotated.
:
:
Oh, I didn't read @jack-williams comments until after commenting. Basically, what he said. =) |
Agreed!
Yes and no - I find it easier to just have the error message tell me what's wrong rather than reconstructing the intent from the span as well.
The suggestions both of you have are much more digestible, and made it clear to me exactly what the error check is. I only have nits at this point. :) What do you think of:
and
? |
No, I like the shorter wording better. The longer message only improves the precision of the wording, but won't help people (especially naive people) track down the problem.
|
assert(typeof x === "string"); // Error | ||
~~~~~~ | ||
!!! error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation. | ||
!!! related TS2728 tests/cases/conformance/controlFlow/assertionTypePredicates1.ts:128:11: 'assert' is declared here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'{0}' must be annotated
would be a lot nicer 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would, but do we really want yet another diagnostic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you already merged this, but for the future, diagnostics are cheap! They bear very little overhead for the <20 of us on the core team compared to at least hundreds of thousands of TypeScript users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed in the team room and agreed that the existing message is fine, so I didn't change it.
Latest commits exclude never-returning functions from check that they match CFA requirements and improve error messages, per discussion in today's design meeting. |
@@ -4062,6 +4062,12 @@ namespace ts { | |||
return node.kind === SyntaxKind.Identifier || isPropertyAccessEntityNameExpression(node); | |||
} | |||
|
|||
export function isDottedName(node: Expression): boolean { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, will this need to be updated for optional chains? Should a t1?.assert(b)
be a thing?
Strangely, with 3.8-beta (or 3.7-stable), this will trigger the export const assert = (value: unknown, message?: string): asserts value => {
if (!value) {
throw new AssertionError({message});
}
}; While this does work properly: export const assert: (value: unknown, message?: string) => asserts value = (value: unknown, message?: string) => {
if (!value) {
throw new AssertionError({message});
}
}; Is this working as intended? |
With this PR we issue errors when calls to assertion functions are written in a manner that isn't recognized by control flow analysis (see #32695 for the specific requirements). For example:
The reported errors are:
t.ts(7,5): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation.
t.ts(11,5): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation.
t.ts(15,5): error TS2776: Assertions require the call target to be an identifier or qualified name.
Fixes #33580.