diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index aca15c666a6ef..28464b301d854 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1170,7 +1170,7 @@ namespace ts { } function getCurrentLineMap() { - return currentLineMap || (currentLineMap = getLineStarts(currentSourceFile!)); + return currentLineMap || (currentLineMap = getLineStarts(Debug.checkDefined(currentSourceFile))); } function emit(node: Node, parenthesizerRule?: (node: Node) => Node): void; @@ -1815,7 +1815,7 @@ namespace ts { const numNodes = bundle ? bundle.sourceFiles.length + numPrepends : 1; for (let i = 0; i < numNodes; i++) { const currentNode = bundle ? i < numPrepends ? bundle.prepends[i] : bundle.sourceFiles[i - numPrepends] : node; - const sourceFile = isSourceFile(currentNode) ? currentNode : isUnparsedSource(currentNode) ? undefined : currentSourceFile!; + const sourceFile = isSourceFile(currentNode) ? currentNode : isUnparsedSource(currentNode) ? undefined : currentSourceFile; const shouldSkip = printerOptions.noEmitHelpers || (!!sourceFile && hasRecordedExternalHelpers(sourceFile)); const shouldBundle = (isSourceFile(currentNode) || isUnparsedSource(currentNode)) && !isOwnFileEmit; const helpers = isUnparsedSource(currentNode) ? currentNode.helpers : getSortedEmitHelpers(currentNode); @@ -2465,7 +2465,7 @@ namespace ts { } const preferNewLine = node.multiLine ? ListFormat.PreferNewLine : ListFormat.None; - const allowTrailingComma = currentSourceFile!.languageVersion >= ScriptTarget.ES5 && !isJsonSourceFile(currentSourceFile!) ? ListFormat.AllowTrailingComma : ListFormat.None; + const allowTrailingComma = currentSourceFile && currentSourceFile.languageVersion >= ScriptTarget.ES5 && !isJsonSourceFile(currentSourceFile) ? ListFormat.AllowTrailingComma : ListFormat.None; emitList(node, node.properties, ListFormat.ObjectLiteralExpressionProperties | allowTrailingComma | preferNewLine); if (indentedFlag) { @@ -2880,7 +2880,7 @@ namespace ts { emitExpression(node.expression, parenthesizer.parenthesizeExpressionOfExpressionStatement); // Emit semicolon in non json files // or if json file that created synthesized expression(eg.define expression statement when --out and amd code generation) - if (!isJsonSourceFile(currentSourceFile!) || nodeIsSynthesized(node.expression)) { + if (!currentSourceFile || !isJsonSourceFile(currentSourceFile) || nodeIsSynthesized(node.expression)) { writeTrailingSemicolon(); } } @@ -3207,7 +3207,7 @@ namespace ts { return false; } - if (!nodeIsSynthesized(body) && !rangeIsOnSingleLine(body, currentSourceFile!)) { + if (!nodeIsSynthesized(body) && currentSourceFile && !rangeIsOnSingleLine(body, currentSourceFile)) { return false; } @@ -3238,12 +3238,7 @@ namespace ts { ? emitBlockFunctionBodyOnSingleLine : emitBlockFunctionBodyWorker; - if (emitBodyWithDetachedComments) { - emitBodyWithDetachedComments(body, body.statements, emitBlockFunctionBody); - } - else { - emitBlockFunctionBody(body); - } + emitBodyWithDetachedComments(body, body.statements, emitBlockFunctionBody); decreaseIndent(); writeToken(SyntaxKind.CloseBraceToken, body.statements.end, writePunctuation, body); @@ -3701,9 +3696,10 @@ namespace ts { statements.length === 1 && ( // treat synthesized nodes as located on the same line for emit purposes + !currentSourceFile || nodeIsSynthesized(parentNode) || nodeIsSynthesized(statements[0]) || - rangeStartPositionsAreOnSameLine(parentNode, statements[0], currentSourceFile!) + rangeStartPositionsAreOnSameLine(parentNode, statements[0], currentSourceFile) ); let format = ListFormat.CaseOrDefaultClauseStatements; @@ -3958,16 +3954,14 @@ namespace ts { function emitSourceFile(node: SourceFile) { writeLine(); const statements = node.statements; - if (emitBodyWithDetachedComments) { - // Emit detached comment if there are no prologue directives or if the first node is synthesized. - // The synthesized node will have no leading comment so some comments may be missed. - const shouldEmitDetachedComment = statements.length === 0 || - !isPrologueDirective(statements[0]) || - nodeIsSynthesized(statements[0]); - if (shouldEmitDetachedComment) { - emitBodyWithDetachedComments(node, statements, emitSourceFileWorker); - return; - } + // Emit detached comment if there are no prologue directives or if the first node is synthesized. + // The synthesized node will have no leading comment so some comments may be missed. + const shouldEmitDetachedComment = statements.length === 0 || + !isPrologueDirective(statements[0]) || + nodeIsSynthesized(statements[0]); + if (shouldEmitDetachedComment) { + emitBodyWithDetachedComments(node, statements, emitSourceFileWorker); + return; } emitSourceFileWorker(node); } @@ -4369,7 +4363,7 @@ namespace ts { if (isEmpty) { // Write a line terminator if the parent node was multi-line - if (format & ListFormat.MultiLine && !(preserveSourceNewlines && (!parentNode || rangeIsOnSingleLine(parentNode, currentSourceFile!)))) { + if (format & ListFormat.MultiLine && !(preserveSourceNewlines && (!parentNode || currentSourceFile && rangeIsOnSingleLine(parentNode, currentSourceFile)))) { writeLine(); } else if (format & ListFormat.SpaceBetweenBraces && !(format & ListFormat.NoSpaceIfEmpty)) { @@ -4673,7 +4667,7 @@ namespace ts { const firstChild = children[0]; if (firstChild === undefined) { - return !parentNode || rangeIsOnSingleLine(parentNode, currentSourceFile!) ? 0 : 1; + return !parentNode || currentSourceFile && rangeIsOnSingleLine(parentNode, currentSourceFile) ? 0 : 1; } if (firstChild.pos === nextListElementPos) { // If this child starts at the beginning of a list item in a parent list, its leading @@ -4697,7 +4691,7 @@ namespace ts { // JsxText will be written with its leading whitespace, so don't add more manually. return 0; } - if (parentNode && + if (currentSourceFile && parentNode && !positionIsSynthesized(parentNode.pos) && !nodeIsSynthesized(firstChild) && (!firstChild.parent || getOriginalNode(firstChild.parent) === getOriginalNode(parentNode)) @@ -4710,7 +4704,7 @@ namespace ts { currentSourceFile!, includeComments)); } - return rangeStartPositionsAreOnSameLine(parentNode, firstChild, currentSourceFile!) ? 0 : 1; + return rangeStartPositionsAreOnSameLine(parentNode, firstChild, currentSourceFile) ? 0 : 1; } if (synthesizedNodeStartsOnNewLine(firstChild, format)) { return 1; @@ -4728,7 +4722,7 @@ namespace ts { // JsxText will be written with its leading whitespace, so don't add more manually. return 0; } - else if (!nodeIsSynthesized(previousNode) && !nodeIsSynthesized(nextNode)) { + else if (currentSourceFile && !nodeIsSynthesized(previousNode) && !nodeIsSynthesized(nextNode)) { if (preserveSourceNewlines && siblingNodePositionsAreComparable(previousNode, nextNode)) { return getEffectiveLines( includeComments => getLinesBetweenRangeEndAndRangeStart( @@ -4743,7 +4737,7 @@ namespace ts { // expensive than checking with `preserveSourceNewlines` as above, but the goal is not to preserve the // effective source lines between two sibling nodes. else if (!preserveSourceNewlines && originalNodesHaveSameParent(previousNode, nextNode)) { - return rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile!) ? 0 : 1; + return rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile) ? 0 : 1; } // If the two nodes are not comparable, add a line terminator based on the format that can indicate // whether new lines are preferred or not. @@ -4767,9 +4761,9 @@ namespace ts { const lastChild = lastOrUndefined(children); if (lastChild === undefined) { - return !parentNode || rangeIsOnSingleLine(parentNode, currentSourceFile!) ? 0 : 1; + return !parentNode || currentSourceFile && rangeIsOnSingleLine(parentNode, currentSourceFile) ? 0 : 1; } - if (parentNode && !positionIsSynthesized(parentNode.pos) && !nodeIsSynthesized(lastChild) && (!lastChild.parent || lastChild.parent === parentNode)) { + if (currentSourceFile && parentNode && !positionIsSynthesized(parentNode.pos) && !nodeIsSynthesized(lastChild) && (!lastChild.parent || lastChild.parent === parentNode)) { if (preserveSourceNewlines) { const end = isNodeArray(children) && !positionIsSynthesized(children.end) ? children.end : lastChild.end; return getEffectiveLines( @@ -4779,7 +4773,7 @@ namespace ts { currentSourceFile!, includeComments)); } - return rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile!) ? 0 : 1; + return rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile) ? 0 : 1; } if (synthesizedNodeStartsOnNewLine(lastChild, format)) { return 1; @@ -4857,7 +4851,7 @@ namespace ts { return 1; } - if (!nodeIsSynthesized(parent) && !nodeIsSynthesized(node1) && !nodeIsSynthesized(node2)) { + if (currentSourceFile && !nodeIsSynthesized(parent) && !nodeIsSynthesized(node1) && !nodeIsSynthesized(node2)) { if (preserveSourceNewlines) { return getEffectiveLines( includeComments => getLinesBetweenRangeEndAndRangeStart( @@ -4866,7 +4860,7 @@ namespace ts { currentSourceFile!, includeComments)); } - return rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile!) ? 0 : 1; + return rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile) ? 0 : 1; } return 0; @@ -4874,7 +4868,7 @@ namespace ts { function isEmptyBlock(block: BlockLike) { return block.statements.length === 0 - && rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile!); + && (!currentSourceFile || rangeEndIsOnSameLineAsRangeStart(block, block, currentSourceFile)); } function skipSynthesizedParentheses(node: Node) { @@ -4885,21 +4879,27 @@ namespace ts { return node; } - function getTextOfNode(node: Node, includeTrivia?: boolean): string { + function getTextOfNode(node: Identifier | PrivateIdentifier | LiteralExpression, includeTrivia?: boolean): string { if (isGeneratedIdentifier(node)) { return generateName(node); } - else if ((isIdentifier(node) || isPrivateIdentifier(node)) && (nodeIsSynthesized(node) || !node.parent || !currentSourceFile || (node.parent && currentSourceFile && getSourceFileOfNode(node) !== getOriginalNode(currentSourceFile)))) { - return idText(node); + if (isStringLiteral(node) && node.textSourceNode) { + return getTextOfNode(node.textSourceNode, includeTrivia); } - else if (node.kind === SyntaxKind.StringLiteral && (node as StringLiteral).textSourceNode) { - return getTextOfNode((node as StringLiteral).textSourceNode!, includeTrivia); + const sourceFile = currentSourceFile; // const needed for control flow + const canUseSourceFile = !!sourceFile && !!node.parent && !nodeIsSynthesized(node); + if (isMemberName(node)) { + if (!canUseSourceFile || getSourceFileOfNode(node) !== getOriginalNode(sourceFile)) { + return idText(node); + } } - else if (isLiteralExpression(node) && (nodeIsSynthesized(node) || !node.parent)) { - return node.text; + else { + Debug.assertNode(node, isLiteralExpression); // not strictly necessary + if (!canUseSourceFile) { + return node.text; + } } - - return getSourceTextOfNodeFromSourceFile(currentSourceFile!, node, includeTrivia); + return getSourceTextOfNodeFromSourceFile(sourceFile, node, includeTrivia); } function getLiteralTextOfNode(node: LiteralLikeNode, neverAsciiEscape: boolean | undefined, jsxAttributeEscape: boolean): string { @@ -4921,7 +4921,7 @@ namespace ts { | (printerOptions.terminateUnterminatedLiterals ? GetLiteralTextFlags.TerminateUnterminatedLiterals : 0) | (printerOptions.target && printerOptions.target === ScriptTarget.ESNext ? GetLiteralTextFlags.AllowNumericSeparator : 0); - return getLiteralText(node, currentSourceFile!, flags); + return getLiteralText(node, currentSourceFile, flags); } /** @@ -5244,7 +5244,7 @@ namespace ts { switch (node.kind) { case SyntaxKind.Identifier: return makeUniqueName( - getTextOfNode(node), + getTextOfNode(node as Identifier), isUniqueName, !!(flags! & GeneratedIdentifierFlags.Optimistic), !!(flags! & GeneratedIdentifierFlags.ReservedInNestedScopes) @@ -5546,7 +5546,7 @@ namespace ts { } function emitLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) { - if (!shouldWriteComment(currentSourceFile!.text, commentPos)) return; + if (!currentSourceFile || !shouldWriteComment(currentSourceFile.text, commentPos)) return; if (!hasWrittenComment) { emitNewLineBeforeLeadingCommentOfPosition(getCurrentLineMap(), writer, rangePos, commentPos); hasWrittenComment = true; @@ -5554,7 +5554,7 @@ namespace ts { // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space emitPos(commentPos); - writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); + writeCommentRange(currentSourceFile.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); emitPos(commentEnd); if (hasTrailingNewLine) { @@ -5578,14 +5578,14 @@ namespace ts { } function emitTrailingComment(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) { - if (!shouldWriteComment(currentSourceFile!.text, commentPos)) return; + if (!currentSourceFile || !shouldWriteComment(currentSourceFile.text, commentPos)) return; // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/ if (!writer.isAtStartOfLine()) { writer.writeSpace(" "); } emitPos(commentPos); - writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); + writeCommentRange(currentSourceFile.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); emitPos(commentEnd); if (hasTrailingNewLine) { @@ -5603,10 +5603,11 @@ namespace ts { } function emitTrailingCommentOfPositionNoNewline(commentPos: number, commentEnd: number, kind: SyntaxKind) { + if (!currentSourceFile) return; // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space emitPos(commentPos); - writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); + writeCommentRange(currentSourceFile.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); emitPos(commentEnd); if (kind === SyntaxKind.SingleLineCommentTrivia) { @@ -5615,10 +5616,11 @@ namespace ts { } function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, _kind: SyntaxKind, hasTrailingNewLine: boolean) { + if(!currentSourceFile) return; // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space emitPos(commentPos); - writeCommentRange(currentSourceFile!.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); + writeCommentRange(currentSourceFile.text, getCurrentLineMap(), writer, commentPos, commentEnd, newLine); emitPos(commentEnd); if (hasTrailingNewLine) { @@ -5653,6 +5655,7 @@ namespace ts { } function forEachLeadingCommentWithoutDetachedComments(cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) { + if (!currentSourceFile) return; // get the leading comments from detachedPos const pos = last(detachedCommentsInfo!).detachedCommentEndPos; if (detachedCommentsInfo!.length - 1) { @@ -5662,11 +5665,11 @@ namespace ts { detachedCommentsInfo = undefined; } - forEachLeadingCommentRange(currentSourceFile!.text, pos, cb, /*state*/ pos); + forEachLeadingCommentRange(currentSourceFile.text, pos, cb, /*state*/ pos); } function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) { - const currentDetachedCommentInfo = emitDetachedComments(currentSourceFile!.text, getCurrentLineMap(), writer, emitComment, range, newLine, commentsDisabled); + const currentDetachedCommentInfo = currentSourceFile && emitDetachedComments(currentSourceFile.text, getCurrentLineMap(), writer, emitComment, range, newLine, commentsDisabled); if (currentDetachedCommentInfo) { if (detachedCommentsInfo) { detachedCommentsInfo.push(currentDetachedCommentInfo); @@ -5678,7 +5681,7 @@ namespace ts { } function emitComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) { - if (!shouldWriteComment(currentSourceFile!.text, commentPos)) return; + if (!currentSourceFile || !shouldWriteComment(currentSourceFile.text, commentPos)) return; emitPos(commentPos); writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine); emitPos(commentEnd); @@ -5690,7 +5693,7 @@ namespace ts { * @return true if the comment is a triple-slash comment else false */ function isTripleSlashComment(commentPos: number, commentEnd: number) { - return isRecognizedTripleSlashComment(currentSourceFile!.text, commentPos, commentEnd); + return !!currentSourceFile && isRecognizedTripleSlashComment(currentSourceFile.text, commentPos, commentEnd); } // Source Maps diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index d44df87c458a6..a71afb5acaee8 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -656,10 +656,10 @@ namespace ts { AllowNumericSeparator = 1 << 3 } - export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile, flags: GetLiteralTextFlags) { + export function getLiteralText(node: LiteralLikeNode, sourceFile: SourceFile | undefined, flags: GetLiteralTextFlags) { // If we don't need to downlevel and we can reach the original source text using // the node's parent reference, then simply get the text as it was originally written. - if (canUseOriginalText(node, flags)) { + if (sourceFile && canUseOriginalText(node, flags)) { return getSourceTextOfNodeFromSourceFile(sourceFile, node); }