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

Control flow based type analysis #8010

Merged
merged 70 commits into from
Apr 22, 2016
Merged
Changes from 1 commit
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
e67d15a
Initial implementation of control flow based type analysis
ahejlsberg Mar 22, 2016
afa1714
Add type annotations to suppress circularity errors
ahejlsberg Mar 22, 2016
7c45c7b
Fixing tests
ahejlsberg Mar 22, 2016
80c2e5e
Accepting new baselines
ahejlsberg Mar 22, 2016
33985b2
Adding a few optimizations
ahejlsberg Mar 24, 2016
ed5002c
Handle assignment of union types in getAssignmentReducedType
ahejlsberg Mar 25, 2016
6d25a42
Remove incorrect type predicate (could be true even when result is fa…
ahejlsberg Mar 25, 2016
bf78470
Fix overly aggressive optimization
ahejlsberg Mar 25, 2016
4f936c4
Add control flow tests
ivogabe Mar 25, 2016
7f02357
Merge pull request #7690 from ivogabe/controlFlowTypesTest
ahejlsberg Mar 25, 2016
9e965d4
Fix issues in analysis of do..while and for..in/for..of
ahejlsberg Mar 26, 2016
9de0a5d
Fix comment in test
ahejlsberg Mar 26, 2016
560bc3f
Accepting new baselines
ahejlsberg Mar 26, 2016
0820249
Fixing some tests
ahejlsberg Mar 26, 2016
5a5d89a
Accepting new baselines
ahejlsberg Mar 26, 2016
424074b
Use type {} for vacuous type guards / New getTypeWithFacts function
ahejlsberg Mar 30, 2016
c6f4de3
Remove unnecessary cast
ahejlsberg Mar 30, 2016
e53f390
Fix some tests
ahejlsberg Mar 30, 2016
a38d863
Accepting new baselines
ahejlsberg Mar 30, 2016
3d0fa31
Delete removeNullableKind, use getTypeWithFacts instead
ahejlsberg Mar 30, 2016
ce81ba5
Support unknown types (host object names) in typeof type guards
ahejlsberg Mar 31, 2016
354fd10
Separate error messages for 'null', 'undefined', or both.
ahejlsberg Apr 1, 2016
5179dd6
Flow analysis of &&, ||, and destructuring assignments
ahejlsberg Apr 8, 2016
019f5bd
Accepting new baselines
ahejlsberg Apr 8, 2016
f13c92f
Handle shorthand property assignments
ahejlsberg Apr 8, 2016
b03d087
Accepting new baselines
ahejlsberg Apr 8, 2016
7a32129
Support destructuring declarations in control flow analysis
ahejlsberg Apr 9, 2016
6fb9424
Accepting new baselines
ahejlsberg Apr 9, 2016
e45bac8
Adding test
ahejlsberg Apr 10, 2016
7dfcad6
Fixing fourslash tests
ahejlsberg Apr 10, 2016
92df029
Fixing tests
ahejlsberg Apr 10, 2016
32e6464
Accepting new baselines
ahejlsberg Apr 10, 2016
560e768
Fix linting errors
ahejlsberg Apr 10, 2016
b1e9f43
Merge branch 'master' into controlFlowTypes
ahejlsberg Apr 10, 2016
4c250d0
Accepting new baselines
ahejlsberg Apr 10, 2016
7c7a1c0
A few cosmetic changes
ahejlsberg Apr 12, 2016
df62fa0
Merge branch 'master' into controlFlowTypes
ahejlsberg Apr 12, 2016
586ac55
Fix finishFlow function and rename to finishFlowLabel
ahejlsberg Apr 12, 2016
cd88f1e
Adding regression test
ahejlsberg Apr 12, 2016
472ab7c
Accepting new baselines
ahejlsberg Apr 12, 2016
b689c07
Improving error reporting as suggested in code review
ahejlsberg Apr 13, 2016
1ed9871
Fix typo
ahejlsberg Apr 13, 2016
921efec
Improved handing of evolving types in iteration statements
ahejlsberg Apr 16, 2016
9aea708
Adding tests
ahejlsberg Apr 16, 2016
10889a0
Accepting new baselines
ahejlsberg Apr 16, 2016
2595f04
Removing unused properties
ahejlsberg Apr 17, 2016
b83dc88
Improve expression type caching to ensure consistent results
ahejlsberg Apr 18, 2016
538e22a
Adding tests
ahejlsberg Apr 18, 2016
b5104cb
Accepting new baselines
ahejlsberg Apr 18, 2016
87f55fa
Only evaluate assigned type when declared type is a union type
ahejlsberg Apr 18, 2016
9defdde
Accepting new baselines
ahejlsberg Apr 18, 2016
d28a4fe
typeof x === "function" type guards include Function interface
ahejlsberg Apr 18, 2016
d735b7a
Variables from different source files default to their declared type
ahejlsberg Apr 19, 2016
c8bf6d8
Variables from different module declarations default to their declare…
ahejlsberg Apr 19, 2016
ea96dfd
Support comma operator in type guards
ahejlsberg Apr 20, 2016
33e359f
Adding test
ahejlsberg Apr 20, 2016
bab8ef4
Accepting new baselines
ahejlsberg Apr 20, 2016
e9a7d3d
Removing unused logic
ahejlsberg Apr 20, 2016
a0101c0
Improve consistency of instanceof and user defined type guards
ahejlsberg Apr 21, 2016
729dfce
Fix incorrect user defined type guard function in compiler
ahejlsberg Apr 21, 2016
3045cf5
Add regression test
ahejlsberg Apr 21, 2016
06928b6
Accepting new baselines
ahejlsberg Apr 21, 2016
8a0bc3b
Support assignments in truthiness type guards
ahejlsberg Apr 21, 2016
d2b89be
Adding test
ahejlsberg Apr 21, 2016
ab4b039
Removing unused logic
ahejlsberg Apr 21, 2016
e12b2a7
Correct issue with exported variables in code flow analysis
ahejlsberg Apr 21, 2016
4fb31ac
Update fourslash test
ahejlsberg Apr 21, 2016
f06d3f6
Only narrow to {} in getNarrowedType when types are completely unrelated
ahejlsberg Apr 22, 2016
42e3fc4
Revert previous change
ahejlsberg Apr 22, 2016
0dee5ad
Accepting new baselines
ahejlsberg Apr 22, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Support destructuring declarations in control flow analysis
  • Loading branch information
ahejlsberg committed Apr 9, 2016
commit 7a321293bf43de3ac4ffbdf1281eb5886b623341
113 changes: 70 additions & 43 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7335,45 +7335,52 @@ namespace ts {
return firstType ? types ? getUnionType(types, /*noSubtypeReduction*/ true) : firstType : emptyUnionType;
}

function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type {
const type = checkExpressionCached(node.right);
const parent = node.parent;
if (parent.kind === SyntaxKind.ArrayLiteralExpression || parent.kind === SyntaxKind.PropertyAssignment) {
const assignedType = getAssignedType(node);
return getUnionType([getTypeWithFacts(assignedType, TypeFacts.NEUndefined), type]);
function getTypeWithDefault(type: Type, defaultExpression: Expression) {
if (defaultExpression) {
const defaultType = checkExpressionCached(defaultExpression);
return getUnionType([getTypeWithFacts(type, TypeFacts.NEUndefined), defaultType]);
}
return type;
}

function getTypeOfDestructuredProperty(type: Type, name: Identifier | LiteralExpression | ComputedPropertyName) {
const text = getTextOfPropertyName(name);
return getTypeOfPropertyOfType(type, text) ||
isNumericLiteralName(text) && getIndexTypeOfType(type, IndexKind.Number) ||
getIndexTypeOfType(type, IndexKind.String) ||
unknownType;
}

function getTypeOfDestructuredArrayElement(type: Type, index: number) {
return isTupleLikeType(type) && getTypeOfPropertyOfType(type, "" + index) ||
checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false) ||
unknownType;
}

function getTypeOfDestructuredSpreadElement(type: Type) {
return createArrayType(checkIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false) || unknownType);
}

function getAssignedTypeOfBinaryExpression(node: BinaryExpression): Type {
return node.parent.kind === SyntaxKind.ArrayLiteralExpression || node.parent.kind === SyntaxKind.PropertyAssignment ?
getTypeWithDefault(getAssignedType(node), node.right) :
checkExpressionCached(node.right);
}

function getAssignedTypeOfArrayLiteralElement(node: ArrayLiteralExpression, element: Expression): Type {
const arrayLikeType = getAssignedType(node);
const elementType = checkIteratedTypeOrElementType(arrayLikeType, node, /*allowStringInput*/ false);
const propName = "" + indexOf(node.elements, element);
return isTupleLikeType(arrayLikeType) && getTypeOfPropertyOfType(arrayLikeType, propName) || elementType;
return getTypeOfDestructuredArrayElement(getAssignedType(node), indexOf(node.elements, element));
}

function getAssignedTypeOfSpreadElement(node: SpreadElementExpression): Type {
const arrayLikeType = getAssignedType(<ArrayLiteralExpression>node.parent);
const elementType = checkIteratedTypeOrElementType(arrayLikeType, node, /*allowStringInput*/ false);
return createArrayType(elementType);
return getTypeOfDestructuredSpreadElement(getAssignedType(<ArrayLiteralExpression>node.parent));
}

function getAssignedTypeOfPropertyAssignment(node: PropertyAssignment | ShorthandPropertyAssignment): Type {
const objectType = getAssignedType(<ObjectLiteralExpression>node.parent);
const text = getTextOfPropertyName(node.name);
return getTypeOfPropertyOfType(objectType, text) ||
isNumericLiteralName(text) && getIndexTypeOfType(objectType, IndexKind.Number) ||
getIndexTypeOfType(objectType, IndexKind.String) ||
unknownType;
return getTypeOfDestructuredProperty(getAssignedType(<ObjectLiteralExpression>node.parent), node.name);
}

function getAssignedTypeOfShorthandPropertyAssignment(node: ShorthandPropertyAssignment): Type {
if (node.objectAssignmentInitializer) {
const defaultType = checkExpressionCached(node.objectAssignmentInitializer);
const assignedType = getAssignedTypeOfPropertyAssignment(node);
return getUnionType([getTypeWithFacts(assignedType, TypeFacts.NEUndefined), defaultType]);
}
return getAssignedTypeOfPropertyAssignment(node);
return getTypeWithDefault(getAssignedTypeOfPropertyAssignment(node), node.objectAssignmentInitializer);
}

function getAssignedType(node: Expression): Type {
Expand All @@ -7382,7 +7389,7 @@ namespace ts {
case SyntaxKind.ForInStatement:
return stringType;
case SyntaxKind.ForOfStatement:
return checkRightHandSideOfForOf((<ForOfStatement>parent).expression);
return checkRightHandSideOfForOf((<ForOfStatement>parent).expression) || unknownType;
case SyntaxKind.BinaryExpression:
return getAssignedTypeOfBinaryExpression(<BinaryExpression>parent);
case SyntaxKind.ArrayLiteralExpression:
Expand All @@ -7397,6 +7404,36 @@ namespace ts {
return unknownType;
}

function getInitialTypeOfBindingElement(node: BindingElement): Type {
const pattern = <BindingPattern>node.parent;
const parentType = getInitialType(<VariableDeclaration | BindingElement>pattern.parent);
const type = pattern.kind === SyntaxKind.ObjectBindingPattern ?
getTypeOfDestructuredProperty(parentType, node.propertyName || <Identifier>node.name) :
!node.dotDotDotToken ?
getTypeOfDestructuredArrayElement(parentType, indexOf(pattern.elements, node)) :
getTypeOfDestructuredSpreadElement(parentType);
return getTypeWithDefault(type, node.initializer);
}

function getInitialTypeOfVariableDeclaration(node: VariableDeclaration) {
if (node.initializer) {
return checkExpressionCached(node.initializer);
}
if (node.parent.parent.kind === SyntaxKind.ForInStatement) {
return stringType;
}
if (node.parent.parent.kind === SyntaxKind.ForOfStatement) {
return checkRightHandSideOfForOf((<ForOfStatement>node.parent.parent).expression) || unknownType;
}
return unknownType;
}

function getInitialType(node: VariableDeclaration | BindingElement) {
return node.kind === SyntaxKind.VariableDeclaration ?
getInitialTypeOfVariableDeclaration(<VariableDeclaration>node) :
getInitialTypeOfBindingElement(<BindingElement>node);
}

function getNarrowedTypeOfReference(type: Type, reference: Node) {
if (!(type.flags & TypeFlags.Narrowable) || !isNarrowableReference(reference)) {
return type;
Expand Down Expand Up @@ -7446,20 +7483,12 @@ namespace ts {
}
}

function getTypeAtVariableDeclaration(node: VariableDeclaration | BindingElement) {
if (reference.kind === SyntaxKind.Identifier && getResolvedSymbol(<Identifier>reference) === getSymbolOfNode(node)) {
if (node.initializer) {
return getAssignmentReducedType(declaredType, checkExpressionCached(node.initializer));
}
return declaredType; // !!! TODO
}
return undefined;
}

function getTypeAtFlowAssignment(flow: FlowAssignment) {
const node = flow.node;
if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) {
return getTypeAtVariableDeclaration(<VariableDeclaration | BindingElement>node);
if ((node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) &&
reference.kind === SyntaxKind.Identifier &&
getResolvedSymbol(<Identifier>reference) === getSymbolOfNode(node)) {
return getAssignmentReducedType(declaredType, getInitialType(<VariableDeclaration | BindingElement>node));
}
// If the node is not a variable declaration or binding element, it is an identifier
// or a dotted name that is the target of an assignment. If we have a match, reduce
Expand Down Expand Up @@ -14091,23 +14120,21 @@ namespace ts {
if (isTypeAny(inputType)) {
return inputType;
}

if (languageVersion >= ScriptTarget.ES6) {
return checkElementTypeOfIterable(inputType, errorNode);
}

if (allowStringInput) {
return checkElementTypeOfArrayOrString(inputType, errorNode);
}

if (isArrayLikeType(inputType)) {
const indexType = getIndexTypeOfType(inputType, IndexKind.Number);
if (indexType) {
return indexType;
}
}

error(errorNode, Diagnostics.Type_0_is_not_an_array_type, typeToString(inputType));
if (errorNode) {
error(errorNode, Diagnostics.Type_0_is_not_an_array_type, typeToString(inputType));
}
return unknownType;
}

Expand Down