rewrite void-returning statements in constructors that capture result of super call (#11868)

* rewrite void-returning statements in constructors that capture result of super call

* linter
This commit is contained in:
Vladimir Matveev 2016-10-26 16:48:29 -07:00 committed by GitHub
parent 8ad68adac9
commit 3e18aba36a
5 changed files with 410 additions and 4 deletions

View File

@ -186,6 +186,7 @@ namespace ts {
let enclosingFunction: FunctionLikeDeclaration;
let enclosingNonArrowFunction: FunctionLikeDeclaration;
let enclosingNonAsyncFunctionBody: FunctionLikeDeclaration | ClassElement;
let isInConstructorWithCapturedSuper: boolean;
/**
* Used to track if we are emitting body of the converted loop
@ -231,14 +232,17 @@ namespace ts {
const savedCurrentParent = currentParent;
const savedCurrentNode = currentNode;
const savedConvertedLoopState = convertedLoopState;
const savedIsInConstructorWithCapturedSuper = isInConstructorWithCapturedSuper;
if (nodeStartsNewLexicalEnvironment(node)) {
// don't treat content of nodes that start new lexical environment as part of converted loop copy
// don't treat content of nodes that start new lexical environment as part of converted loop copy or constructor body
isInConstructorWithCapturedSuper = false;
convertedLoopState = undefined;
}
onBeforeVisitNode(node);
const visited = f(node);
isInConstructorWithCapturedSuper = savedIsInConstructorWithCapturedSuper;
convertedLoopState = savedConvertedLoopState;
enclosingFunction = savedEnclosingFunction;
enclosingNonArrowFunction = savedEnclosingNonArrowFunction;
@ -251,6 +255,14 @@ namespace ts {
return visited;
}
function returnCapturedThis(node: Node): Node {
return setOriginalNode(createReturn(createIdentifier("_this")), node);
}
function isReturnVoidStatementInConstructorWithCapturedSuper(node: Node): boolean {
return isInConstructorWithCapturedSuper && node.kind === SyntaxKind.ReturnStatement && !(<ReturnStatement>node).expression;
}
function shouldCheckNode(node: Node): boolean {
return (node.transformFlags & TransformFlags.ES2015) !== 0 ||
node.kind === SyntaxKind.LabeledStatement ||
@ -258,10 +270,16 @@ namespace ts {
}
function visitorWorker(node: Node): VisitResult<Node> {
if (shouldCheckNode(node)) {
if (isReturnVoidStatementInConstructorWithCapturedSuper(node)) {
return returnCapturedThis(<ReturnStatement>node);
}
else if (shouldCheckNode(node)) {
return visitJavaScript(node);
}
else if (node.transformFlags & TransformFlags.ContainsES2015) {
else if (node.transformFlags & TransformFlags.ContainsES2015 || (isInConstructorWithCapturedSuper && !isExpression(node))) {
// we want to dive in this branch either if node has children with ES2015 specific syntax
// or we are inside constructor that captures result of the super call so all returns without expression should be
// rewritten. Note: we skip expressions since returns should never appear there
return visitEachChild(node, visitor, context);
}
else {
@ -283,6 +301,7 @@ namespace ts {
function visitNodesInConvertedLoop(node: Node): VisitResult<Node> {
switch (node.kind) {
case SyntaxKind.ReturnStatement:
node = isReturnVoidStatementInConstructorWithCapturedSuper(node) ? returnCapturedThis(node) : node;
return visitReturnStatement(<ReturnStatement>node);
case SyntaxKind.VariableStatement:
@ -864,7 +883,10 @@ namespace ts {
}
if (constructor) {
const body = saveStateAndInvoke(constructor, constructor => visitNodes(constructor.body.statements, visitor, isStatement, /*start*/ statementOffset));
const body = saveStateAndInvoke(constructor, constructor => {
isInConstructorWithCapturedSuper = superCaptureStatus === SuperCaptureResult.ReplaceSuperCapture;
return visitNodes(constructor.body.statements, visitor, isStatement, /*start*/ statementOffset);
});
addRange(statements, body);
}

View File

