8000 Control flow analysis for implicit any variables by ahejlsberg · Pull Request #11263 · microsoft/TypeScript · GitHub
[go: up one dir, main page]

Skip to content

Control flow analysis for implicit any variables #11263

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

Merged
merged 16 commits into from
Oct 6, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Use control flow analysis for let/var with no type annotation or init…
…ializer
  • Loading branch information
ahejlsberg committed Sep 21, 2016
commit 864ee1cfc550d3887554461c1d1c4059e9927faa
31 changes: 25 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ namespace ts {
const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");

const anyType = createIntrinsicType(TypeFlags.Any, "any");
const autoType = createIntrinsicType(TypeFlags.Any, "any");
const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
Expand Down Expand Up @@ -3086,6 +3087,13 @@ namespace ts {
return addOptionality(getTypeFromTypeNode(declaration.type), /*optional*/ declaration.questionToken && includeOptionality);
}

// Use control flow type inference for non-ambient, non-exported var or let variables with no initializer
if (declaration.kind === SyntaxKind.VariableDeclaration && !isBindingPattern(declaration.name) &&
!(getCombinedNodeFlags(declaration) & NodeFlags.Const) && !(getCombinedModifierFlags(declaration) & ModifierFlags.Export) &&
!declaration.initializer && !isInAmbientContext(declaration)) {
return autoType;
}

if (declaration.kind === SyntaxKind.Parameter) {
const func = <FunctionLikeDeclaration>declaration.parent;
// For a parameter of a set accessor, use the type of the get accessor if one is present
Expand Down Expand Up @@ -8352,7 +8360,9 @@ namespace ts {
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
return declaredType;
}
const initialType = assumeInitialized ? declaredType : includeFalsyTypes(declaredType, TypeFlags.Undefined);
const initialType = assumeInitialized ? declaredType :
declaredType === autoType ? undefinedType :
includeFalsyTypes(declaredType, TypeFlags.Undefined);
const visitedFlowStart = visitedFlowCount;
const result = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode));
visitedFlowCount = visitedFlowStart;
Expand Down Expand Up @@ -8430,8 +8440,8 @@ namespace ts {
const flowType = getTypeAtFlowNode(flow.antecedent);
return crea 8000 teFlowType(getBaseTypeOfLiteralType(getTypeFromFlowType(flowType)), isIncomplete(flowType));
}
return declaredType.flags & TypeFlags.Union ?
getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(node)) :
return declaredType === autoType ? getBaseTypeOfLiteralType(getInitialOrAssignedType(node)) :
declaredType.flags & TypeFlags.Union ? getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(node)) :
declaredType;
}
// We didn't have a direct match. However, if the reference is a dotted name, this
Expand Down Expand Up @@ -9042,13 +9052,22 @@ namespace ts {
// We only look for uninitialized variables in strict null checking mode, and only when we can analyze
// the entire control flow graph from the variable's declaration (i.e. when the flow container and
// declaration container are the same).
const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isParameter ||
isOuterVariable || isInAmbientContext(declaration);
const assumeInitialized = isParameter || isOuterVariable ||
type !== autoType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0) ||
isInAmbientContext(declaration);
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, flowContainer);
// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
// from declaration to use, and when the variable's declared type doesn't include undefined but the
// control flow based type does include undefined.
if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
if (type === autoType) {
if (flowType === autoType) {
if (compilerOptions.noImplicitAny) {
error(declaration.name, Diagnostics.Variable_0_implicitly_has_type_any_and_is_referenced_in_a_nested_function, symbolToString(symbol));
}
return anyType;
}
}
else if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
// Return the declared type to reduce follow-on errors
return type;
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2945,6 +2945,10 @@
"category": "Error",
"code": 7033
},
"Variable '{0}' implicitly has type 'any' and is referenced in a nested function.": {
"category": "Error",
"code": 7034
},
"You cannot rename this element.": {
"category": "Error",
"code": 8000
Expand Down
0