mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-09 20:51:43 -06:00
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:
parent
8ad68adac9
commit
3e18aba36a
@ -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);
|
||||
}
|
||||
|
||||
|
||||
123
tests/baselines/reference/constructorWithCapturedSuper.js
Normal file
123
tests/baselines/reference/constructorWithCapturedSuper.js
Normal 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));
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
113
tests/baselines/reference/constructorWithCapturedSuper.types
Normal file
113
tests/baselines/reference/constructorWithCapturedSuper.types
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
tests/cases/compiler/constructorWithCapturedSuper.ts
Normal file
52
tests/cases/compiler/constructorWithCapturedSuper.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user