@ -0,0 +1,123 @@
//// [constructorWithCapturedSuper.ts]
let oneA: A;
class A {
constructor() {
return oneA;
}
}
class B extends A {
constructor(x: number) {
super();
if (x === 1) {
return;
}
while (x < 2) {
return;
}
try {
return
}
catch (e) {
return;
}
finally {
return;
}
}
}
class C extends A {
constructor(x: number) {
super();
for (let i = 0; i < 10; ++i) {
() => i + x;
if (x === 1) {
return;
}
}
}
}
class D extends A {
constructor(x: number) {
super();
() => {
return;
}
function foo() {
return;
}
}
}
//// [constructorWithCapturedSuper.js]
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var oneA;
var A = (function () {
function A() {
return oneA;
}
return A;
}());
var B = (function (_super) {
__extends(B, _super);
function B(x) {
var _this = _super.call(this) || this;
if (x === 1) {
return _this;
}
while (x < 2) {
return _this;
}
try {
return _this;
}
catch (e) {
return _this;
}
finally {
return _this;
}
return _this;
}
return B;
}(A));
var C = (function (_super) {
__extends(C, _super);
function C(x) {
var _this = _super.call(this) || this;
var _loop_1 = function (i) {
(function () { return i + x; });
if (x === 1) {
return { value: _this };
}
};
for (var i = 0; i < 10; ++i) {
var state_1 = _loop_1(i);
if (typeof state_1 === "object")
return state_1.value;
}
return _this;
}
return C;
}(A));
var D = (function (_super) {
__extends(D, _super);
function D(x) {
var _this = _super.call(this) || this;
(function () {
return;
});
function foo() {
return;
}
return _this;
}
return D;
}(A));

View File

@ -0,0 +1,96 @@
=== tests/cases/compiler/constructorWithCapturedSuper.ts ===
let oneA: A;
>oneA : Symbol(oneA, Decl(constructorWithCapturedSuper.ts, 0, 3))
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
class A {
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
constructor() {
return oneA;
>oneA : Symbol(oneA, Decl(constructorWithCapturedSuper.ts, 0, 3))
}
}
class B extends A {
>B : Symbol(B, Decl(constructorWithCapturedSuper.ts, 6, 1))
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
constructor(x: number) {
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 9, 16))
super();
>super : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
if (x === 1) {
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 9, 16))
return;
}
while (x < 2) {
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 9, 16))
return;
}
try {
return
}
catch (e) {
>e : Symbol(e, Decl(constructorWithCapturedSuper.ts, 20, 15))
return;
}
finally {
return;
}
}
}
class C extends A {
>C : Symbol(C, Decl(constructorWithCapturedSuper.ts, 27, 1))
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
constructor(x: number) {
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 30, 16))
super();
>super : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
for (let i = 0; i < 10; ++i) {
>i : Symbol(i, Decl(constructorWithCapturedSuper.ts, 32, 16))
>i : Symbol(i, Decl(constructorWithCapturedSuper.ts, 32, 16))
>i : Symbol(i, Decl(constructorWithCapturedSuper.ts, 32, 16))
() => i + x;
>i : Symbol(i, Decl(constructorWithCapturedSuper.ts, 32, 16))
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 30, 16))
if (x === 1) {
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 30, 16))
return;
}
}
}
}
class D extends A {
>D : Symbol(D, Decl(constructorWithCapturedSuper.ts, 39, 1))
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
constructor(x: number) {
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 42, 16))
super();
>super : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
() => {
return;
}
function foo() {
>foo : Symbol(foo, Decl(constructorWithCapturedSuper.ts, 46, 9))
return;
}
}
}

View File

@ -0,0 +1,113 @@
=== tests/cases/compiler/constructorWithCapturedSuper.ts ===
let oneA: A;
>oneA : A
>A : A
class A {
>A : A
constructor() {
return oneA;
>oneA : A
}
}
class B extends A {
>B : B
>A : A
constructor(x: number) {
>x : number
super();
>super() : void
>super : typeof A
if (x === 1) {
>x === 1 : boolean
>x : number
>1 : 1
return;
}
while (x < 2) {
>x < 2 : boolean
>x : number
>2 : 2
return;
}
try {
return
}
catch (e) {
>e : any
return;
}
finally {
return;
}
}
}
class C extends A {
>C : C
>A : A
constructor(x: number) {
>x : number
super();
>super() : void
>super : typeof A
for (let i = 0; i < 10; ++i) {
>i : number
>0 : 0
>i < 10 : boolean
>i : number
>10 : 10
>++i : number
>i : number
() => i + x;
>() => i + x : () => number
>i + x : number
>i : number
>x : number
if (x === 1) {
>x === 1 : boolean
>x : number
>1 : 1
return;
}
}
}
}
class D extends A {
>D : D
>A : A
constructor(x: number) {
>x : number
super();
>super() : void
>super : typeof A
() => {
>() => { return; } : () => void
return;
}
function foo() {
>foo : () => void
return;
}
}
}

View File

@ -0,0 +1,52 @@
let oneA: A;
class A {
constructor() {
return oneA;
}
}
class B extends A {
constructor(x: number) {
super();
if (x === 1) {
return;
}
while (x < 2) {
return;
}
try {
return
}
catch (e) {
return;
}
finally {
return;
}
}
}
class C extends A {
constructor(x: number) {
super();
for (let i = 0; i < 10; ++i) {
() => i + x;
if (x === 1) {
return;
}
}
}
}
class D extends A {
constructor(x: number) {
super();
() => {
return;
}
function foo() {
return;
}
}
}