Fix emit for undefined SourceFile (#48774)

Co-authored-by: Ron Buckton <ron.buckton@microsoft.com>
This commit is contained in:
Jake Bailey 2022-04-22 11:18:53 -07:00 committed by GitHub
parent 94cb657b1c
commit 07660c8307
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 57 deletions

View File

@ -1172,7 +1172,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;
@ -1817,7 +1817,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);
@ -2467,7 +2467,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) {
@ -2882,7 +2882,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();
}
}
@ -3209,7 +3209,7 @@ namespace ts {
return false;
}
if (!nodeIsSynthesized(body) && !rangeIsOnSingleLine(body, currentSourceFile!)) {
if (!nodeIsSynthesized(body) && currentSourceFile && !rangeIsOnSingleLine(body, currentSourceFile)) {
return false;
}
@ -3240,12 +3240,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);
@ -3703,9 +3698,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;
@ -3960,16 +3956,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);
}
@ -4371,7 +4365,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)) {
@ -4675,7 +4669,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
@ -4699,7 +4693,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))
@ -4712,7 +4706,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;
@ -4730,7 +4724,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(
@ -4745,7 +4739,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.
@ -4769,9 +4763,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(
@ -4781,7 +4775,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;
@ -4859,7 +4853,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(
@ -4868,7 +4862,7 @@ namespace ts {
currentSourceFile!,
includeComments));
}
return rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile!) ? 0 : 1;
return rangeEndIsOnSameLineAsRangeStart(node1, node2, currentSourceFile) ? 0 : 1;
}
return 0;
@ -4876,7 +4870,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) {
@ -4887,21 +4881,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 {
@ -4923,7 +4923,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);
}
/**
@ -5246,7 +5246,7 @@ namespace ts {
switch (node.kind) {
case SyntaxKind.Identifier:
return makeUniqueName(
getTextOfNode(node),
getTextOfNode(node as Identifier),
isUniqueName,
!!(flags! & GeneratedIdentifierFlags.Optimistic),
!!(flags! & GeneratedIdentifierFlags.ReservedInNestedScopes)
@ -5548,7 +5548,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;
@ -5556,7 +5556,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) {
@ -5580,14 +5580,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) {
@ -5605,10 +5605,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) {
@ -5617,10 +5618,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) {
@ -5655,6 +5657,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) {
@ -5664,11 +5667,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);
@ -5680,7 +5683,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);
@ -5692,7 +5695,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

View File

@ -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);
}