Merge branch 'transforms-transformer-ts' into transforms-transformer-jsx

This commit is contained in:
Ron Buckton 2016-02-23 16:27:26 -08:00
commit 608822d93f
14 changed files with 441 additions and 295 deletions

View File

@ -239,6 +239,7 @@ function concatenateFiles(destinationFile, sourceFiles) {
var useDebugMode = true;
var useTransforms = process.env.USE_TRANSFORMS || false;
var useTransformCompat = false;
var host = (process.env.host || process.env.TYPESCRIPT_HOST || "node");
var compilerFilename = "tsc.js";
var LKGCompiler = path.join(LKGDirectory, compilerFilename);
@ -301,6 +302,9 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, noOu
if (useBuiltCompiler && useTransforms) {
options += " --experimentalTransforms"
}
else if (useBuiltCompiler && useTransformCompat) {
options += " --transformCompatibleEmit"
}
var cmd = host + " " + compilerPath + " " + options + " ";
cmd = cmd + sources.join(" ");
@ -429,6 +433,10 @@ task("setTransforms", function() {
useTransforms = true;
});
task("setTransformCompat", function() {
useTransformCompat = true;
});
task("configure-nightly", [configureNightlyJs], function() {
var cmd = host + " " + configureNightlyJs + " " + packageJson + " " + programTs;
console.log(cmd);

View File

@ -322,9 +322,18 @@ namespace ts {
description: Diagnostics.Allow_default_imports_from_modules_with_no_default_export_This_does_not_affect_code_emit_just_typechecking
},
{
// this option will be removed when this is merged with master and exists solely
// to enable the tree transforming emitter side-by-side with the existing emitter.
name: "experimentalTransforms",
type: "boolean",
experimental: true
},
{
// this option will be removed when this is merged with master and exists solely
// to enable the tree transforming emitter side-by-side with the existing emitter.
name: "transformCompatibleEmit",
type: "boolean",
experimental: true
}
];

View File

@ -1919,6 +1919,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
if (multiLine) {
decreaseIndent();
if (!compilerOptions.transformCompatibleEmit) {
writeLine();
}
}
write(")");
@ -4335,7 +4338,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
writeLine();
emitStart(restParam);
emitNodeWithCommentsAndWithoutSourcemap(restParam.name);
write(restIndex > 0
write(restIndex > 0 || !compilerOptions.transformCompatibleEmit
? `[${tempName} - ${restIndex}] = arguments[${tempName}];`
: `[${tempName}] = arguments[${tempName}];`);
emitEnd(restParam);
@ -5357,7 +5360,7 @@ const _super = (function (geti, seti) {
const isClassExpressionWithStaticProperties = staticProperties.length > 0 && node.kind === SyntaxKind.ClassExpression;
let tempVariable: Identifier;
if (isClassExpressionWithStaticProperties) {
if (isClassExpressionWithStaticProperties && compilerOptions.transformCompatibleEmit) {
tempVariable = createAndRecordTempVariable(TempFlags.Auto);
write("(");
increaseIndent();
@ -5394,6 +5397,11 @@ const _super = (function (geti, seti) {
writeLine();
emitConstructor(node, baseTypeNode);
emitMemberFunctionsForES5AndLower(node);
if (!compilerOptions.transformCompatibleEmit) {
emitPropertyDeclarations(node, staticProperties);
writeLine();
emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined);
}
writeLine();
emitToken(SyntaxKind.CloseBraceToken, node.members.end, () => {
write("return ");
@ -5420,11 +5428,13 @@ const _super = (function (geti, seti) {
write("))");
if (node.kind === SyntaxKind.ClassDeclaration) {
write(";");
emitPropertyDeclarations(node, staticProperties);
writeLine();
emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined);
if (compilerOptions.transformCompatibleEmit) {
emitPropertyDeclarations(node, staticProperties);
writeLine();
emitDecoratorsOfClass(node, /*decoratedClassAlias*/ undefined);
}
}
else if (isClassExpressionWithStaticProperties) {
else if (isClassExpressionWithStaticProperties && compilerOptions.transformCompatibleEmit) {
for (const property of staticProperties) {
write(",");
writeLine();

View File

@ -250,7 +250,7 @@ namespace ts {
node.decorators = undefined;
node.modifiers = undefined;
node.typeParameters = undefined;
node.parameters = createSynthesizedNodeArray(parameters);
node.parameters = createNodeArray(parameters);
node.type = undefined;
node.body = body;
return node;
@ -286,7 +286,7 @@ namespace ts {
node.name = typeof name === "string" ? createIdentifier(name) : name;
node.questionToken = undefined;
node.type = undefined;
node.initializer = initializer;
node.initializer = initializer ? parenthesizeExpressionForList(initializer) : undefined;
return node;
}
@ -294,7 +294,7 @@ namespace ts {
export function createArrayLiteral(elements?: Expression[]) {
const node = <ArrayLiteralExpression>createNode(SyntaxKind.ArrayLiteralExpression);
node.elements = createNodeArray(elements);
node.elements = parenthesizeListElements(createNodeArray(elements));
return node;
}
@ -322,14 +322,16 @@ namespace ts {
export function createCall(expression: Expression, argumentsArray: Expression[], location?: TextRange) {
const node = <CallExpression>createNode(SyntaxKind.CallExpression, location);
node.expression = parenthesizeForAccess(expression);
node.arguments = createNodeArray(argumentsArray);
node.arguments = parenthesizeListElements(createNodeArray(argumentsArray));
return node;
}
export function createNew(expression: Expression, argumentsArray: Expression[], location?: TextRange) {
const node = <NewExpression>createNode(SyntaxKind.NewExpression, location);
node.expression = parenthesizeForAccess(expression);
node.arguments = argumentsArray ? createNodeArray(argumentsArray) : undefined;
node.arguments = argumentsArray
? parenthesizeListElements(createNodeArray(argumentsArray))
: undefined;
return node;
}
@ -358,7 +360,7 @@ namespace ts {
node.parameters = createNodeArray(parameters);
node.type = undefined;
node.equalsGreaterThanToken = createNode(SyntaxKind.EqualsGreaterThanToken);
node.body = body;
node.body = parenthesizeConciseBody(body);
return node;
}
@ -414,7 +416,7 @@ namespace ts {
export function createSpread(expression: Expression) {
const node = <SpreadElementExpression>createNode(SyntaxKind.SpreadElementExpression);
node.expression = expression;
node.expression = parenthesizeExpressionForList(expression);
return node;
}
@ -424,8 +426,8 @@ namespace ts {
node.modifiers = undefined;
node.name = name;
node.typeParameters = undefined;
node.heritageClauses = createSynthesizedNodeArray(heritageClauses);
node.members = createSynthesizedNodeArray(members);
node.heritageClauses = createNodeArray(heritageClauses);
node.members = createNodeArray(members);
return node;
}
@ -469,7 +471,7 @@ namespace ts {
export function createVariableDeclaration(name: string | BindingPattern | Identifier, initializer?: Expression, location?: TextRange): VariableDeclaration {
const node = <VariableDeclaration>createNode(SyntaxKind.VariableDeclaration, location);
node.name = typeof name === "string" ? createIdentifier(name) : name;
node.initializer = initializer;
node.initializer = initializer !== undefined ? parenthesizeExpressionForList(initializer) : undefined;
return node;
}
@ -479,7 +481,7 @@ namespace ts {
export function createStatement(expression: Expression, location?: TextRange): ExpressionStatement {
const node = <ExpressionStatement>createNode(SyntaxKind.ExpressionStatement, location);
node.expression = expression;
node.expression = parenthesizeExpressionForExpressionStatement(expression);
return node;
}
@ -562,8 +564,8 @@ namespace ts {
setModifiers(node, modifiers);
node.name = name;
node.typeParameters = undefined;
node.heritageClauses = createSynthesizedNodeArray(heritageClauses);
node.members = createSynthesizedNodeArray(members);
node.heritageClauses = createNodeArray(heritageClauses);
node.members = createNodeArray(members);
return node;
}
@ -599,7 +601,14 @@ namespace ts {
export function createHeritageClause(token: SyntaxKind, types: ExpressionWithTypeArguments[], location?: TextRange) {
const node = <HeritageClause>createNode(SyntaxKind.HeritageClause, location);
node.token = token;
node.types = createSynthesizedNodeArray(types);
node.types = createNodeArray(types);
return node;
}
export function createCaseClause(expression: Expression, statements: Statement[], location?: TextRange) {
const node = <CaseClause>createNode(SyntaxKind.CaseClause, location);
node.expression = parenthesizeExpressionForList(expression);
node.statements = createNodeArray(statements);
return node;
}
@ -609,7 +618,7 @@ namespace ts {
const node = <PropertyAssignment>createNode(SyntaxKind.PropertyAssignment, location);
node.name = typeof name === "string" ? createIdentifier(name) : name;
node.questionToken = undefined;
node.initializer = initializer;
node.initializer = initializer !== undefined ? parenthesizeExpressionForList(initializer) : undefined;
return node;
}
@ -1206,6 +1215,26 @@ namespace ts {
: createParen(operand, /*location*/ operand);
}
function parenthesizeListElements(elements: NodeArray<Expression>) {
let result: Expression[];
for (let i = 0; i < elements.length; i++) {
const element = parenthesizeExpressionForList(elements[i]);
if (result !== undefined || element !== elements[i]) {
if (result === undefined) {
result = elements.slice(0, i);
}
result.push(element);
}
}
if (result !== undefined) {
return createNodeArray(result, elements, elements.hasTrailingComma);
}
return elements;
}
export function parenthesizeExpressionForList(expression: Expression) {
const expressionPrecedence = getExpressionPrecedence(expression);
const commaPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, SyntaxKind.CommaToken);
@ -1231,6 +1260,14 @@ namespace ts {
return expression;
}
export function parenthesizeConciseBody(body: ConciseBody): ConciseBody {
if (body.kind === SyntaxKind.ObjectLiteralExpression) {
return createParen(<Expression>body, /*location*/ body);
}
return body;
}
function getLeftmostExpression(node: Expression): Expression {
while (true) {
switch (node.kind) {

View File

@ -1,6 +1,7 @@
/// <reference path="sys.ts" />
/// <reference path="emitter.ts" />
/// <reference path="core.ts" />
/// <reference path="printer.ts" />
namespace ts {
/* @internal */ export let programTime = 0;

View File

@ -67,6 +67,11 @@ namespace ts {
// Source map data
let sourceMapData: SourceMapData;
// This keeps track of the number of times `disable` has been called without a
// corresponding call to `enable`. As long as this value is non-zero, mappings will not
// be recorded.
// This is primarily used to provide a better experience when debugging binding
// patterns and destructuring assignments for simple expressions.
let disableDepth: number;
return {
@ -160,12 +165,18 @@ namespace ts {
disableDepth = 0;
}
/**
* Re-enables the recording of mappings.
*/
function enable() {
if (disableDepth > 0) {
disableDepth--;
}
}
/**
* Disables the recording of mappings.
*/
function disable() {
disableDepth++;
}

View File

@ -1,15 +1,47 @@
/// <reference path="visitor.ts" />
/// <reference path="transformers/ts.ts" />
/// <reference path="transformers/jsx.ts" />
/// <reference path="transformers/es7.ts" />
/// <reference path="transformers/es6.ts" />
/// <reference path="transformers/module/module.ts" />
/// <reference path="transformers/module/system.ts" />
/// <reference path="transformers/module/es6.ts" />
/* @internal */
namespace ts {
const moduleTransformerMap: Map<Transformer> = {
[ModuleKind.ES6]: transformES6Module,
[ModuleKind.System]: transformSystemModule,
[ModuleKind.AMD]: transformModule,
[ModuleKind.CommonJS]: transformModule,
[ModuleKind.UMD]: transformModule,
[ModuleKind.None]: transformModule,
};
const enum SyntaxKindFeatureFlags {
ExpressionSubstitution = 1 << 0,
EmitNotifications = 1 << 1,
}
export function getTransformers(compilerOptions: CompilerOptions) {
const jsx = compilerOptions.jsx;
const languageVersion = getEmitScriptTarget(compilerOptions);
const moduleKind = getEmitModuleKind(compilerOptions);
const transformers: Transformer[] = [];
// TODO(rbuckton): Add transformers
transformers.push(transformTypeScript);
transformers.push(moduleTransformerMap[moduleKind]);
if (jsx === JsxEmit.React) {
transformers.push(transformJsx);
}
transformers.push(transformES7);
if (languageVersion < ScriptTarget.ES6) {
transformers.push(transformES6);
}
return transformers;
}
@ -75,9 +107,7 @@ namespace ts {
}
currentSourceFile = sourceFile;
const visited = transformation(sourceFile);
currentSourceFile = undefined;
return visited;
return transformation(sourceFile);
}
function enableExpressionSubstitution(kind: SyntaxKind) {

View File

@ -18,22 +18,29 @@ namespace ts {
recordTempVariable: (node: Identifier) => void,
visitor?: (node: Node) => Node) {
let location: TextRange = node;
let value = node.right;
if (isEmptyObjectLiteralOrArrayLiteral(node.left)) {
return value;
return node.right;
}
let location: TextRange = node;
let value = node.right;
const expressions: Expression[] = [];
if (needsValue) {
// Temporary assignment needed to emit root should highlight whole binary expression
value = ensureIdentifier(node.right, /*reuseIdentifierExpressions*/ true, node, emitTempVariableAssignment);
// If the right-hand value of the destructuring assignment needs to be preserved (as
// is the case when the destructuring assignmen) is part of a larger expression),
// then we need to cache the right-hand value.
//
// The source map location for the assignment should point to the entire binary
// expression.
value = ensureIdentifier(node.right, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment);
}
else if (nodeIsSynthesized(node)) {
// Source map node for root.left = root.right is root
// but if root is synthetic, which could be in below case, use the target which is { a }
// for ({a} of {a: string}) {
// }
// Generally, the source map location for a destructuring assignment is the root
// expression.
//
// However, if the root expression is synthesized (as in the case
// of the initializer when transforming a ForOfStatement), then the source map
// location should point to the right-hand value of the expression.
location = node.right;
}
@ -190,7 +197,7 @@ namespace ts {
}
if (isBinaryExpression(root)) {
emitDestructuringAssignment(root.left, value, location)
emitDestructuringAssignment(root.left, value, location);
}
else {
emitBindingElement(root, value);
@ -255,21 +262,22 @@ namespace ts {
function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) {
const elements = target.elements;
if (elements.length !== 1) {
const numElements = elements.length;
if (numElements !== 1) {
// For anything but a single element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once.
// When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment);
}
for (let i = 0; i < elements.length; i++) {
for (let i = 0; i < numElements; i++) {
const e = elements[i];
if (e.kind !== SyntaxKind.OmittedExpression) {
// Assignment for target = value.propName should highligh whole property, hence use e as source map node
if (e.kind !== SyntaxKind.SpreadElementExpression) {
emitDestructuringAssignment(e, createElementAccess(value, createLiteral(i)), e);
}
else if (i === elements.length - 1) {
else if (i === numElements - 1) {
emitDestructuringAssignment((<SpreadElementExpression>e).expression, createArraySlice(value, i), e);
}
}
@ -299,11 +307,11 @@ namespace ts {
// so in that case, we'll intentionally create that temporary.
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ numElements !== 0, target, emitTempVariableAssignment);
}
for (let i = 0; i < elements.length; i++) {
let element = elements[i];
for (let i = 0; i < numElements; i++) {
const element = elements[i];
if (name.kind === SyntaxKind.ObjectBindingPattern) {
// Rewrite element to a declaration with an initializer that fetches property
let propName = element.propertyName || <Identifier>element.name;
const propName = element.propertyName || <Identifier>element.name;
emitBindingElement(element, createDestructuringPropertyAccess(value, propName));
}
else if (element.kind !== SyntaxKind.OmittedExpression) {
@ -311,7 +319,7 @@ namespace ts {
// Rewrite element to a declaration that accesses array element at index i
emitBindingElement(element, createElementAccess(value, i));
}
else if (i === elements.length - 1) {
else if (i === numElements - 1) {
emitBindingElement(element, createArraySlice(value, i));
}
}
@ -332,16 +340,23 @@ namespace ts {
);
}
function createDestructuringPropertyAccess(object: Expression, propertyName: PropertyName): LeftHandSideExpression {
/**
* Creates either a PropertyAccessExpression or an ElementAccessExpression for the
* right-hand side of a transformed destructuring assignment.
*
* @param expression The right-hand expression that is the source of the property.
* @param propertyName The destructuring property name.
*/
function createDestructuringPropertyAccess(expression: Expression, propertyName: PropertyName): LeftHandSideExpression {
if (isComputedPropertyName(propertyName)) {
return createElementAccess(
object,
ensureIdentifier(propertyName.expression, /*reuseIdentifierExpressions*/ false, propertyName, emitTempVariableAssignment)
expression,
ensureIdentifier(propertyName.expression, /*reuseIdentifierExpressions*/ false, /*location*/ propertyName, emitTempVariableAssignment)
);
}
else if (isIdentifier(propertyName)) {
return createPropertyAccess(
object,
expression,
propertyName.text
);
}
@ -349,7 +364,7 @@ namespace ts {
// We create a synthetic copy of the identifier in order to avoid the rewriting that might
// otherwise occur when the identifier is emitted.
return createElementAccess(
object,
expression,
cloneNode(propertyName)
);
}

View File

@ -5,8 +5,6 @@
namespace ts {
// TODO(rbuckton): ES7->ES6 transformer
export function transformES7(context: TransformationContext) {
const { hoistVariableDeclaration } = context;
return transformSourceFile;
function transformSourceFile(node: SourceFile) {

View File

@ -6,10 +6,8 @@
namespace ts {
type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration;
// TODO(rbuckton): TS->ES7 transformer
export function transformTypeScript(context: TransformationContext) {
const {
nodeHasGeneratedName,
getGeneratedNameForNode,
makeUniqueName,
setNodeEmitFlags,
@ -21,24 +19,46 @@ namespace ts {
const resolver = context.getEmitResolver();
const compilerOptions = context.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
const decoratedClassAliases: Map<Identifier> = {};
const currentDecoratedClassAliases: Map<Identifier> = {};
// Save the previous transformation hooks.
const previousExpressionSubstitution = context.expressionSubstitution;
const previousOnBeforeEmitNode = context.onBeforeEmitNode;
const previousOnAfterEmitNode = context.onAfterEmitNode;
context.enableExpressionSubstitution(SyntaxKind.Identifier);
// Set new transformation hooks.
context.expressionSubstitution = substituteExpression;
context.onBeforeEmitNode = onBeforeEmitNode;
context.onAfterEmitNode = onAfterEmitNode;
let hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = false;
// These variables contain state that changes as we descend into the tree.
let currentSourceFile: SourceFile;
let currentNamespace: ModuleDeclaration;
let currentNamespaceLocalName: Identifier;
let currentScope: SourceFile | Block | ModuleBlock | CaseBlock;
let currentParent: Node;
let currentNode: Node;
let isRightmostExpression: boolean;
// These variables keep track of whether expression substitution has been enabled for
// specific edge cases. They are persisted between each SourceFile transformation and
// should not be reset.
let hasEnabledExpressionSubstitutionForDecoratedClasses = false;
let hasEnabledExpressionSubstitutionForNamespaceExports = false;
let hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = false;
// This map keeps track of aliases created for classes with decorators to avoid issues
// with the double-binding behavior of classes.
let decoratedClassAliases: Map<Identifier>;
// This map keeps track of currently active aliases defined in `decoratedClassAliases`
// when just-in-time substitution occurs while printing an expression identifier.
let currentDecoratedClassAliases: Map<Identifier>;
// This value keeps track of how deeply nested we are within any containing namespaces
// when performing just-in-time substitution while printing an expression identifier.
let namespaceNestLevel: number;
// This array keeps track of containers where `super` is valid, for use with
// just-in-time substitution for `super` expressions inside of async methods.
let superContainerStack: SuperContainer[];
return transformSourceFile;
@ -47,7 +67,6 @@ namespace ts {
currentSourceFile = node;
node = visitEachChild(node, visitor, context);
setNodeEmitFlags(node, NodeEmitFlags.EmitEmitHelpers);
currentSourceFile = undefined;
return node;
}
@ -57,18 +76,23 @@ namespace ts {
* @param node The node to visit.
*/
function visitWithStack(node: Node, visitor: (node: Node) => Node): Node {
// Save state
const savedCurrentNamespace = currentNamespace;
const savedCurrentScope = currentScope;
const savedCurrentParent = currentParent;
const savedCurrentNode = currentNode;
const savedIsRightmostExpression = isRightmostExpression;
// Handle state changes before visiting a node.
onBeforeVisitNode(node);
node = visitor(node);
// Restore state
currentNamespace = savedCurrentNamespace;
currentScope = savedCurrentScope;
currentParent = savedCurrentParent;
currentNode = savedCurrentNode;
isRightmostExpression = savedIsRightmostExpression;
return node;
}
@ -151,6 +175,11 @@ namespace ts {
return visitorWorker(node);
}
/**
* Branching visitor, visits a TypeScript syntax node.
*
* @param node The node to visit.
*/
function visitTypeScript(node: Node): Node {
// TypeScript ambient declarations are elided.
if (node.flags & NodeFlags.Ambient) {
@ -207,7 +236,7 @@ namespace ts {
case SyntaxKind.Constructor:
// TypeScript constructors are elided. The constructor of a class will be
// reordered to the start of the member list in `transformClassDeclaration`.
// transformed as part of `transformClassDeclaration`.
return undefined;
case SyntaxKind.ClassDeclaration:
@ -334,11 +363,6 @@ namespace ts {
currentScope = <SourceFile | CaseBlock | ModuleBlock | Block>node;
break;
}
// Keep track of whether this is the right-most expression of an expression tree.
// This is used to determine whether to parenthesize an `await` expression transformed
// into a `yield` expression.
isRightmostExpression = currentParent && isRightmost(currentParent, currentNode, isRightmostExpression);
}
/**
@ -471,11 +495,13 @@ namespace ts {
// Record an alias to avoid class double-binding.
if (resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.ClassWithBodyScopedClassBinding) {
enableExpressionSubstitutionForDecoratedClasses();
decoratedClassAlias = makeUniqueName(node.name ? node.name.text : "default");
decoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAlias;
// We emit the class alias as a `let` declaration here so that it has the same
// TDZ as the class.
// let ${decoratedClassAlias};
addNode(statements,
createVariableStatement(
@ -538,7 +564,7 @@ namespace ts {
// Write any decorators of the node.
addNodes(statements, generateClassElementDecorationStatements(node, /*isStatic*/ false));
addNodes(statements, generateClassElementDecorationStatements(node, /*isStatic*/ true));
addNode(statements, generateConstructorDecorationStatement(node, decoratedClassAlias))
addNode(statements, generateConstructorDecorationStatement(node, decoratedClassAlias));
// If the class is exported as part of a TypeScript namespace, emit the namespace export.
// Otherwise, if the class was exported at the top level and was decorated, emit an export
@ -567,7 +593,7 @@ namespace ts {
*
* @param node The node to transform.
*/
function visitClassExpression(node: ClassExpression): LeftHandSideExpression {
function visitClassExpression(node: ClassExpression): Expression {
const staticProperties = getInitializedProperties(node, /*isStatic*/ true);
const heritageClauses = visitNodes(node.heritageClauses, visitor, isHeritageClause);
const members = transformClassMembers(node, heritageClauses !== undefined);
@ -598,7 +624,7 @@ namespace ts {
addNode(expressions, createAssignment(temp, classExpression));
addNodes(expressions, generateInitializedPropertyExpressions(node, staticProperties, temp));
addNode(expressions, temp);
return createParen(inlineExpressions(expressions));
return inlineExpressions(expressions);
}
return classExpression;
@ -1070,7 +1096,7 @@ namespace ts {
* Transforms all of the decorators for a declaration into an array of expressions.
*
* @param node The declaration node.
* @param allDecorators The AllDecorators object for the node.
* @param allDecorators An object containing all of the decorators for the declaration.
*/
function transformAllDecoratorsOfDeclaration(node: Declaration, allDecorators: AllDecorators) {
if (!allDecorators) {
@ -1267,13 +1293,13 @@ namespace ts {
*/
function addTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) {
if (compilerOptions.emitDecoratorMetadata) {
if (shouldAppendTypeMetadata(node)) {
if (shouldAddTypeMetadata(node)) {
decoratorExpressions.push(createMetadataHelper("design:type", serializeTypeOfNode(node), /*defer*/ true));
}
if (shouldAppendParamTypesMetadata(node)) {
if (shouldAddParamTypesMetadata(node)) {
decoratorExpressions.push(createMetadataHelper("design:paramtypes", serializeParameterTypesOfNode(node), /*defer*/ true));
}
if (shouldAppendReturnTypeMetadata(node)) {
if (shouldAddReturnTypeMetadata(node)) {
decoratorExpressions.push(createMetadataHelper("design:returntype", serializeReturnTypeOfNode(node), /*defer*/ true));
}
}
@ -1281,12 +1307,12 @@ namespace ts {
/**
* Determines whether to emit the "design:type" metadata based on the node's kind.
* The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata
* compiler option is set.
* The caller should have already tested whether the node has decorators and whether the
* emitDecoratorMetadata compiler option is set.
*
* @param node The node to test.
*/
function shouldAppendTypeMetadata(node: Declaration): boolean {
function shouldAddTypeMetadata(node: Declaration): boolean {
const kind = node.kind;
return kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
@ -1296,23 +1322,23 @@ namespace ts {
/**
* Determines whether to emit the "design:returntype" metadata based on the node's kind.
* The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata
* compiler option is set.
* The caller should have already tested whether the node has decorators and whether the
* emitDecoratorMetadata compiler option is set.
*
* @param node The node to test.
*/
function shouldAppendReturnTypeMetadata(node: Declaration): boolean {
function shouldAddReturnTypeMetadata(node: Declaration): boolean {
return node.kind === SyntaxKind.MethodDeclaration;
}
/**
* Determines whether to emit the "design:paramtypes" metadata based on the node's kind.
* The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata
* compiler option is set.
* The caller should have already tested whether the node has decorators and whether the
* emitDecoratorMetadata compiler option is set.
*
* @param node The node to test.
*/
function shouldAppendParamTypesMetadata(node: Declaration): boolean {
function shouldAddParamTypesMetadata(node: Declaration): boolean {
const kind = node.kind;
return kind === SyntaxKind.ClassDeclaration
|| kind === SyntaxKind.ClassExpression
@ -1442,7 +1468,7 @@ namespace ts {
case SyntaxKind.TypePredicate:
case SyntaxKind.BooleanKeyword:
return createIdentifier("Boolean")
return createIdentifier("Boolean");
case SyntaxKind.StringKeyword:
case SyntaxKind.StringLiteral:
@ -1568,7 +1594,7 @@ namespace ts {
* qualified name at runtime.
*/
function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): Expression {
let left: Expression
let left: Expression;
if (node.left.kind === SyntaxKind.Identifier) {
left = serializeEntityNameAsExpression(node.left, useFallback);
}
@ -1974,7 +2000,7 @@ namespace ts {
)
);
const block = createBlock(statements);
const block = createBlock(statements, /*location*/ node.body);
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
// This step isn't needed if we eventually transform this to ES5.
@ -2000,25 +2026,6 @@ namespace ts {
}
}
function enableExpressionSubstitutionForAsyncMethodsWithSuper() {
if (!hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper) {
hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = true;
// We need to enable substitutions for call, property access, and element access
// if we need to rewrite super calls.
context.enableExpressionSubstitution(SyntaxKind.CallExpression);
context.enableExpressionSubstitution(SyntaxKind.PropertyAccessExpression);
context.enableExpressionSubstitution(SyntaxKind.ElementAccessExpression);
// We need to be notified when entering and exiting declarations that bind super.
context.enableEmitNotification(SyntaxKind.ClassDeclaration);
context.enableEmitNotification(SyntaxKind.MethodDeclaration);
context.enableEmitNotification(SyntaxKind.GetAccessor);
context.enableEmitNotification(SyntaxKind.SetAccessor);
context.enableEmitNotification(SyntaxKind.Constructor);
}
}
/**
* Visits a parameter declaration node.
*
@ -2198,7 +2205,7 @@ namespace ts {
* @param member The enum member node.
*/
function transformEnumMemberDeclarationValue(member: EnumMember): Expression {
let value = resolver.getConstantValue(member);
const value = resolver.getConstantValue(member);
if (value !== undefined) {
return createLiteral(value);
}
@ -2229,12 +2236,10 @@ namespace ts {
* @param node The await expression node.
*/
function visitAwaitExpression(node: AwaitExpression): Expression {
const expression = createYield(
return createYield(
visitNode(node.expression, visitor, isExpression),
node
);
return isRightmostExpression ? expression : createParen(expression);
}
/**
@ -2286,6 +2291,8 @@ namespace ts {
Debug.assert(isIdentifier(node.name));
enableExpressionSubstitutionForNamespaceExports();
const savedCurrentNamespaceLocalName = currentNamespaceLocalName;
const modifiers = visitNodes(node.modifiers, visitor, isModifier);
const statements: Statement[] = [];
@ -2483,19 +2490,6 @@ namespace ts {
return createStatement(expression, /*location*/ undefined);
}
function createExportStatement(node: ClassExpression | ClassDeclaration | FunctionDeclaration): Statement {
const name = getDeclarationName(node);
if (currentNamespace) {
return createNamespaceExport(name, name);
}
else if (node.flags & NodeFlags.Default) {
return createExportDefault(name);
}
else {
return createModuleExport(name);
}
}
function createNamespaceExport(exportName: Identifier, exportValue: Expression, location?: TextRange) {
return createStatement(
createAssignment(
@ -2518,7 +2512,7 @@ namespace ts {
name = getSynthesizedNode(name);
return currentNamespaceLocalName
? createPropertyAccess(currentNamespaceLocalName, name)
: name
: name;
}
function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration) {
@ -2535,8 +2529,65 @@ namespace ts {
: getClassPrototype(node);
}
function onBeforeEmitNode(node: Node): void {
previousOnBeforeEmitNode(node);
const kind = node.kind;
if (hasEnabledExpressionSubstitutionForDecoratedClasses
&& kind === SyntaxKind.ClassDeclaration && node.decorators) {
currentDecoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAliases[getOriginalNodeId(node)];
}
if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper
&& (kind === SyntaxKind.ClassDeclaration
|| kind === SyntaxKind.Constructor
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor)) {
if (!superContainerStack) {
superContainerStack = [];
}
superContainerStack.push(<SuperContainer>node);
}
if (hasEnabledExpressionSubstitutionForNamespaceExports
&& kind === SyntaxKind.ModuleDeclaration) {
namespaceNestLevel++;
}
}
function onAfterEmitNode(node: Node): void {
previousOnAfterEmitNode(node);
const kind = node.kind;
if (hasEnabledExpressionSubstitutionForDecoratedClasses
&& kind === SyntaxKind.ClassDeclaration && node.decorators) {
currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined;
}
if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper
&& (kind === SyntaxKind.ClassDeclaration
|| kind === SyntaxKind.Constructor
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor)) {
if (superContainerStack) {
superContainerStack.pop();
}
}
if (hasEnabledExpressionSubstitutionForNamespaceExports
&& kind === SyntaxKind.ModuleDeclaration) {
namespaceNestLevel--;
}
}
function substituteExpression(node: Expression): Expression {
node = previousExpressionSubstitution ? previousExpressionSubstitution(node) : node;
node = previousExpressionSubstitution(node);
switch (node.kind) {
case SyntaxKind.Identifier:
return substituteExpressionIdentifier(<Identifier>node);
@ -2556,8 +2607,10 @@ namespace ts {
return node;
}
function substituteExpressionIdentifier(node: Identifier) {
if (!nodeIsSynthesized(node) && resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) {
function substituteExpressionIdentifier(node: Identifier): Expression {
if (hasEnabledExpressionSubstitutionForDecoratedClasses
&& !nodeIsSynthesized(node)
&& resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) {
// Due to the emit for class decorators, any reference to the class from inside of the class body
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
// behavior of class names in ES6.
@ -2571,10 +2624,23 @@ namespace ts {
}
}
if (hasEnabledExpressionSubstitutionForNamespaceExports
&& namespaceNestLevel > 0) {
// If we are nested within a namespace declaration, we may need to qualifiy
// an identifier that is exported from a merged namespace.
const original = getOriginalNode(node);
if (isIdentifier(original) && original.parent) {
const container = resolver.getReferencedExportContainer(original);
if (container && container.kind === SyntaxKind.ModuleDeclaration) {
return createPropertyAccess(getGeneratedNameForNode(container), node, /*location*/ node);
}
}
}
return node;
}
function substituteCallExpression(node: CallExpression) {
function substituteCallExpression(node: CallExpression): Expression {
const expression = node.expression;
if (isSuperPropertyOrElementAccess(expression)) {
const flags = getSuperContainerAsyncMethodFlags();
@ -2624,6 +2690,54 @@ namespace ts {
return node;
}
function enableExpressionSubstitutionForAsyncMethodsWithSuper() {
if (!hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper) {
hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = true;
// We need to enable substitutions for call, property access, and element access
// if we need to rewrite super calls.
context.enableExpressionSubstitution(SyntaxKind.CallExpression);
context.enableExpressionSubstitution(SyntaxKind.PropertyAccessExpression);
context.enableExpressionSubstitution(SyntaxKind.ElementAccessExpression);
// We need to be notified when entering and exiting declarations that bind super.
context.enableEmitNotification(SyntaxKind.ClassDeclaration);
context.enableEmitNotification(SyntaxKind.MethodDeclaration);
context.enableEmitNotification(SyntaxKind.GetAccessor);
context.enableEmitNotification(SyntaxKind.SetAccessor);
context.enableEmitNotification(SyntaxKind.Constructor);
}
}
function enableExpressionSubstitutionForDecoratedClasses() {
if (!hasEnabledExpressionSubstitutionForDecoratedClasses) {
hasEnabledExpressionSubstitutionForDecoratedClasses = true;
// We need to enable substitutions for identifiers. This allows us to
// substitute class names inside of a class declaration.
context.enableExpressionSubstitution(SyntaxKind.Identifier);
// Keep track of class aliases.
decoratedClassAliases = {};
currentDecoratedClassAliases = {};
}
}
function enableExpressionSubstitutionForNamespaceExports() {
if (!hasEnabledExpressionSubstitutionForNamespaceExports) {
hasEnabledExpressionSubstitutionForNamespaceExports = true;
// We need to enable substitutions for identifiers. This allows us to
// substitute the names of exported members of a namespace.
context.enableExpressionSubstitution(SyntaxKind.Identifier);
// We need to be notified when entering and exiting namespaces.
context.enableEmitNotification(SyntaxKind.ModuleDeclaration);
// Keep track of namespace nesting depth
namespaceNestLevel = 0;
}
}
function createSuperAccessInAsyncMethod(argumentExpression: Expression, flags: NodeCheckFlags, location: TextRange): LeftHandSideExpression {
if (flags & NodeCheckFlags.AsyncMethodWithSuperBinding) {
@ -2650,88 +2764,5 @@ namespace ts {
return container !== undefined
&& resolver.getNodeCheckFlags(getOriginalNode(container)) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding);
}
function onBeforeEmitNode(node: Node): void {
const kind = node.kind;
if (kind === SyntaxKind.ClassDeclaration && node.decorators) {
currentDecoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAliases[getOriginalNodeId(node)];
}
if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper
&& (kind === SyntaxKind.ClassDeclaration
|| kind === SyntaxKind.Constructor
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor)) {
if (!superContainerStack) {
superContainerStack = [];
}
superContainerStack.push(<SuperContainer>node);
}
}
function onAfterEmitNode(node: Node): void {
const kind = node.kind;
if (kind === SyntaxKind.ClassDeclaration && node.decorators) {
currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined;
}
if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper
&& (kind === SyntaxKind.ClassDeclaration
|| kind === SyntaxKind.Constructor
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor)) {
if (superContainerStack) {
superContainerStack.pop();
}
}
}
function isRightmost(parentNode: Node, node: Node, wasRightmost: boolean) {
switch (parentNode.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Parameter:
case SyntaxKind.BindingElement:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
case SyntaxKind.ExpressionStatement:
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.ThrowStatement:
case SyntaxKind.ReturnStatement:
case SyntaxKind.ForStatement:
case SyntaxKind.ForOfStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.SwitchStatement:
case SyntaxKind.CaseClause:
case SyntaxKind.WithStatement:
case SyntaxKind.Decorator:
case SyntaxKind.ExpressionWithTypeArguments:
case SyntaxKind.SpreadElementExpression:
case SyntaxKind.AsExpression:
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.EnumMember:
case SyntaxKind.JsxAttribute:
case SyntaxKind.JsxSpreadAttribute:
case SyntaxKind.JsxExpression:
return true;
case SyntaxKind.TemplateSpan:
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return node !== (<CallExpression | NewExpression>parentNode).expression;
case SyntaxKind.BinaryExpression:
return wasRightmost && node === (<BinaryExpression>parentNode).right;
case SyntaxKind.ConditionalExpression:
return wasRightmost && node === (<ConditionalExpression>parentNode).whenTrue;
default:
return wasRightmost;
}
}
}
}

View File

@ -2470,6 +2470,7 @@ namespace ts {
allowJs?: boolean;
/* @internal */ stripInternal?: boolean;
/* @internal */ experimentalTransforms?: boolean;
/* @internal */ transformCompatibleEmit?: boolean;
// Skip checking lib.d.ts to help speed up tests.
/* @internal */ skipDefaultLibCheck?: boolean;

View File

@ -2792,7 +2792,7 @@ namespace ts {
else if (kind === SyntaxKind.ConditionalExpression) {
return isSimpleExpressionWorker((<ConditionalExpression>node).condition, depth + 1)
&& isSimpleExpressionWorker((<ConditionalExpression>node).whenTrue, depth + 1)
&& isSimpleExpressionWorker((<ConditionalExpression>node).whenFalse, depth + 1)
&& isSimpleExpressionWorker((<ConditionalExpression>node).whenFalse, depth + 1);
}
else if (kind === SyntaxKind.VoidExpression
|| kind === SyntaxKind.TypeOfExpression

View File

@ -1,4 +1,5 @@
/// <reference path="checker.ts" />
/// <reference path="factory.ts" />
/* @internal */
namespace ts {
@ -163,7 +164,7 @@ namespace ts {
{ name: "typeParameters", test: isTypeParameter },
{ name: "parameters", test: isParameter },
{ name: "type", test: isTypeNode, optional: true },
{ name: "body", test: isConciseBody, lift: liftToBlock },
{ name: "body", test: isConciseBody, lift: liftToBlock, parenthesize: parenthesizeConciseBody },
],
[SyntaxKind.DeleteExpression]: [
{ name: "expression", test: isUnaryExpression, parenthesize: parenthesizePrefixOperand },
@ -408,7 +409,7 @@ namespace ts {
{ name: "expression", test: isExpression, optional: true },
],
[SyntaxKind.CaseClause]: [
{ name: "expression", test: isExpression },
{ name: "expression", test: isExpression, parenthesize: parenthesizeExpressionForList },
{ name: "statements", test: isStatement },
],
[SyntaxKind.DefaultClause]: [
@ -478,25 +479,46 @@ namespace ts {
* @param lift An optional callback to execute to lift a NodeArrayNode into a valid Node.
*/
export function visitNode<T extends Node>(node: T, visitor: (node: Node) => Node, test: (node: Node) => boolean, optional?: boolean, lift?: (node: NodeArray<Node>) => T): T {
return <T>visitNodeWorker(node, visitor, test, optional, lift, /*parenthesize*/ undefined, /*parentNode*/ undefined);
}
/**
* Visits a Node using the supplied visitor, possibly returning a new Node in its place.
*
* @param node The Node to visit.
* @param visitor The callback used to visit the Node.
* @param test A callback to execute to verify the Node is valid.
* @param optional A value indicating whether the Node is itself optional.
* @param lift A callback to execute to lift a NodeArrayNode into a valid Node.
* @param parenthesize A callback used to parenthesize the node if needed.
* @param parentNode A parentNode for the node.
*/
function visitNodeWorker(node: Node, visitor: (node: Node) => Node, test: (node: Node) => boolean, optional: boolean, lift: (node: NodeArray<Node>) => Node, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node): Node {
if (node === undefined) {
return undefined;
}
const visited = visitor(node);
let visited = visitor(node);
if (visited === node) {
return node;
}
const lifted = liftNode(visited, lift);
if (lifted === undefined) {
if (visited !== undefined && isNodeArrayNode(visited)) {
visited = (lift || extractSingleNode)((<NodeArrayNode<Node>>visited).nodes);
}
if (parenthesize !== undefined && visited !== undefined) {
visited = parenthesize(visited, parentNode);
}
if (visited === undefined) {
Debug.assert(optional, "Node not optional.");
return undefined;
}
Debug.assert(test === undefined || test(visited), "Wrong node type after visit.");
aggregateTransformFlags(visited);
visited.original = node;
return <T>visited;
return visited;
}
/**
@ -509,11 +531,24 @@ namespace ts {
* @param count An optional value indicating the maximum number of nodes to visit.
*/
export function visitNodes<T extends Node, TArray extends NodeArray<T>>(nodes: TArray, visitor: (node: Node) => Node, test: (node: Node) => boolean, start?: number, count?: number): TArray {
return <TArray>visitNodesWorker(nodes, visitor, test, /*parenthesize*/ undefined, /*parentNode*/ undefined, start, count);
}
/**
* Visits a NodeArray using the supplied visitor, possibly returning a new NodeArray in its place.
*
* @param nodes The NodeArray to visit.
* @param visitor The callback used to visit a Node.
* @param test A node test to execute for each node.
* @param start An optional value indicating the starting offset at which to start visiting.
* @param count An optional value indicating the maximum number of nodes to visit.
*/
function visitNodesWorker(nodes: NodeArray<Node>, visitor: (node: Node) => Node, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, start: number, count: number): NodeArray<Node> {
if (nodes === undefined) {
return undefined;
}
let updated: T[];
let updated: Node[];
// Ensure start and count have valid values
const length = nodes.length;
@ -533,25 +568,21 @@ namespace ts {
// Visit each original node.
for (let i = 0; i < count; i++) {
const node = nodes[i + start];
const visited = node && <OneOrMany<T>>visitor(node);
const visited = node !== undefined ? visitor(node) : undefined;
if (updated !== undefined || visited === undefined || visited !== node) {
if (updated === undefined) {
// Ensure we have a copy of `nodes`, up to the current index.
updated = nodes.slice(0, i);
}
if (visited !== node) {
aggregateTransformFlags(visited);
}
addNodeWorker(updated, visited, /*addOnNewLine*/ undefined, test);
addNodeWorker(updated, visited, /*addOnNewLine*/ undefined, test, parenthesize, parentNode, /*isVisiting*/ visited !== node);
}
}
if (updated !== undefined) {
return <TArray>(isModifiersArray(nodes)
return isModifiersArray(nodes)
? createModifiersArray(updated, nodes)
: createNodeArray(updated, nodes, nodes.hasTrailingComma));
: createNodeArray(updated, nodes, nodes.hasTrailingComma);
}
return nodes;
@ -584,9 +615,17 @@ namespace ts {
for (const edge of edgeTraversalPath) {
const value = <Node | NodeArray<Node>>node[edge.name];
if (value !== undefined) {
const visited = visitEdge(edge, value, visitor);
if (visited && isArray(visited) && isModifiersArray(visited)) {
modifiers = visited.flags;
let visited: Node | NodeArray<Node>;
if (isArray(value)) {
const visitedArray = visitNodesWorker(value, visitor, edge.test, edge.parenthesize, node, 0, value.length);
if (isModifiersArray(visitedArray)) {
modifiers = visitedArray.flags;
}
visited = visitedArray;
}
else {
visited = visitNodeWorker(<Node>value, visitor, edge.test, edge.optional, edge.lift, edge.parenthesize, node);
}
if (updated !== undefined || visited !== value) {
@ -601,7 +640,7 @@ namespace ts {
}
if (visited !== value) {
setEdgeValue(updated, edge, visited);
updated[edge.name] = visited;
}
}
}
@ -627,39 +666,6 @@ namespace ts {
return updated;
}
/**
* Visits a node edge.
*
* @param edge The edge of the Node.
* @param value The Node or NodeArray value for the edge.
* @param visitor A callback used to visit the node.
*/
function visitEdge(edge: NodeEdge, value: Node | NodeArray<Node>, visitor: (node: Node) => Node) {
return isArray(value)
? visitNodes(<NodeArray<Node>>value, visitor, edge.test, /*start*/ undefined, /*count*/ undefined)
: visitNode(<Node>value, visitor, edge.test, edge.optional, edge.lift);
}
/**
* Sets the value of an edge, adjusting the value as necessary for cases such as expression precedence.
*/
function setEdgeValue(parentNode: Node & Map<any>, edge: NodeEdge, value: Node | NodeArray<Node>) {
if (value && edge.parenthesize && !isArray(value)) {
value = parenthesizeEdge(<Node>value, parentNode, edge.parenthesize, edge.test);
}
parentNode[edge.name] = value;
}
/**
* Applies parentheses to a node to ensure the correct precedence.
*/
function parenthesizeEdge(node: Node, parentNode: Node, parenthesize: (node: Node, parentNode: Node) => Node, test: (node: Node) => boolean) {
node = parenthesize(node, parentNode);
Debug.assert(test === undefined || test(node), "Unexpected node kind after visit.");
return node;
}
/**
* Appends a node to an array.
*
@ -668,7 +674,7 @@ namespace ts {
* @param test The node test used to validate each node.
*/
export function addNode<T extends Node>(to: T[], from: OneOrMany<T>, startOnNewLine?: boolean) {
addNodeWorker(to, from, startOnNewLine, /*test*/ undefined);
addNodeWorker(to, from, startOnNewLine, /*test*/ undefined, /*parenthesize*/ undefined, /*parentNode*/ undefined, /*isVisiting*/ false);
}
/**
@ -679,29 +685,38 @@ namespace ts {
* @param test The node test used to validate each node.
*/
export function addNodes<T extends Node>(to: T[], from: OneOrMany<T>[], startOnNewLine?: boolean) {
addNodesWorker(to, from, startOnNewLine, /*test*/ undefined);
addNodesWorker(to, from, startOnNewLine, /*test*/ undefined, /*parenthesize*/ undefined, /*parentNode*/ undefined, /*isVisiting*/ false);
}
function addNodeWorker<T extends Node>(to: T[], from: OneOrMany<T>, startOnNewLine: boolean, test: (node: Node) => boolean) {
function addNodeWorker(to: Node[], from: OneOrMany<Node>, startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean) {
if (to && from) {
if (isNodeArrayNode(from)) {
addNodesWorker(to, from.nodes, startOnNewLine, test);
addNodesWorker(to, from.nodes, startOnNewLine, test, parenthesize, parentNode, isVisiting);
return;
}
else {
Debug.assert(test === undefined || test(from), "Wrong node type after visit.");
if (startOnNewLine) {
from.startsOnNewLine = true;
}
to.push(from);
if (parenthesize !== undefined) {
from = parenthesize(from, parentNode);
}
Debug.assert(test === undefined || test(from), "Wrong node type after visit.");
if (startOnNewLine) {
from.startsOnNewLine = true;
}
if (isVisiting) {
aggregateTransformFlags(from);
}
to.push(from);
}
}
function addNodesWorker<T extends Node>(to: T[], from: OneOrMany<T>[], startOnNewLine: boolean, test: (node: Node) => boolean) {
function addNodesWorker(to: Node[], from: OneOrMany<Node>[], startOnNewLine: boolean, test: (node: Node) => boolean, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node, isVisiting: boolean) {
if (to && from) {
for (const node of from) {
addNodeWorker(to, node, startOnNewLine, test);
addNodeWorker(to, node, startOnNewLine, test, parenthesize, parentNode, isVisiting);
}
}
}
@ -841,26 +856,6 @@ namespace ts {
return createNodeArray(concatenate(statements, declarations), /*location*/ statements);
}
/**
* Tries to lift a NodeArrayNode to a Node. This is primarily used to
* lift multiple statements into a single Block.
*
* @param node The visited Node.
* @param options Options used to control lift behavior.
*/
function liftNode(node: Node, lifter: (nodes: NodeArray<Node>) => Node): Node {
if (node === undefined) {
return undefined;
}
else if (isNodeArrayNode(node)) {
const lift = lifter || extractSingleNode;
return lift(node.nodes);
}
else {
return node;
}
}
/**
* Lifts a NodeArray containing only Statement nodes to a block.
*

View File

@ -204,7 +204,7 @@ namespace ts.formatting {
// - parent is SourceFile - by default immediate children of SourceFile are not indented except when user indents them manually
// - parent and child are not on the same line
let useActualIndentation =
(isDeclaration(current) || isStatement(current)) &&
(isDeclaration(current) || isStatementButNotDeclaration(current)) &&
(parent.kind === SyntaxKind.SourceFile || !parentAndChildShareLine);
if (!useActualIndentation) {