refactor(compiler): replace Comment nodes with leadingComments property (#38811)

Common AST formats such as TS and Babel do not use a separate
node for comments, but instead attach comments to other AST nodes.
Previously this was worked around in TS by creating a `NotEmittedStatement`
AST node to attach the comment to. But Babel does not have this facility,
so it will not be a viable approach for the linker.

This commit refactors the output AST, to remove the `CommentStmt` and
`JSDocCommentStmt` nodes. Instead statements have a collection of
`leadingComments` that are rendered/attached to the final AST nodes
when being translated or printed.

PR Close #38811
This commit is contained in:
Pete Bacon Darwin
2020-09-11 16:43:23 +01:00
committed by Misko Hevery
parent d8657ddb5c
commit a0756e9fa4
24 changed files with 427 additions and 363 deletions

View File

@ -78,7 +78,7 @@ export * from './ml_parser/tags';
export {LexerRange} from './ml_parser/lexer';
export * from './ml_parser/xml_parser';
export {NgModuleCompiler} from './ng_module_compiler';
export {ArrayType, AssertNotNull, DYNAMIC_TYPE, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinType, BuiltinTypeName, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, literalMap, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, NONE_TYPE, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, STRING_TYPE, TypeofExpr, collectExternalReferences} from './output/output_ast';
export {ArrayType, AssertNotNull, DYNAMIC_TYPE, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinType, BuiltinTypeName, BuiltinVar, CastExpr, ClassField, ClassMethod, ClassStmt, CommaExpr, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, literalMap, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, NONE_TYPE, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, StatementVisitor, ThrowStmt, TryCatchStmt, Type, TypeVisitor, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, StmtModifier, Statement, STRING_TYPE, TypeofExpr, collectExternalReferences, jsDocComment, leadingComment, LeadingComment, JSDocComment, UnaryOperator, UnaryOperatorExpr, LocalizedString} from './output/output_ast';
export {EmitterVisitorContext} from './output/abstract_emitter';
export {JitEvaluator} from './output/output_jit';
export * from './output/ts_emitter';

View File

@ -202,13 +202,34 @@ export class EmitterVisitorContext {
export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.ExpressionVisitor {
constructor(private _escapeDollarInStrings: boolean) {}
protected printLeadingComments(stmt: o.Statement, ctx: EmitterVisitorContext): void {
if (stmt.leadingComments === undefined) {
return;
}
for (const comment of stmt.leadingComments) {
if (comment instanceof o.JSDocComment) {
ctx.print(stmt, `/*${comment.toString()}*/`, comment.trailingNewline);
} else {
if (comment.multiline) {
ctx.print(stmt, `/* ${comment.text} */`, comment.trailingNewline);
} else {
comment.text.split('\n').forEach((line) => {
ctx.println(stmt, `// ${line}`);
});
}
}
}
}
visitExpressionStmt(stmt: o.ExpressionStatement, ctx: EmitterVisitorContext): any {
this.printLeadingComments(stmt, ctx);
stmt.expr.visitExpression(this, ctx);
ctx.println(stmt, ';');
return null;
}
visitReturnStmt(stmt: o.ReturnStatement, ctx: EmitterVisitorContext): any {
this.printLeadingComments(stmt, ctx);
ctx.print(stmt, `return `);
stmt.value.visitExpression(this, ctx);
ctx.println(stmt, ';');
@ -220,6 +241,7 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
abstract visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any;
visitIfStmt(stmt: o.IfStmt, ctx: EmitterVisitorContext): any {
this.printLeadingComments(stmt, ctx);
ctx.print(stmt, `if (`);
stmt.condition.visitExpression(this, ctx);
ctx.print(stmt, `) {`);
@ -248,25 +270,12 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
abstract visitTryCatchStmt(stmt: o.TryCatchStmt, ctx: EmitterVisitorContext): any;
visitThrowStmt(stmt: o.ThrowStmt, ctx: EmitterVisitorContext): any {
this.printLeadingComments(stmt, ctx);
ctx.print(stmt, `throw `);
stmt.error.visitExpression(this, ctx);
ctx.println(stmt, `;`);
return null;
}
visitCommentStmt(stmt: o.CommentStmt, ctx: EmitterVisitorContext): any {
if (stmt.multiline) {
ctx.println(stmt, `/* ${stmt.comment} */`);
} else {
stmt.comment.split('\n').forEach((line) => {
ctx.println(stmt, `// ${line}`);
});
}
return null;
}
visitJSDocCommentStmt(stmt: o.JSDocCommentStmt, ctx: EmitterVisitorContext) {
ctx.println(stmt, `/*${stmt.toString()}*/`);
return null;
}
abstract visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any;

View File

@ -16,15 +16,11 @@ export enum TypeModifier {
}
export abstract class Type {
constructor(public modifiers: TypeModifier[]|null = null) {
if (!modifiers) {
this.modifiers = [];
}
}
constructor(public modifiers: TypeModifier[] = []) {}
abstract visitType(visitor: TypeVisitor, context: any): any;
hasModifier(modifier: TypeModifier): boolean {
return this.modifiers!.indexOf(modifier) !== -1;
return this.modifiers.indexOf(modifier) !== -1;
}
}
@ -40,7 +36,7 @@ export enum BuiltinTypeName {
}
export class BuiltinType extends Type {
constructor(public name: BuiltinTypeName, modifiers: TypeModifier[]|null = null) {
constructor(public name: BuiltinTypeName, modifiers?: TypeModifier[]) {
super(modifiers);
}
visitType(visitor: TypeVisitor, context: any): any {
@ -50,8 +46,7 @@ export class BuiltinType extends Type {
export class ExpressionType extends Type {
constructor(
public value: Expression, modifiers: TypeModifier[]|null = null,
public typeParams: Type[]|null = null) {
public value: Expression, modifiers?: TypeModifier[], public typeParams: Type[]|null = null) {
super(modifiers);
}
visitType(visitor: TypeVisitor, context: any): any {
@ -61,7 +56,7 @@ export class ExpressionType extends Type {
export class ArrayType extends Type {
constructor(public of: Type, modifiers: TypeModifier[]|null = null) {
constructor(public of: Type, modifiers?: TypeModifier[]) {
super(modifiers);
}
visitType(visitor: TypeVisitor, context: any): any {
@ -72,7 +67,7 @@ export class ArrayType extends Type {
export class MapType extends Type {
public valueType: Type|null;
constructor(valueType: Type|null|undefined, modifiers: TypeModifier[]|null = null) {
constructor(valueType: Type|null|undefined, modifiers?: TypeModifier[]) {
super(modifiers);
this.valueType = valueType || null;
}
@ -357,7 +352,7 @@ export class WriteVarExpr extends Expression {
return visitor.visitWriteVarExpr(this, context);
}
toDeclStmt(type?: Type|null, modifiers?: StmtModifier[]|null): DeclareVarStmt {
toDeclStmt(type?: Type|null, modifiers?: StmtModifier[]): DeclareVarStmt {
return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
}
@ -764,7 +759,7 @@ export class FunctionExpr extends Expression {
return visitor.visitFunctionExpr(this, context);
}
toDeclStmt(name: string, modifiers: StmtModifier[]|null = null): DeclareFunctionStmt {
toDeclStmt(name: string, modifiers?: StmtModifier[]): DeclareFunctionStmt {
return new DeclareFunctionStmt(
name, this.params, this.statements, this.type, modifiers, this.sourceSpan);
}
@ -978,13 +973,25 @@ export enum StmtModifier {
Static,
}
export abstract class Statement {
public modifiers: StmtModifier[];
public sourceSpan: ParseSourceSpan|null;
constructor(modifiers?: StmtModifier[]|null, sourceSpan?: ParseSourceSpan|null) {
this.modifiers = modifiers || [];
this.sourceSpan = sourceSpan || null;
export class LeadingComment {
constructor(public text: string, public multiline: boolean, public trailingNewline: boolean) {}
toString() {
return this.multiline ? ` ${this.text} ` : this.text;
}
}
export class JSDocComment extends LeadingComment {
constructor(public tags: JSDocTag[]) {
super('', /* multiline */ true, /* trailingNewline */ true);
}
toString(): string {
return serializeTags(this.tags);
}
}
export abstract class Statement {
constructor(
public modifiers: StmtModifier[] = [], public sourceSpan: ParseSourceSpan|null = null,
public leadingComments?: LeadingComment[]) {}
/**
* Calculates whether this statement produces the same value as the given statement.
* Note: We don't check Types nor ParseSourceSpans nor function arguments.
@ -994,7 +1001,12 @@ export abstract class Statement {
abstract visitStatement(visitor: StatementVisitor, context: any): any;
hasModifier(modifier: StmtModifier): boolean {
return this.modifiers!.indexOf(modifier) !== -1;
return this.modifiers.indexOf(modifier) !== -1;
}
addLeadingComment(leadingComment: LeadingComment): void {
this.leadingComments = this.leadingComments ?? [];
this.leadingComments.push(leadingComment);
}
}
@ -1002,9 +1014,9 @@ export abstract class Statement {
export class DeclareVarStmt extends Statement {
public type: Type|null;
constructor(
public name: string, public value?: Expression, type?: Type|null,
modifiers: StmtModifier[]|null = null, sourceSpan?: ParseSourceSpan|null) {
super(modifiers, sourceSpan);
public name: string, public value?: Expression, type?: Type|null, modifiers?: StmtModifier[],
sourceSpan?: ParseSourceSpan|null, leadingComments?: LeadingComment[]) {
super(modifiers, sourceSpan, leadingComments);
this.type = type || (value && value.type) || null;
}
isEquivalent(stmt: Statement): boolean {
@ -1020,28 +1032,29 @@ export class DeclareFunctionStmt extends Statement {
public type: Type|null;
constructor(
public name: string, public params: FnParam[], public statements: Statement[],
type?: Type|null, modifiers: StmtModifier[]|null = null, sourceSpan?: ParseSourceSpan|null) {
super(modifiers, sourceSpan);
type?: Type|null, modifiers?: StmtModifier[], sourceSpan?: ParseSourceSpan|null,
leadingComments?: LeadingComment[]) {
super(modifiers, sourceSpan, leadingComments);
this.type = type || null;
}
isEquivalent(stmt: Statement): boolean {
return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) &&
areAllEquivalent(this.statements, stmt.statements);
}
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitDeclareFunctionStmt(this, context);
}
}
export class ExpressionStatement extends Statement {
constructor(public expr: Expression, sourceSpan?: ParseSourceSpan|null) {
super(null, sourceSpan);
constructor(
public expr: Expression, sourceSpan?: ParseSourceSpan|null,
leadingComments?: LeadingComment[]) {
super([], sourceSpan, leadingComments);
}
isEquivalent(stmt: Statement): boolean {
return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr);
}
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitExpressionStmt(this, context);
}
@ -1049,8 +1062,10 @@ export class ExpressionStatement extends Statement {
export class ReturnStatement extends Statement {
constructor(public value: Expression, sourceSpan?: ParseSourceSpan|null) {
super(null, sourceSpan);
constructor(
public value: Expression, sourceSpan: ParseSourceSpan|null = null,
leadingComments?: LeadingComment[]) {
super([], sourceSpan, leadingComments);
}
isEquivalent(stmt: Statement): boolean {
return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value);
@ -1061,21 +1076,15 @@ export class ReturnStatement extends Statement {
}
export class AbstractClassPart {
public type: Type|null;
constructor(type: Type|null|undefined, public modifiers: StmtModifier[]|null) {
if (!modifiers) {
this.modifiers = [];
}
this.type = type || null;
}
constructor(public type: Type|null = null, public modifiers: StmtModifier[] = []) {}
hasModifier(modifier: StmtModifier): boolean {
return this.modifiers!.indexOf(modifier) !== -1;
return this.modifiers.indexOf(modifier) !== -1;
}
}
export class ClassField extends AbstractClassPart {
constructor(
public name: string, type?: Type|null, modifiers: StmtModifier[]|null = null,
public name: string, type?: Type|null, modifiers?: StmtModifier[],
public initializer?: Expression) {
super(type, modifiers);
}
@ -1088,7 +1097,7 @@ export class ClassField extends AbstractClassPart {
export class ClassMethod extends AbstractClassPart {
constructor(
public name: string|null, public params: FnParam[], public body: Statement[],
type?: Type|null, modifiers: StmtModifier[]|null = null) {
type?: Type|null, modifiers?: StmtModifier[]) {
super(type, modifiers);
}
isEquivalent(m: ClassMethod) {
@ -1099,8 +1108,7 @@ export class ClassMethod extends AbstractClassPart {
export class ClassGetter extends AbstractClassPart {
constructor(
public name: string, public body: Statement[], type?: Type|null,
modifiers: StmtModifier[]|null = null) {
public name: string, public body: Statement[], type?: Type|null, modifiers?: StmtModifier[]) {
super(type, modifiers);
}
isEquivalent(m: ClassGetter) {
@ -1113,9 +1121,9 @@ export class ClassStmt extends Statement {
constructor(
public name: string, public parent: Expression|null, public fields: ClassField[],
public getters: ClassGetter[], public constructorMethod: ClassMethod,
public methods: ClassMethod[], modifiers: StmtModifier[]|null = null,
sourceSpan?: ParseSourceSpan|null) {
super(modifiers, sourceSpan);
public methods: ClassMethod[], modifiers?: StmtModifier[], sourceSpan?: ParseSourceSpan|null,
leadingComments?: LeadingComment[]) {
super(modifiers, sourceSpan, leadingComments);
}
isEquivalent(stmt: Statement): boolean {
return stmt instanceof ClassStmt && this.name === stmt.name &&
@ -1134,8 +1142,9 @@ export class ClassStmt extends Statement {
export class IfStmt extends Statement {
constructor(
public condition: Expression, public trueCase: Statement[],
public falseCase: Statement[] = [], sourceSpan?: ParseSourceSpan|null) {
super(null, sourceSpan);
public falseCase: Statement[] = [], sourceSpan?: ParseSourceSpan|null,
leadingComments?: LeadingComment[]) {
super([], sourceSpan, leadingComments);
}
isEquivalent(stmt: Statement): boolean {
return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) &&
@ -1147,38 +1156,11 @@ export class IfStmt extends Statement {
}
}
export class CommentStmt extends Statement {
constructor(public comment: string, public multiline = false, sourceSpan?: ParseSourceSpan|null) {
super(null, sourceSpan);
}
isEquivalent(stmt: Statement): boolean {
return stmt instanceof CommentStmt;
}
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitCommentStmt(this, context);
}
}
export class JSDocCommentStmt extends Statement {
constructor(public tags: JSDocTag[] = [], sourceSpan?: ParseSourceSpan|null) {
super(null, sourceSpan);
}
isEquivalent(stmt: Statement): boolean {
return stmt instanceof JSDocCommentStmt && this.toString() === stmt.toString();
}
visitStatement(visitor: StatementVisitor, context: any): any {
return visitor.visitJSDocCommentStmt(this, context);
}
toString(): string {
return serializeTags(this.tags);
}
}
export class TryCatchStmt extends Statement {
constructor(
public bodyStmts: Statement[], public catchStmts: Statement[],
sourceSpan?: ParseSourceSpan|null) {
super(null, sourceSpan);
sourceSpan: ParseSourceSpan|null = null, leadingComments?: LeadingComment[]) {
super([], sourceSpan, leadingComments);
}
isEquivalent(stmt: Statement): boolean {
return stmt instanceof TryCatchStmt && areAllEquivalent(this.bodyStmts, stmt.bodyStmts) &&
@ -1191,8 +1173,10 @@ export class TryCatchStmt extends Statement {
export class ThrowStmt extends Statement {
constructor(public error: Expression, sourceSpan?: ParseSourceSpan|null) {
super(null, sourceSpan);
constructor(
public error: Expression, sourceSpan: ParseSourceSpan|null = null,
leadingComments?: LeadingComment[]) {
super([], sourceSpan, leadingComments);
}
isEquivalent(stmt: ThrowStmt): boolean {
return stmt instanceof TryCatchStmt && this.error.isEquivalent(stmt.error);
@ -1211,8 +1195,6 @@ export interface StatementVisitor {
visitIfStmt(stmt: IfStmt, context: any): any;
visitTryCatchStmt(stmt: TryCatchStmt, context: any): any;
visitThrowStmt(stmt: ThrowStmt, context: any): any;
visitCommentStmt(stmt: CommentStmt, context: any): any;
visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: any): any;
}
export class AstTransformer implements StatementVisitor, ExpressionVisitor {
@ -1374,7 +1356,7 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor {
const entries = ast.entries.map(
(entry): LiteralMapEntry => new LiteralMapEntry(
entry.key, entry.value.visitExpression(this, context), entry.quoted));
const mapType = new MapType(ast.valueType, null);
const mapType = new MapType(ast.valueType);
return this.transformExpr(new LiteralMapExpr(entries, mapType, ast.sourceSpan), context);
}
visitCommaExpr(ast: CommaExpr, context: any): any {
@ -1388,25 +1370,30 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor {
visitDeclareVarStmt(stmt: DeclareVarStmt, context: any): any {
const value = stmt.value && stmt.value.visitExpression(this, context);
return this.transformStmt(
new DeclareVarStmt(stmt.name, value, stmt.type, stmt.modifiers, stmt.sourceSpan), context);
new DeclareVarStmt(
stmt.name, value, stmt.type, stmt.modifiers, stmt.sourceSpan, stmt.leadingComments),
context);
}
visitDeclareFunctionStmt(stmt: DeclareFunctionStmt, context: any): any {
return this.transformStmt(
new DeclareFunctionStmt(
stmt.name, stmt.params, this.visitAllStatements(stmt.statements, context), stmt.type,
stmt.modifiers, stmt.sourceSpan),
stmt.modifiers, stmt.sourceSpan, stmt.leadingComments),
context);
}
visitExpressionStmt(stmt: ExpressionStatement, context: any): any {
return this.transformStmt(
new ExpressionStatement(stmt.expr.visitExpression(this, context), stmt.sourceSpan),
new ExpressionStatement(
stmt.expr.visitExpression(this, context), stmt.sourceSpan, stmt.leadingComments),
context);
}
visitReturnStmt(stmt: ReturnStatement, context: any): any {
return this.transformStmt(
new ReturnStatement(stmt.value.visitExpression(this, context), stmt.sourceSpan), context);
new ReturnStatement(
stmt.value.visitExpression(this, context), stmt.sourceSpan, stmt.leadingComments),
context);
}
visitDeclareClassStmt(stmt: ClassStmt, context: any): any {
@ -1435,7 +1422,8 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor {
new IfStmt(
stmt.condition.visitExpression(this, context),
this.visitAllStatements(stmt.trueCase, context),
this.visitAllStatements(stmt.falseCase, context), stmt.sourceSpan),
this.visitAllStatements(stmt.falseCase, context), stmt.sourceSpan,
stmt.leadingComments),
context);
}
@ -1443,21 +1431,16 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor {
return this.transformStmt(
new TryCatchStmt(
this.visitAllStatements(stmt.bodyStmts, context),
this.visitAllStatements(stmt.catchStmts, context), stmt.sourceSpan),
this.visitAllStatements(stmt.catchStmts, context), stmt.sourceSpan,
stmt.leadingComments),
context);
}
visitThrowStmt(stmt: ThrowStmt, context: any): any {
return this.transformStmt(
new ThrowStmt(stmt.error.visitExpression(this, context), stmt.sourceSpan), context);
}
visitCommentStmt(stmt: CommentStmt, context: any): any {
return this.transformStmt(stmt, context);
}
visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: any): any {
return this.transformStmt(stmt, context);
new ThrowStmt(
stmt.error.visitExpression(this, context), stmt.sourceSpan, stmt.leadingComments),
context);
}
visitAllStatements(stmts: Statement[], context: any): Statement[] {
@ -1647,12 +1630,6 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor
stmt.error.visitExpression(this, context);
return stmt;
}
visitCommentStmt(stmt: CommentStmt, context: any): any {
return stmt;
}
visitJSDocCommentStmt(stmt: JSDocCommentStmt, context: any): any {
return stmt;
}
visitAllStatements(stmts: Statement[], context: any): void {
stmts.forEach(stmt => stmt.visitStatement(this, context));
}
@ -1743,6 +1720,15 @@ class _ApplySourceSpanTransformer extends AstTransformer {
}
}
export function leadingComment(
text: string, multiline: boolean = false, trailingNewline: boolean = true): LeadingComment {
return new LeadingComment(text, multiline, trailingNewline);
}
export function jsDocComment(tags: JSDocTag[] = []): JSDocComment {
return new JSDocComment(tags);
}
export function variable(
name: string, type?: Type|null, sourceSpan?: ParseSourceSpan|null): ReadVarExpr {
return new ReadVarExpr(name, type, sourceSpan);
@ -1755,14 +1741,13 @@ export function importExpr(
}
export function importType(
id: ExternalReference, typeParams: Type[]|null = null,
typeModifiers: TypeModifier[]|null = null): ExpressionType|null {
id: ExternalReference, typeParams?: Type[]|null,
typeModifiers?: TypeModifier[]): ExpressionType|null {
return id != null ? expressionType(importExpr(id, typeParams, null), typeModifiers) : null;
}
export function expressionType(
expr: Expression, typeModifiers: TypeModifier[]|null = null,
typeParams: Type[]|null = null): ExpressionType {
expr: Expression, typeModifiers?: TypeModifier[], typeParams?: Type[]|null): ExpressionType {
return new ExpressionType(expr, typeModifiers, typeParams);
}
@ -1802,8 +1787,10 @@ export function fn(
return new FunctionExpr(params, body, type, sourceSpan, name);
}
export function ifStmt(condition: Expression, thenClause: Statement[], elseClause?: Statement[]) {
return new IfStmt(condition, thenClause, elseClause);
export function ifStmt(
condition: Expression, thenClause: Statement[], elseClause?: Statement[],
sourceSpan?: ParseSourceSpan, leadingComments?: LeadingComment[]) {
return new IfStmt(condition, thenClause, elseClause, sourceSpan, leadingComments);
}
export function literal(
@ -1865,10 +1852,15 @@ function tagToString(tag: JSDocTag): string {
function serializeTags(tags: JSDocTag[]): string {
if (tags.length === 0) return '';
if (tags.length === 1 && tags[0].tagName && !tags[0].text) {
// The JSDOC comment is a single simple tag: e.g `/** @tagname */`.
return `*${tagToString(tags[0])} `;
}
let out = '*\n';
for (const tag of tags) {
out += ' *';
// If the tagToString is multi-line, insert " * " prefixes on subsequent lines.
// If the tagToString is multi-line, insert " * " prefixes on lines.
out += tagToString(tag).replace(/\n/g, '\n * ');
out += '\n';
}

View File

@ -233,12 +233,6 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
visitThrowStmt(stmt: o.ThrowStmt, ctx: _ExecutionContext): any {
throw stmt.error.visitExpression(this, ctx);
}
visitCommentStmt(stmt: o.CommentStmt, context?: any): any {
return null;
}
visitJSDocCommentStmt(stmt: o.JSDocCommentStmt, context?: any): any {
return null;
}
visitInstantiateExpr(ast: o.InstantiateExpr, ctx: _ExecutionContext): any {
const args = this.visitAllExpressions(ast.args, ctx);
const clazz = ast.classExpr.visitExpression(this, ctx);

View File

@ -45,14 +45,14 @@ export function convertMetaToOutput(meta: any, ctx: OutputContext): o.Expression
}
export function typeWithParameters(type: o.Expression, numParams: number): o.ExpressionType {
let params: o.Type[]|null = null;
if (numParams > 0) {
params = [];
for (let i = 0; i < numParams; i++) {
params.push(o.DYNAMIC_TYPE);
}
if (numParams === 0) {
return o.expressionType(type);
}
return o.expressionType(type, null, params);
const params: o.Type[] = [];
for (let i = 0; i < numParams; i++) {
params.push(o.DYNAMIC_TYPE);
}
return o.expressionType(type, undefined, params);
}
export interface R3Reference {

View File

@ -10,7 +10,7 @@ import {mapLiteral} from '../../../output/map_util';
import * as o from '../../../output/output_ast';
import {serializeIcuNode} from './icu_serializer';
import {i18nMetaToDocStmt} from './meta';
import {i18nMetaToJSDoc} from './meta';
import {formatI18nPlaceholderName} from './util';
/** Closure uses `goog.getMsg(message)` to lookup translations */
@ -31,15 +31,13 @@ export function createGoogleGetMsgStatements(
// */
// const MSG_... = goog.getMsg(..);
// I18N_X = MSG_...;
const statements = [];
const jsdocComment = i18nMetaToDocStmt(message);
if (jsdocComment !== null) {
statements.push(jsdocComment);
const googGetMsgStmt = closureVar.set(o.variable(GOOG_GET_MSG).callFn(args)).toConstDecl();
const metaComment = i18nMetaToJSDoc(message);
if (metaComment !== null) {
googGetMsgStmt.addLeadingComment(metaComment);
}
statements.push(closureVar.set(o.variable(GOOG_GET_MSG).callFn(args)).toConstDecl());
statements.push(new o.ExpressionStatement(variable.set(closureVar)));
return statements;
const i18nAssignmentStmt = new o.ExpressionStatement(variable.set(closureVar));
return [googGetMsgStmt, i18nAssignmentStmt];
}
/**

View File

@ -231,7 +231,7 @@ export function parseI18nMeta(meta: string = ''): I18nMeta {
// Converts i18n meta information for a message (id, description, meaning)
// to a JsDoc statement formatted as expected by the Closure compiler.
export function i18nMetaToDocStmt(meta: I18nMeta): o.JSDocCommentStmt|null {
export function i18nMetaToJSDoc(meta: I18nMeta): o.JSDocComment|null {
const tags: o.JSDocTag[] = [];
if (meta.description) {
tags.push({tagName: o.JSDocTagName.Desc, text: meta.description});
@ -239,5 +239,5 @@ export function i18nMetaToDocStmt(meta: I18nMeta): o.JSDocCommentStmt|null {
if (meta.meaning) {
tags.push({tagName: o.JSDocTagName.Meaning, text: meta.meaning});
}
return tags.length == 0 ? null : new o.JSDocCommentStmt(tags);
return tags.length == 0 ? null : o.jsDocComment(tags);
}

View File

@ -179,5 +179,5 @@ export function getTranslationConstPrefix(extra: string): string {
*/
export function declareI18nVariable(variable: o.ReadVarExpr): o.Statement {
return new o.DeclareVarStmt(
variable.name!, undefined, o.INFERRED_TYPE, null, variable.sourceSpan);
variable.name!, undefined, o.INFERRED_TYPE, undefined, variable.sourceSpan);
}

View File

@ -892,7 +892,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
const templateFunctionExpr = templateVisitor.buildTemplateFunction(
template.children, template.variables,
this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, template.i18n);
this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName, null));
this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName));
if (templateVisitor._ngContentReservedSlots.length) {
this._ngContentReservedSlots.push(...templateVisitor._ngContentReservedSlots);
}