Fix emit issue regarding null/undefined in type annotations

This commit is contained in:
Ron Buckton
2016-10-14 18:10:34 -07:00
parent cfbfe328b6
commit 980a894787
13 changed files with 392 additions and 49 deletions

View File

@@ -2330,6 +2330,9 @@ namespace ts {
case SyntaxKind.CallExpression:
return computeCallExpression(<CallExpression>node, subtreeFlags);
case SyntaxKind.NewExpression:
return computeNewExpression(<NewExpression>node, subtreeFlags);
case SyntaxKind.ModuleDeclaration:
return computeModuleDeclaration(<ModuleDeclaration>node, subtreeFlags);
@@ -2407,6 +2410,10 @@ namespace ts {
const expression = node.expression;
const expressionKind = expression.kind;
if (node.typeArguments) {
transformFlags |= TransformFlags.AssertTypeScript;
}
if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression
|| isSuperOrSuperProperty(expression, expressionKind)) {
// If the this node contains a SpreadElementExpression, or is a super call, then it is an ES6
@@ -2433,6 +2440,21 @@ namespace ts {
return false;
}
function computeNewExpression(node: NewExpression, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
if (node.typeArguments) {
transformFlags |= TransformFlags.AssertTypeScript;
}
if (subtreeFlags & TransformFlags.ContainsSpreadElementExpression) {
// If the this node contains a SpreadElementExpression then it is an ES6
// node.
transformFlags |= TransformFlags.AssertES6;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.ArrayLiteralOrCallOrNewExcludes;
}
function computeBinaryExpression(node: BinaryExpression, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const operatorTokenKind = node.operatorToken.kind;
@@ -2461,13 +2483,12 @@ namespace ts {
const initializer = node.initializer;
const dotDotDotToken = node.dotDotDotToken;
// If the parameter has a question token, then it is TypeScript syntax.
if (node.questionToken) {
transformFlags |= TransformFlags.AssertTypeScript;
}
// If the parameter's name is 'this', then it is TypeScript syntax.
if (subtreeFlags & TransformFlags.ContainsDecorators || isThisIdentifier(name)) {
// The '?' token, type annotations, decorators, and 'this' parameters are TypeSCript
// syntax.
if (node.questionToken
|| node.type
|| subtreeFlags & TransformFlags.ContainsDecorators
|| isThisIdentifier(name)) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -2526,7 +2547,8 @@ namespace ts {
// TypeScript syntax.
// An exported declaration may be TypeScript syntax.
if ((subtreeFlags & TransformFlags.TypeScriptClassSyntaxMask)
|| (modifierFlags & ModifierFlags.Export)) {
|| (modifierFlags & ModifierFlags.Export)
|| node.typeParameters) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -2547,7 +2569,8 @@ namespace ts {
// A class with a parameter property assignment, property initializer, or decorator is
// TypeScript syntax.
if (subtreeFlags & TransformFlags.TypeScriptClassSyntaxMask) {
if (subtreeFlags & TransformFlags.TypeScriptClassSyntaxMask
|| node.typeParameters) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -2601,10 +2624,10 @@ namespace ts {
function computeConstructor(node: ConstructorDeclaration, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const body = node.body;
if (body === undefined) {
// An overload constructor is TypeScript syntax.
// TypeScript-specific modifiers and overloads are TypeScript syntax
if (hasModifier(node, ModifierFlags.TypeScriptModifier)
|| !node.body) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -2615,22 +2638,19 @@ namespace ts {
function computeMethod(node: MethodDeclaration, subtreeFlags: TransformFlags) {
// A MethodDeclaration is ES6 syntax.
let transformFlags = subtreeFlags | TransformFlags.AssertES6;
const modifierFlags = getModifierFlags(node);
const body = node.body;
const typeParameters = node.typeParameters;
const asteriskToken = node.asteriskToken;
// A MethodDeclaration is TypeScript syntax if it is either async, abstract, overloaded,
// generic, or has a decorator.
if (!body
|| typeParameters
|| (modifierFlags & (ModifierFlags.Async | ModifierFlags.Abstract))
|| (subtreeFlags & TransformFlags.ContainsDecorators)) {
// Decorators, TypeScript-specific modifiers, type parameters, type annotations, and
// overloads are TypeScript syntax.
if (node.decorators
|| hasModifier(node, ModifierFlags.TypeScriptModifier)
|| node.typeParameters
|| node.type
|| !node.body) {
transformFlags |= TransformFlags.AssertTypeScript;
}
// Currently, we only support generators that were originally async function bodies.
if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
if (node.asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}
@@ -2640,14 +2660,13 @@ namespace ts {
function computeAccessor(node: AccessorDeclaration, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const modifierFlags = getModifierFlags(node);
const body = node.body;
// A MethodDeclaration is TypeScript syntax if it is either async, abstract, overloaded,
// generic, or has a decorator.
if (!body
|| (modifierFlags & (ModifierFlags.Async | ModifierFlags.Abstract))
|| (subtreeFlags & TransformFlags.ContainsDecorators)) {
// Decorators, TypeScript-specific modifiers, type annotations, and overloads are
// TypeScript syntax.
if (node.decorators
|| hasModifier(node, ModifierFlags.TypeScriptModifier)
|| node.type
|| !node.body) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -2673,7 +2692,6 @@ namespace ts {
let transformFlags: TransformFlags;
const modifierFlags = getModifierFlags(node);
const body = node.body;
const asteriskToken = node.asteriskToken;
if (!body || (modifierFlags & ModifierFlags.Ambient)) {
// An ambient declaration is TypeScript syntax.
@@ -2688,8 +2706,11 @@ namespace ts {
transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.AssertES6;
}
// If a FunctionDeclaration is async, then it is TypeScript syntax.
if (modifierFlags & ModifierFlags.Async) {
// TypeScript-specific modifiers, type parameters, and type annotations are TypeScript
// syntax.
if (modifierFlags & ModifierFlags.TypeScriptModifier
|| node.typeParameters
|| node.type) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -2705,7 +2726,7 @@ namespace ts {
// down-level generator.
// Currently we do not support transforming any other generator fucntions
// down level.
if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
if (node.asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}
}
@@ -2716,11 +2737,12 @@ namespace ts {
function computeFunctionExpression(node: FunctionExpression, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const modifierFlags = getModifierFlags(node);
const asteriskToken = node.asteriskToken;
// An async function expression is TypeScript syntax.
if (modifierFlags & ModifierFlags.Async) {
// TypeScript-specific modifiers, type parameters, and type annotations are TypeScript
// syntax.
if (hasModifier(node, ModifierFlags.TypeScriptModifier)
|| node.typeParameters
|| node.type) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -2736,7 +2758,7 @@ namespace ts {
// down-level generator.
// Currently we do not support transforming any other generator fucntions
// down level.
if (asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
if (node.asteriskToken && getEmitFlags(node) & EmitFlags.AsyncFunctionBody) {
transformFlags |= TransformFlags.AssertGenerator;
}
@@ -2747,10 +2769,12 @@ namespace ts {
function computeArrowFunction(node: ArrowFunction, subtreeFlags: TransformFlags) {
// An ArrowFunction is ES6 syntax, and excludes markers that should not escape the scope of an ArrowFunction.
let transformFlags = subtreeFlags | TransformFlags.AssertES6;
const modifierFlags = getModifierFlags(node);
// An async arrow function is TypeScript syntax.
if (modifierFlags & ModifierFlags.Async) {
// TypeScript-specific modifiers, type parameters, and type annotations are TypeScript
// syntax.
if (hasModifier(node, ModifierFlags.TypeScriptModifier)
|| node.typeParameters
|| node.type) {
transformFlags |= TransformFlags.AssertTypeScript;
}
@@ -2787,6 +2811,11 @@ namespace ts {
transformFlags |= TransformFlags.AssertES6 | TransformFlags.ContainsBindingPattern;
}
// Type annotations are TypeScript syntax.
if (node.type) {
transformFlags |= TransformFlags.AssertTypeScript;
}
node.transformFlags = transformFlags | TransformFlags.HasComputedFlags;
return transformFlags & ~TransformFlags.NodeExcludes;
}

View File

@@ -504,15 +504,29 @@ const _super = (function (geti, seti) {
// Contextual keywords
case SyntaxKind.AbstractKeyword:
case SyntaxKind.AsKeyword:
case SyntaxKind.AnyKeyword:
case SyntaxKind.AsyncKeyword:
case SyntaxKind.AwaitKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.ConstructorKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.GetKeyword:
case SyntaxKind.IsKeyword:
case SyntaxKind.ModuleKeyword:
case SyntaxKind.NamespaceKeyword:
case SyntaxKind.NeverKeyword:
case SyntaxKind.ReadonlyKeyword:
case SyntaxKind.RequireKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.SetKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.SymbolKeyword:
case SyntaxKind.TypeKeyword:
case SyntaxKind.UndefinedKeyword:
case SyntaxKind.FromKeyword:
case SyntaxKind.GlobalKeyword:
case SyntaxKind.OfKeyword:
writeTokenText(kind);
return;
@@ -1198,12 +1212,14 @@ const _super = (function (geti, seti) {
function emitCallExpression(node: CallExpression) {
emitExpression(node.expression);
emitTypeArguments(node, node.typeArguments);
emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments);
}
function emitNewExpression(node: NewExpression) {
write("new ");
emitExpression(node.expression);
emitTypeArguments(node, node.typeArguments);
emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments);
}
@@ -1575,6 +1591,7 @@ const _super = (function (geti, seti) {
function emitVariableDeclaration(node: VariableDeclaration) {
emit(node.name);
emitWithPrefix(": ", node.type);
emitExpressionWithPrefix(" = ", node.initializer);
}

View File

@@ -1416,6 +1416,7 @@ namespace ts {
if (getAccessor) {
const getterFunction = transformFunctionLikeToExpression(getAccessor, /*location*/ undefined, /*name*/ undefined);
setSourceMapRange(getterFunction, getSourceMapRange(getAccessor));
setEmitFlags(getterFunction, EmitFlags.NoLeadingComments);
const getter = createPropertyAssignment("get", getterFunction);
setCommentRange(getter, getCommentRange(getAccessor));
properties.push(getter);
@@ -1424,6 +1425,7 @@ namespace ts {
if (setAccessor) {
const setterFunction = transformFunctionLikeToExpression(setAccessor, /*location*/ undefined, /*name*/ undefined);
setSourceMapRange(setterFunction, getSourceMapRange(setAccessor));
setEmitFlags(setterFunction, EmitFlags.NoLeadingComments);
const setter = createPropertyAssignment("set", setterFunction);
setCommentRange(setter, getCommentRange(setAccessor));
properties.push(setter);

View File

@@ -287,7 +287,7 @@ namespace ts {
case SyntaxKind.Constructor:
// TypeScript constructors are transformed in `visitClassDeclaration`.
return undefined;
return visitConstructor(<ConstructorDeclaration>node);
case SyntaxKind.InterfaceDeclaration:
// TypeScript interfaces are elided, but some comments may be preserved.
@@ -377,6 +377,12 @@ namespace ts {
// TypeScript type assertions are removed, but their subtrees are preserved.
return visitAssertionExpression(<AssertionExpression>node);
case SyntaxKind.CallExpression:
return visitCallExpression(<CallExpression>node);
case SyntaxKind.NewExpression:
return visitNewExpression(<NewExpression>node);
case SyntaxKind.NonNullExpression:
// TypeScript non-null expressions are removed, but their subtrees are preserved.
return visitNonNullExpression(<NonNullExpression>node);
@@ -393,6 +399,9 @@ namespace ts {
// TypeScript namespace exports for variable statements must be transformed.
return visitVariableStatement(<VariableStatement>node);
case SyntaxKind.VariableDeclaration:
return visitVariableDeclaration(<VariableDeclaration>node);
case SyntaxKind.ModuleDeclaration:
// TypeScript namespace declarations must be transformed.
return visitModuleDeclaration(<ModuleDeclaration>node);
@@ -2088,6 +2097,14 @@ namespace ts {
return !nodeIsMissing(node.body);
}
function visitConstructor(node: ConstructorDeclaration) {
if (!shouldEmitFunctionLikeDeclaration(node)) {
return undefined;
}
return visitEachChild(node, visitor, context);
}
/**
* Visits a method declaration of a class.
*
@@ -2295,13 +2312,16 @@ namespace ts {
function transformFunctionBodyWorker(body: Block, start = 0) {
const savedCurrentScope = currentScope;
const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName;
currentScope = body;
currentScopeFirstDeclarationsOfName = createMap<Node>();
startLexicalEnvironment();
const statements = visitNodes(body.statements, visitor, isStatement, start);
const visited = updateBlock(body, statements);
const declarations = endLexicalEnvironment();
currentScope = savedCurrentScope;
currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName;
return mergeFunctionBodyLexicalEnvironment(visited, declarations);
}
@@ -2481,6 +2501,14 @@ namespace ts {
}
}
function visitVariableDeclaration(node: VariableDeclaration) {
return updateVariableDeclaration(
node,
visitNode(node.name, visitor, isBindingName),
/*type*/ undefined,
visitNode(node.initializer, visitor, isExpression));
}
/**
* Visits an await expression.
*
@@ -2541,6 +2569,22 @@ namespace ts {
return createPartiallyEmittedExpression(expression, node);
}
function visitCallExpression(node: CallExpression) {
return updateCall(
node,
visitNode(node.expression, visitor, isExpression),
/*typeArguments*/ undefined,
visitNodes(node.arguments, visitor, isExpression));
}
function visitNewExpression(node: NewExpression) {
return updateNew(
node,
visitNode(node.expression, visitor, isExpression),
/*typeArguments*/ undefined,
visitNodes(node.arguments, visitor, isExpression));
}
/**
* Determines whether to emit an enum declaration.
*

View File

@@ -456,6 +456,8 @@ namespace ts {
// Accessibility modifiers and 'readonly' can be attached to a parameter in a constructor to make it a property.
ParameterPropertyModifier = AccessibilityModifier | Readonly,
NonPublicAccessibilityModifier = Private | Protected,
TypeScriptModifier = Ambient | Public | Private | Protected | Readonly | Abstract | Async | Const
}
export const enum JsxFlags {

View File

@@ -6,7 +6,7 @@ class foo {
//// [constructorArgsErrors1.js]
var foo = (function () {
function foo(static a) {
function foo(a) {
}
return foo;
}());

View File

@@ -7,7 +7,7 @@ class foo {
//// [constructorArgsErrors5.js]
var foo = (function () {
function foo(export a) {
function foo(a) {
}
return foo;
}());

View File

@@ -7,7 +7,6 @@ class Foo {
var Foo = (function () {
function Foo() {
}
Foo.prototype.banana = function (x) { };
return Foo;
}());
break ;

View File

@@ -0,0 +1,54 @@
//// [transformsElideNullUndefinedType.ts]
var v0: null;
var v1: undefined;
function f0(): null { return null; }
function f1(): undefined { return undefined; }
var f2 = function (): null { return null; }
var f3 = function (): undefined { return undefined; }
var f4 = (): null => null;
var f5 = (): undefined => undefined;
function f6(p0: null) { }
function f7(p1: undefined) { }
class C<T> {
m0(): null { return null; }
m1(): undefined { return undefined; }
get a0(): null { return null; }
get a1(): undefined { return undefined; }
}
declare function fn<T>();
fn<null>();
fn<undefined>();
new C<null>();
new C<undefined>();
//// [transformsElideNullUndefinedType.js]
var v0;
var v1;
function f0() { return null; }
function f1() { return undefined; }
var f2 = function () { return null; };
var f3 = function () { return undefined; };
var f4 = () => null;
var f5 = () => undefined;
function f6(p0) { }
function f7(p1) { }
class C {
m0() { return null; }
m1() { return undefined; }
get a0() { return null; }
get a1() { return undefined; }
}
fn();
fn();
new C();
new C();

View File

@@ -0,0 +1,72 @@
=== tests/cases/compiler/transformsElideNullUndefinedType.ts ===
var v0: null;
>v0 : Symbol(v0, Decl(transformsElideNullUndefinedType.ts, 1, 3))
var v1: undefined;
>v1 : Symbol(v1, Decl(transformsElideNullUndefinedType.ts, 2, 3))
function f0(): null { return null; }
>f0 : Symbol(f0, Decl(transformsElideNullUndefinedType.ts, 2, 18))
function f1(): undefined { return undefined; }
>f1 : Symbol(f1, Decl(transformsElideNullUndefinedType.ts, 4, 36))
>undefined : Symbol(undefined)
var f2 = function (): null { return null; }
>f2 : Symbol(f2, Decl(transformsElideNullUndefinedType.ts, 7, 3))
var f3 = function (): undefined { return undefined; }
>f3 : Symbol(f3, Decl(transformsElideNullUndefinedType.ts, 8, 3))
>undefined : Symbol(undefined)
var f4 = (): null => null;
>f4 : Symbol(f4, Decl(transformsElideNullUndefinedType.ts, 10, 3))
var f5 = (): undefined => undefined;
>f5 : Symbol(f5, Decl(transformsElideNullUndefinedType.ts, 11, 3))
>undefined : Symbol(undefined)
function f6(p0: null) { }
>f6 : Symbol(f6, Decl(transformsElideNullUndefinedType.ts, 11, 36))
>p0 : Symbol(p0, Decl(transformsElideNullUndefinedType.ts, 13, 12))
function f7(p1: undefined) { }
>f7 : Symbol(f7, Decl(transformsElideNullUndefinedType.ts, 13, 25))
>p1 : Symbol(p1, Decl(transformsElideNullUndefinedType.ts, 14, 12))
class C<T> {
>C : Symbol(C, Decl(transformsElideNullUndefinedType.ts, 14, 30))
>T : Symbol(T, Decl(transformsElideNullUndefinedType.ts, 16, 8))
m0(): null { return null; }
>m0 : Symbol(C.m0, Decl(transformsElideNullUndefinedType.ts, 16, 12))
m1(): undefined { return undefined; }
>m1 : Symbol(C.m1, Decl(transformsElideNullUndefinedType.ts, 17, 31))
>undefined : Symbol(undefined)
get a0(): null { return null; }
>a0 : Symbol(C.a0, Decl(transformsElideNullUndefinedType.ts, 18, 41))
get a1(): undefined { return undefined; }
>a1 : Symbol(C.a1, Decl(transformsElideNullUndefinedType.ts, 20, 35))
>undefined : Symbol(undefined)
}
declare function fn<T>();
>fn : Symbol(fn, Decl(transformsElideNullUndefinedType.ts, 22, 1))
>T : Symbol(T, Decl(transformsElideNullUndefinedType.ts, 24, 20))
fn<null>();
>fn : Symbol(fn, Decl(transformsElideNullUndefinedType.ts, 22, 1))
fn<undefined>();
>fn : Symbol(fn, Decl(transformsElideNullUndefinedType.ts, 22, 1))
new C<null>();
>C : Symbol(C, Decl(transformsElideNullUndefinedType.ts, 14, 30))
new C<undefined>();
>C : Symbol(C, Decl(transformsElideNullUndefinedType.ts, 14, 30))

View File

@@ -0,0 +1,94 @@
=== tests/cases/compiler/transformsElideNullUndefinedType.ts ===
var v0: null;
>v0 : null
>null : null
var v1: undefined;
>v1 : undefined
function f0(): null { return null; }
>f0 : () => null
>null : null
>null : null
function f1(): undefined { return undefined; }
>f1 : () => undefined
>undefined : undefined
var f2 = function (): null { return null; }
>f2 : () => null
>function (): null { return null; } : () => null
>null : null
>null : null
var f3 = function (): undefined { return undefined; }
>f3 : () => undefined
>function (): undefined { return undefined; } : () => undefined
>undefined : undefined
var f4 = (): null => null;
>f4 : () => null
>(): null => null : () => null
>null : null
>null : null
var f5 = (): undefined => undefined;
>f5 : () => undefined
>(): undefined => undefined : () => undefined
>undefined : undefined
function f6(p0: null) { }
>f6 : (p0: null) => void
>p0 : null
>null : null
function f7(p1: undefined) { }
>f7 : (p1: undefined) => void
>p1 : undefined
class C<T> {
>C : C<T>
>T : T
m0(): null { return null; }
>m0 : () => null
>null : null
>null : null
m1(): undefined { return undefined; }
>m1 : () => undefined
>undefined : undefined
get a0(): null { return null; }
>a0 : null
>null : null
>null : null
get a1(): undefined { return undefined; }
>a1 : undefined
>undefined : undefined
}
declare function fn<T>();
>fn : <T>() => any
>T : T
fn<null>();
>fn<null>() : any
>fn : <T>() => any
>null : null
fn<undefined>();
>fn<undefined>() : any
>fn : <T>() => any
new C<null>();
>new C<null>() : C<null>
>C : typeof C
>null : null
new C<undefined>();
>new C<undefined>() : C<undefined>
>C : typeof C

View File

@@ -171,7 +171,6 @@ var C = (function (_super) {
function hasANonBooleanReturnStatement(x) {
return '';
}
function hasTypeGuardTypeInsideTypeGuardType(x) { }
is;
A;
{
@@ -232,7 +231,6 @@ function b2(a, A) {
if (a === void 0) { a = is; }
}
;
function b3() { }
is;
A;
{

View File

@@ -0,0 +1,32 @@
// @target: es6
var v0: null;
var v1: undefined;
function f0(): null { return null; }
function f1(): undefined { return undefined; }
var f2 = function (): null { return null; }
var f3 = function (): undefined { return undefined; }
var f4 = (): null => null;
var f5 = (): undefined => undefined;
function f6(p0: null) { }
function f7(p1: undefined) { }
class C<T> {
m0(): null { return null; }
m1(): undefined { return undefined; }
get a0(): null { return null; }
get a1(): undefined { return undefined; }
}
declare function fn<T>();
fn<null>();
fn<undefined>();
new C<null>();
new C<undefined>();