-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Assertions in control flow analysis #32695
Changes from 4 commits
e89acb6
77f2a41
1f5bb97
fe70a62
1c55e5d
df02ad6
99ab53e
d5e08d4
19f1d3b
83212e7
259ba77
cdeddf1
9791f1d
0599f84
e7cbfc4
2c36249
2152874
8791b62
5a180ba
436339d
a9336ba
971b0df
3749de6
3a89c8c
cc6e493
0060964
51dcce2
59b76ce
945babb
d26afd7
05d1e68
e97ebb7
def5e37
6d6c620
d9c9129
282a7af
8cbf694
9466025
ba30fdc
cafec55
21b5418
97d69d4
c3dcc37
bcdf33d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -706,6 +706,9 @@ namespace ts { | |
case SyntaxKind.CaseClause: | ||
bindCaseClause(<CaseClause>node); | ||
break; | ||
case SyntaxKind.ExpressionStatement: | ||
bindExpressionStatement(<ExpressionStatement>node); | ||
break; | ||
case SyntaxKind.LabeledStatement: | ||
bindLabeledStatement(<LabeledStatement>node); | ||
break; | ||
|
@@ -896,6 +899,11 @@ namespace ts { | |
return flowNodeCreated({ flags: FlowFlags.Assignment, antecedent, node }); | ||
} | ||
|
||
function createFlowCall(antecedent: FlowNode, node: CallExpression): FlowNode { | ||
setFlowNodeReferenced(antecedent); | ||
return flowNodeCreated({ flags: FlowFlags.Call, antecedent, node }); | ||
} | ||
|
||
function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode { | ||
setFlowNodeReferenced(antecedent); | ||
const res: FlowArrayMutation = flowNodeCreated({ flags: FlowFlags.ArrayMutation, antecedent, node }); | ||
|
@@ -1276,6 +1284,22 @@ namespace ts { | |
activeLabels!.pop(); | ||
} | ||
|
||
function isDottedName(node: Expression): boolean { | ||
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((<PropertyAccessExpression>node).expression); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason not to include There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that would be okay, but I'll have to convince myself it can't trigger circularities in control flow analysis. |
||
} | ||
|
||
function bindExpressionStatement(node: ExpressionStatement): void { | ||
bind(node.expression); | ||
// A top level call expression with a dotted function name and at least one argument | ||
// is potentially an assertion and is therefore included in the control flow. | ||
if (node.expression.kind === SyntaxKind.CallExpression) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before I forget: Even if we don't support them, we'll need tests for our behavior of:
|
||
const call = <CallExpression>node.expression; | ||
if (isDottedName(call.expression) && call.arguments.length >= 1) { | ||
currentFlow = createFlowCall(currentFlow, call); | ||
} | ||
} | ||
} | ||
|
||
function bindLabeledStatement(node: LabeledStatement): void { | ||
const preStatementLabel = createLoopLabel(); | ||
const postStatementLabel = createBranchLabel(); | ||
|
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -667,17 +667,18 @@ namespace ts { | |
return <KeywordTypeNode>createSynthesizedNode(kind); | ||
} | ||
|
||
export function createTypePredicateNode(parameterName: Identifier | ThisTypeNode | string, type: TypeNode) { | ||
export function createTypePredicateNode(assertsModifier: AssertsToken | undefined, parameterName: Identifier | ThisTypeNode | string, type: TypeNode | undefined) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a breaking API change. |
||
const node = createSynthesizedNode(SyntaxKind.TypePredicate) as TypePredicateNode; | ||
node.assertsModifier = assertsModifier; | ||
node.parameterName = asName(parameterName); | ||
node.type = type; | ||
return node; | ||
} | ||
|
||
export function updateTypePredicateNode(node: TypePredicateNode, parameterName: Identifier | ThisTypeNode, type: TypeNode) { | ||
export function updateTypePredicateNode(node: TypePredicateNode, assertsModifier: AssertsToken | undefined, parameterName: Identifier | ThisTypeNode, type: TypeNode | undefined) { | ||
return node.parameterName !== parameterName | ||
|| node.type !== type | ||
? updateNode(createTypePredicateNode(parameterName, type), node) | ||
? updateNode(createTypePredicateNode(assertsModifier, parameterName, type), node) | ||
: node; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2992,6 +2992,8 @@ namespace ts { | |
return parseParenthesizedType(); | ||
case SyntaxKind.ImportKeyword: | ||
return parseImportType(); | ||
case SyntaxKind.AssertsKeyword: | ||
return lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine) ? parseAssertsTypePredicate() : parseTypeReference(); | ||
default: | ||
return parseTypeReference(); | ||
} | ||
|
@@ -3032,6 +3034,7 @@ namespace ts { | |
case SyntaxKind.DotDotDotToken: | ||
case SyntaxKind.InferKeyword: | ||
case SyntaxKind.ImportKeyword: | ||
case SyntaxKind.AssertsKeyword: | ||
return true; | ||
case SyntaxKind.FunctionKeyword: | ||
return !inStartOfParameter; | ||
|
@@ -3225,6 +3228,16 @@ namespace ts { | |
} | ||
} | ||
|
||
function parseAssertsTypePredicate(): TypeNode { | ||
const node = <TypePredicateNode>createNode(SyntaxKind.TypePredicate); | ||
node.assertsModifier = parseExpectedToken(SyntaxKind.AssertsKeyword); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. adding this property here and not assigning it in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a possibility that there will be more modifiers in the future? If so, would it make sense to put this into There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's possible, but for now I'm going to keep it the way it is. |
||
node.parameterName = parseIdentifier(); | ||
if (parseOptional(SyntaxKind.IsKeyword)) { | ||
node.type = parseType(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes this type of object polymorphic. Could you instead always assign the property and use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup |
||
} | ||
return finishNode(node); | ||
} | ||
|
||
function parseType(): TypeNode { | ||
// The rules about 'yield' only apply to actual code/expression contexts. They don't | ||
// apply to 'type' contexts. So we disable these parameters here before moving on. | ||
|
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.
what's the difference to
isEntityNameExpression
?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.
Good catch! No difference, will change to use the existing function.