-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Simplify deferred function, class, and accessor checking #6083
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
Changes from all commits
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 |
---|---|---|
|
@@ -161,6 +161,8 @@ namespace ts { | |
|
||
let jsxElementClassType: Type; | ||
|
||
let deferredNodes: Node[]; | ||
|
||
const tupleTypes: Map<TupleType> = {}; | ||
const unionTypes: Map<UnionType> = {}; | ||
const intersectionTypes: Map<IntersectionType> = {}; | ||
|
@@ -10123,6 +10125,7 @@ namespace ts { | |
|
||
if (!contextChecked) { | ||
checkSignatureDeclaration(node); | ||
checkNodeDeferred(node); | ||
} | ||
} | ||
} | ||
|
@@ -10135,7 +10138,7 @@ namespace ts { | |
return type; | ||
} | ||
|
||
function checkFunctionExpressionOrObjectLiteralMethodBody(node: ArrowFunction | FunctionExpression | MethodDeclaration) { | ||
function checkFunctionExpressionOrObjectLiteralMethodDeferred(node: ArrowFunction | FunctionExpression | MethodDeclaration) { | ||
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); | ||
|
||
const isAsync = isAsyncFunctionLike(node); | ||
|
@@ -10178,8 +10181,6 @@ namespace ts { | |
checkTypeAssignableTo(exprType, returnOrPromisedType, node.body); | ||
} | ||
} | ||
|
||
checkFunctionAndClassExpressionBodies(node.body); | ||
} | ||
} | ||
} | ||
|
@@ -11428,13 +11429,13 @@ namespace ts { | |
if (node.parent.kind !== SyntaxKind.ObjectLiteralExpression) { | ||
checkSourceElement(node.body); | ||
} | ||
else { | ||
checkNodeDeferred(node); | ||
} | ||
} | ||
|
||
function checkObjectLiteralAccessorBody(node: AccessorDeclaration) { | ||
if (node.body) { | ||
checkSourceElement(node.body); | ||
checkFunctionAndClassExpressionBodies(node.body); | ||
} | ||
function checkAccessorDeferred(node: AccessorDeclaration) { | ||
checkSourceElement(node.body); | ||
} | ||
|
||
function checkMissingDeclaration(node: Node) { | ||
|
@@ -12373,11 +12374,7 @@ namespace ts { | |
if (node.kind === SyntaxKind.Block) { | ||
checkGrammarStatementInAmbientContext(node); | ||
} | ||
|
||
forEach(node.statements, checkSourceElement); | ||
if (isFunctionBlock(node) || node.kind === SyntaxKind.ModuleBlock) { | ||
checkFunctionAndClassExpressionBodies(node); | ||
} | ||
} | ||
|
||
function checkCollisionWithArgumentsInGeneratedCode(node: SignatureDeclaration) { | ||
|
@@ -13406,15 +13403,19 @@ namespace ts { | |
|
||
function checkClassExpression(node: ClassExpression): Type { | ||
checkClassLikeDeclaration(node); | ||
checkNodeDeferred(node); | ||
return getTypeOfSymbol(getSymbolOfNode(node)); | ||
} | ||
|
||
function checkClassExpressionDeferred(node: ClassExpression) { | ||
forEach(node.members, checkSourceElement); | ||
} | ||
|
||
function checkClassDeclaration(node: ClassDeclaration) { | ||
if (!node.name && !(node.flags & NodeFlags.Default)) { | ||
grammarErrorOnFirstToken(node, Diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name); | ||
} | ||
checkClassLikeDeclaration(node); | ||
|
||
forEach(node.members, checkSourceElement); | ||
} | ||
|
||
|
@@ -14478,107 +14479,29 @@ namespace ts { | |
// Here, performing a full type check of the body of the function expression whilst in the process of | ||
// determining the type of foo would cause foo to be given type any because of the recursive reference. | ||
// Delaying the type check of the body ensures foo has been assigned a type. | ||
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. Move the comment up to the definition of |
||
function checkFunctionAndClassExpressionBodies(node: Node): void { | ||
switch (node.kind) { | ||
case SyntaxKind.FunctionExpression: | ||
case SyntaxKind.ArrowFunction: | ||
forEach((<FunctionLikeDeclaration>node).parameters, checkFunctionAndClassExpressionBodies); | ||
checkFunctionExpressionOrObjectLiteralMethodBody(<FunctionExpression>node); | ||
break; | ||
case SyntaxKind.ClassExpression: | ||
forEach((<ClassExpression>node).members, checkSourceElement); | ||
forEachChild(node, checkFunctionAndClassExpressionBodies); | ||
break; | ||
case SyntaxKind.MethodDeclaration: | ||
case SyntaxKind.MethodSignature: | ||
forEach(node.decorators, checkFunctionAndClassExpressionBodies); | ||
forEach((<MethodDeclaration>node).parameters, checkFunctionAndClassExpressionBodies); | ||
if (isObjectLiteralMethod(node)) { | ||
checkFunctionExpressionOrObjectLiteralMethodBody(node); | ||
} | ||
break; | ||
case SyntaxKind.Constructor: | ||
case SyntaxKind.FunctionDeclaration: | ||
forEach((<FunctionLikeDeclaration>node).parameters, checkFunctionAndClassExpressionBodies); | ||
break; | ||
case SyntaxKind.GetAccessor: | ||
case SyntaxKind.SetAccessor: | ||
forEach((<FunctionLikeDeclaration>node).parameters, checkFunctionAndClassExpressionBodies); | ||
if (node.parent.kind === SyntaxKind.ObjectLiteralExpression) { | ||
checkObjectLiteralAccessorBody(<AccessorDeclaration>node); | ||
} | ||
break; | ||
case SyntaxKind.WithStatement: | ||
checkFunctionAndClassExpressionBodies((<WithStatement>node).expression); | ||
break; | ||
case SyntaxKind.Decorator: | ||
case SyntaxKind.Parameter: | ||
case SyntaxKind.PropertyDeclaration: | ||
case SyntaxKind.PropertySignature: | ||
case SyntaxKind.ObjectBindingPattern: | ||
case SyntaxKind.ArrayBindingPattern: | ||
case SyntaxKind.BindingElement: | ||
case SyntaxKind.ArrayLiteralExpression: | ||
case SyntaxKind.ObjectLiteralExpression: | ||
case SyntaxKind.PropertyAssignment: | ||
case SyntaxKind.PropertyAccessExpression: | ||
case SyntaxKind.ElementAccessExpression: | ||
case SyntaxKind.CallExpression: | ||
case SyntaxKind.NewExpression: | ||
case SyntaxKind.TaggedTemplateExpression: | ||
case SyntaxKind.TemplateExpression: | ||
case SyntaxKind.TemplateSpan: | ||
case SyntaxKind.TypeAssertionExpression: | ||
case SyntaxKind.AsExpression: | ||
case SyntaxKind.ParenthesizedExpression: | ||
case SyntaxKind.TypeOfExpression: | ||
case SyntaxKind.VoidExpression: | ||
case SyntaxKind.AwaitExpression: | ||
case SyntaxKind.DeleteExpression: | ||
case SyntaxKind.PrefixUnaryExpression: | ||
case SyntaxKind.PostfixUnaryExpression: | ||
case SyntaxKind.BinaryExpression: | ||
case SyntaxKind.ConditionalExpression: | ||
case SyntaxKind.SpreadElementExpression: | ||
case SyntaxKind.YieldExpression: | ||
case SyntaxKind.Block: | ||
case SyntaxKind.ModuleBlock: | ||
case SyntaxKind.VariableStatement: | ||
case SyntaxKind.ExpressionStatement: | ||
case SyntaxKind.IfStatement: | ||
case SyntaxKind.DoStatement: | ||
case SyntaxKind.WhileStatement: | ||
case SyntaxKind.ForStatement: | ||
case SyntaxKind.ForInStatement: | ||
case SyntaxKind.ForOfStatement: | ||
case SyntaxKind.ContinueStatement: | ||
case SyntaxKind.BreakStatement: | ||
case SyntaxKind.ReturnStatement: | ||
case SyntaxKind.SwitchStatement: | ||
case SyntaxKind.CaseBlock: | ||
case SyntaxKind.CaseClause: | ||
case SyntaxKind.DefaultClause: | ||
case SyntaxKind.LabeledStatement: | ||
case SyntaxKind.ThrowStatement: | ||
case SyntaxKind.TryStatement: | ||
case SyntaxKind.CatchClause: | ||
case SyntaxKind.VariableDeclaration: | ||
case SyntaxKind.VariableDeclarationList: | ||
case SyntaxKind.ClassDeclaration: | ||
case SyntaxKind.HeritageClause: | ||
case SyntaxKind.ExpressionWithTypeArguments: | ||
case SyntaxKind.EnumDeclaration: | ||
case SyntaxKind.EnumMember: | ||
case SyntaxKind.ExportAssignment: | ||
case SyntaxKind.SourceFile: | ||
case SyntaxKind.JsxExpression: | ||
case SyntaxKind.JsxElement: | ||
case SyntaxKind.JsxSelfClosingElement: | ||
case SyntaxKind.JsxAttribute: | ||
case SyntaxKind.JsxSpreadAttribute: | ||
case SyntaxKind.JsxOpeningElement: | ||
forEachChild(node, checkFunctionAndClassExpressionBodies); | ||
break; | ||
function checkNodeDeferred(node: Node) { | ||
if (deferredNodes) { | ||
deferredNodes.push(node); | ||
} | ||
} | ||
|
||
function checkDeferredNodes() { | ||
for (const node of deferredNodes) { | ||
switch (node.kind) { | ||
case SyntaxKind.FunctionExpression: | ||
case SyntaxKind.ArrowFunction: | ||
case SyntaxKind.MethodDeclaration: | ||
case SyntaxKind.MethodSignature: | ||
checkFunctionExpressionOrObjectLiteralMethodDeferred(<FunctionExpression>node); | ||
break; | ||
case SyntaxKind.GetAccessor: | ||
case SyntaxKind.SetAccessor: | ||
checkAccessorDeferred(<AccessorDeclaration>node); | ||
break; | ||
case SyntaxKind.ClassExpression: | ||
checkClassExpressionDeferred(<ClassExpression>node); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
|
@@ -14613,8 +14536,10 @@ namespace ts { | |
emitAwaiter = false; | ||
potentialThisCollisions.length = 0; | ||
|
||
deferredNodes = []; | ||
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 can't see what's above this because it's above the 11K mark, but does that mean that this is unconditionally initialized? If so, you can remove the initialization check 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. No, it is not unconditional. See my earlier comment. |
||
forEach(node.statements, checkSourceElement); | ||
checkFunctionAndClassExpressionBodies(node); | ||
checkDeferredNodes(); | ||
deferredNodes = undefined; | ||
|
||
if (isExternalOrCommonJsModule(node)) { | ||
checkExternalModuleExports(node); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,11 @@ | ||
tests/cases/compiler/multiLinePropertyAccessAndArrowFunctionIndent1.ts(1,1): error TS1108: A 'return' statement can only be used within a function body. | ||
tests/cases/compiler/multiLinePropertyAccessAndArrowFunctionIndent1.ts(1,18): error TS2304: Cannot find name 'role'. | ||
tests/cases/compiler/multiLinePropertyAccessAndArrowFunctionIndent1.ts(2,18): error TS2304: Cannot find name 'Role'. | ||
tests/cases/compiler/multiLinePropertyAccessAndArrowFunctionIndent1.ts(4,26): error TS2503: Cannot find namespace 'ng'. | ||
|
||
|
||
==== tests/cases/compiler/multiLinePropertyAccessAndArrowFunctionIndent1.ts (4 errors) ==== | ||
==== tests/cases/compiler/multiLinePropertyAccessAndArrowFunctionIndent1.ts (1 errors) ==== | ||
return this.edit(role) | ||
~~~~~~ | ||
!!! error TS1108: A 'return' statement can only be used within a function body. | ||
~~~~ | ||
!!! error TS2304: Cannot find name 'role'. | ||
.then((role: Role) => | ||
~~~~ | ||
!!! error TS2304: Cannot find name 'Role'. | ||
this.roleService.add(role) | ||
.then((data: ng.IHttpPromiseCallbackArg<Role>) => data.data)); | ||
~~ | ||
!!! error TS2503: Cannot find namespace 'ng'. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,9 +6,8 @@ return this.edit(role) | |
|
||
|
||
//// [multiLinePropertyAccessAndArrowFunctionIndent1.js] | ||
var _this = this; | ||
return this.edit(role) | ||
.then(function (role) { | ||
return _this.roleService.add(role) | ||
return this.roleService.add(role) | ||
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. Hmz, is this intended? 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. Yes, it is intended. The 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. Ah oke! Thanks for the clarification 👍 |
||
.then(function (data) { return data.data; }); | ||
}); |
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.
deferredCheckNodes
and leave a decent comment about why these need to be deferred until the end.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.
Are you ever going to not have deferred nodes for most purposes? Seems like you'd have more overhead on a large project from checking over and over whether to defer than to create a one-time array allocation.
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.
The
deferredNodes
array is used only when doing a full type check. When called from the language service (i.e. when "helicoptering in") we don't use it.