Skip to content

Commit 6b4eec4

Browse files
authored
Underline type nodes without position mappings in .types baselines (#57963)
1 parent b24b5c4 commit 6b4eec4

File tree

11,995 files changed

+560121
-1565
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

11,995 files changed

+560121
-1565
lines changed

src/harness/harnessIO.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -856,8 +856,12 @@ export namespace Compiler {
856856
}
857857
lastIndexWritten = result.line;
858858
const typeOrSymbolString = isSymbolBaseline ? result.symbol : result.type;
859-
const formattedLine = result.sourceText.replace(/\r?\n/g, "") + " : " + typeOrSymbolString;
859+
const lineText = result.sourceText.replace(/\r?\n/g, "");
860+
const formattedLine = lineText + " : " + typeOrSymbolString;
860861
typeLines += ">" + formattedLine + "\r\n";
862+
if (result.underline) {
863+
typeLines += ">" + " ".repeat(lineText.length) + " : " + result.underline + "\r\n";
864+
}
861865
}
862866

863867
lastIndexWritten ??= -1;

src/harness/typeWriter.ts

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import * as ts from "./_namespaces/ts";
2+
import {
3+
createPrinter,
4+
createTextWriter,
5+
memoize,
6+
} from "./_namespaces/ts";
27

38
export interface TypeWriterTypeResult {
49
line: number;
510
syntaxKind: number;
611
sourceText: string;
712
type: string;
13+
underline?: string;
814
}
915

1016
export interface TypeWriterSymbolResult {
@@ -20,6 +26,7 @@ export interface TypeWriterResult {
2026
sourceText: string;
2127
symbol?: string;
2228
type?: string;
29+
underline?: string;
2330
}
2431

2532
function* forEachASTNode(node: ts.Node) {
@@ -37,6 +44,134 @@ function* forEachASTNode(node: ts.Node) {
3744
}
3845
}
3946

47+
const createSyntheticNodeUnderliningPrinter = memoize((): { printer: ts.Printer; writer: ts.EmitTextWriter; underliner: ts.EmitTextWriter; reset(): void; } => {
48+
let underlining = false;
49+
const printer = createPrinter({ removeComments: true }, {
50+
onEmitNode: (hint, node, cb) => {
51+
if (ts.nodeIsSynthesized(node) !== underlining) {
52+
// either node is synthetic and underlining needs to be enabled, or node is not synthetic and
53+
// underlining needs to be disabled
54+
underlining = !underlining;
55+
const result = cb(hint, node);
56+
underlining = !underlining;
57+
return result;
58+
}
59+
// underlining does not need to change
60+
return cb(hint, node);
61+
},
62+
});
63+
const baseWriter = createTextWriter("");
64+
const underliner = createTextWriter("");
65+
66+
return {
67+
printer,
68+
writer: {
69+
write(s: string): void {
70+
baseWriter.write(s);
71+
underliner.write(underlineFor(s));
72+
},
73+
writeTrailingSemicolon(text: string): void {
74+
baseWriter.writeTrailingSemicolon(text);
75+
underliner.writeTrailingSemicolon(underlineFor(text));
76+
},
77+
writeComment(text: string): void {
78+
baseWriter.writeComment(text);
79+
underliner.writeComment(underlineFor(text));
80+
},
81+
getText(): string {
82+
return baseWriter.getText();
83+
},
84+
rawWrite(s: string): void {
85+
baseWriter.rawWrite(s);
86+
underliner.rawWrite(underlineFor(s));
87+
},
88+
writeLiteral(s: string): void {
89+
baseWriter.writeLiteral(s);
90+
underliner.writeLiteral(underlineFor(s));
91+
},
92+
getTextPos(): number {
93+
return baseWriter.getTextPos();
94+
},
95+
getLine(): number {
96+
return baseWriter.getLine();
97+
},
98+
getColumn(): number {
99+
return baseWriter.getColumn();
100+
},
101+
getIndent(): number {
102+
return baseWriter.getIndent();
103+
},
104+
isAtStartOfLine(): boolean {
105+
return baseWriter.isAtStartOfLine();
106+
},
107+
hasTrailingComment(): boolean {
108+
return baseWriter.hasTrailingComment();
109+
},
110+
hasTrailingWhitespace(): boolean {
111+
return baseWriter.hasTrailingWhitespace();
112+
},
113+
writeKeyword(text: string): void {
114+
baseWriter.writeKeyword(text);
115+
underliner.writeKeyword(underlineFor(text));
116+
},
117+
writeOperator(text: string): void {
118+
baseWriter.writeOperator(text);
119+
underliner.writeOperator(underlineFor(text));
120+
},
121+
writePunctuation(text: string): void {
122+
baseWriter.writePunctuation(text);
123+
underliner.writePunctuation(underlineFor(text));
124+
},
125+
writeSpace(text: string): void {
126+
baseWriter.writeSpace(text);
127+
underliner.writeSpace(underlineFor(text));
128+
},
129+
writeStringLiteral(text: string): void {
130+
baseWriter.writeStringLiteral(text);
131+
underliner.writeStringLiteral(underlineFor(text));
132+
},
133+
writeParameter(text: string): void {
134+
baseWriter.writeParameter(text);
135+
underliner.writeParameter(underlineFor(text));
136+
},
137+
writeProperty(text: string): void {
138+
baseWriter.writeProperty(text);
139+
underliner.writeProperty(underlineFor(text));
140+
},
141+
writeSymbol(text: string, symbol: ts.Symbol): void {
142+
baseWriter.writeSymbol(text, symbol);
143+
underliner.writeSymbol(underlineFor(text), symbol);
144+
},
145+
writeLine(force?: boolean | undefined): void {
146+
baseWriter.writeLine(force);
147+
underliner.writeLine(force);
148+
},
149+
increaseIndent(): void {
150+
baseWriter.increaseIndent();
151+
underliner.increaseIndent();
152+
},
153+
decreaseIndent(): void {
154+
baseWriter.decreaseIndent();
155+
underliner.decreaseIndent();
156+
},
157+
clear(): void {
158+
baseWriter.clear();
159+
underliner.clear();
160+
},
161+
},
162+
underliner,
163+
reset() {
164+
underlining = false;
165+
baseWriter.clear();
166+
underliner.clear();
167+
},
168+
};
169+
170+
function underlineFor(s: string) {
171+
return s.length === 0 ? s : (underlining ? "^" : " ").repeat(s.length);
172+
}
173+
});
174+
40175
export class TypeWriterWalker {
41176
currentSourceFile!: ts.SourceFile;
42177

@@ -123,6 +258,7 @@ export class TypeWriterWalker {
123258
// return `error`s via `getTypeAtLocation`
124259
// But this is generally expected, so we don't call those out, either
125260
let typeString: string;
261+
let underline: string | undefined;
126262
if (
127263
!this.hadErrorBaseline &&
128264
type.flags & ts.TypeFlags.Any &&
@@ -139,18 +275,25 @@ export class TypeWriterWalker {
139275
}
140276
else {
141277
const typeFormatFlags = ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.AllowUniqueESSymbolType | ts.TypeFormatFlags.GenerateNamesForShadowedTypeParams;
142-
typeString = this.checker.typeToString(type, node.parent, typeFormatFlags);
143-
if (ts.isIdentifier(node) && ts.isTypeAliasDeclaration(node.parent) && node.parent.name === node && typeString === ts.idText(node)) {
278+
let typeNode = this.checker.typeToTypeNode(type, node.parent, (typeFormatFlags & ts.TypeFormatFlags.NodeBuilderFlagsMask) | ts.NodeBuilderFlags.IgnoreErrors)!;
279+
if (ts.isIdentifier(node) && ts.isTypeAliasDeclaration(node.parent) && node.parent.name === node && ts.isIdentifier(typeNode) && ts.idText(typeNode) === ts.idText(node)) {
144280
// for a complex type alias `type T = ...`, showing "T : T" isn't very helpful for type tests. When the type produced is the same as
145281
// the name of the type alias, recreate the type string without reusing the alias name
146-
typeString = this.checker.typeToString(type, node.parent, typeFormatFlags | ts.TypeFormatFlags.InTypeAlias);
282+
typeNode = this.checker.typeToTypeNode(type, node.parent, ((typeFormatFlags | ts.TypeFormatFlags.InTypeAlias) & ts.TypeFormatFlags.NodeBuilderFlagsMask) | ts.NodeBuilderFlags.IgnoreErrors)!;
147283
}
284+
285+
const { printer, writer, underliner, reset } = createSyntheticNodeUnderliningPrinter();
286+
printer.writeNode(ts.EmitHint.Unspecified, typeNode, this.currentSourceFile, writer);
287+
typeString = writer.getText();
288+
underline = underliner.getText();
289+
reset();
148290
}
149291
return {
150292
line: lineAndCharacter.line,
151293
syntaxKind: node.kind,
152294
sourceText,
153295
type: typeString,
296+
underline,
154297
};
155298
}
156299
const symbol = this.checker.getSymbolAtLocation(node);

0 commit comments

Comments
 (0)