Skip to content

Commit f7a97b7

Browse files
authored
Cache accessibe symbol chains and serialized type parameter name generation (#43973)
* Cache accessibe symbol chains, type parameter name generation * Move signature declaration helper length approximation to start of function * Add node result caching internal to `typeToTypeNodeHelper` * Suggestion from PR
1 parent 0454ae4 commit f7a97b7

11 files changed

+271
-25
lines changed

src/compiler/checker.ts

Lines changed: 98 additions & 15 deletions
Large diffs are not rendered by default.

src/compiler/transformers/declarations.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,10 @@ namespace ts {
145145
symbolAccessibilityResult.errorSymbolName,
146146
symbolAccessibilityResult.errorModuleName));
147147
}
148+
return true;
148149
}
149150
}
151+
return false;
150152
}
151153

152154
function trackExternalModuleSymbolOfImportTypeNode(symbol: Symbol) {
@@ -156,9 +158,10 @@ namespace ts {
156158
}
157159

158160
function trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) {
159-
if (symbol.flags & SymbolFlags.TypeParameter) return;
160-
handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ true));
161+
if (symbol.flags & SymbolFlags.TypeParameter) return false;
162+
const issuedDiagnostic = handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ true));
161163
recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForSymbol(symbol, meaning));
164+
return issuedDiagnostic;
162165
}
163166

164167
function reportPrivateInBaseOfClassExpression(propertyName: string) {

src/compiler/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4831,6 +4831,7 @@ namespace ts {
48314831
typeOnlyDeclaration?: TypeOnlyCompatibleAliasDeclaration | false; // First resolved alias declaration that makes the symbol only usable in type constructs
48324832
isConstructorDeclaredProperty?: boolean; // Property declared through 'this.x = ...' assignment in constructor
48334833
tupleLabelDeclaration?: NamedTupleMember | ParameterDeclaration; // Declaration associated with the tuple's label
4834+
accessibleChainCache?: ESMap<string, Symbol[] | undefined>;
48344835
}
48354836

48364837
/* @internal */
@@ -4986,6 +4987,7 @@ namespace ts {
49864987
isExhaustive?: boolean; // Is node an exhaustive switch statement
49874988
skipDirectInference?: true; // Flag set by the API `getContextualType` call on a node when `Completions` is passed to force the checker to skip making inferences to a node's type
49884989
declarationRequiresScopeChange?: boolean; // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter.
4990+
serializedTypes?: ESMap<string, TypeNode & {truncating?: boolean, addedLength: number}>; // Collection of types serialized at this location
49894991
}
49904992

49914993
export const enum TypeFlags {
@@ -8118,7 +8120,7 @@ namespace ts {
81188120
// Called when the symbol writer encounters a symbol to write. Currently only used by the
81198121
// declaration emitter to help determine if it should patch up the final declaration file
81208122
// with import statements it previously saw (but chose not to emit).
8121-
trackSymbol?(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags): void;
8123+
trackSymbol?(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags): boolean;
81228124
reportInaccessibleThisError?(): void;
81238125
reportPrivateInBaseOfClassExpression?(propertyName: string): void;
81248126
reportInaccessibleUniqueSymbolError?(): void;

src/compiler/utilities.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ namespace ts {
8383
increaseIndent: noop,
8484
decreaseIndent: noop,
8585
clear: () => str = "",
86-
trackSymbol: noop,
86+
trackSymbol: () => false,
8787
reportInaccessibleThisError: noop,
8888
reportInaccessibleUniqueSymbolError: noop,
8989
reportPrivateInBaseOfClassExpression: noop,
@@ -4029,7 +4029,7 @@ namespace ts {
40294029
reportInaccessibleThisError: noop,
40304030
reportPrivateInBaseOfClassExpression: noop,
40314031
reportInaccessibleUniqueSymbolError: noop,
4032-
trackSymbol: noop,
4032+
trackSymbol: () => false,
40334033
writeKeyword: write,
40344034
writeOperator: write,
40354035
writeParameter: write,

src/harness/compilerImpl.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,11 @@ namespace compiler {
254254
if (compilerOptions.skipDefaultLibCheck === undefined) compilerOptions.skipDefaultLibCheck = true;
255255
if (compilerOptions.noErrorTruncation === undefined) compilerOptions.noErrorTruncation = true;
256256

257-
const preProgram = ts.length(rootFiles) < 100 ? ts.createProgram(rootFiles || [], { ...compilerOptions, configFile: compilerOptions.configFile, traceResolution: false }, host) : undefined;
257+
// pre-emit/post-emit error comparison requires declaration emit twice, which can be slow. If it's unlikely to flag any error consistency issues
258+
// and if the test is running `skipLibCheck` - an indicator that we want the tets to run quickly - skip the before/after error comparison, too
259+
const skipErrorComparison = ts.length(rootFiles) >= 100 || (!!compilerOptions.skipLibCheck && !!compilerOptions.declaration);
260+
261+
const preProgram = !skipErrorComparison ? ts.createProgram(rootFiles || [], { ...compilerOptions, configFile: compilerOptions.configFile, traceResolution: false }, host) : undefined;
258262
const preErrors = preProgram && ts.getPreEmitDiagnostics(preProgram);
259263

260264
const program = ts.createProgram(rootFiles || [], compilerOptions, host);

src/services/codefixes/helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace ts.codefix {
1818

1919
export function getNoopSymbolTrackerWithResolver(context: TypeConstructionContext): SymbolTracker {
2020
return {
21-
trackSymbol: noop,
21+
trackSymbol: () => false,
2222
moduleResolverHost: getModuleSpecifierResolverHost(context.program, context.host),
2323
};
2424
}

src/services/utilities.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2093,7 +2093,7 @@ namespace ts {
20932093
increaseIndent: () => { indent++; },
20942094
decreaseIndent: () => { indent--; },
20952095
clear: resetWriter,
2096-
trackSymbol: noop,
2096+
trackSymbol: () => false,
20972097
reportInaccessibleThisError: noop,
20982098
reportInaccessibleUniqueSymbolError: noop,
20992099
reportPrivateInBaseOfClassExpression: noop,
@@ -2619,6 +2619,7 @@ namespace ts {
26192619
const res = checker.typeToTypeNode(type, enclosingScope, NodeBuilderFlags.NoTruncation, {
26202620
trackSymbol: (symbol, declaration, meaning) => {
26212621
typeIsAccessible = typeIsAccessible && checker.isSymbolAccessible(symbol, declaration, meaning, /*shouldComputeAliasToMarkVisible*/ false).accessibility === SymbolAccessibility.Accessible;
2622+
return !typeIsAccessible;
26222623
},
26232624
reportInaccessibleThisError: notAccessible,
26242625
reportPrivateInBaseOfClassExpression: notAccessible,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
tests/cases/compiler/Api.ts(6,5): error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.
2+
tests/cases/compiler/Api.ts(7,5): error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.
3+
tests/cases/compiler/Api.ts(8,5): error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.
4+
5+
6+
==== tests/cases/compiler/http-client.ts (0 errors) ====
7+
type TPromise<ResolveType, RejectType = any> = Omit<Promise<ResolveType>, "then" | "catch"> & {
8+
then<TResult1 = ResolveType, TResult2 = never>(
9+
onfulfilled?: ((value: ResolveType) => TResult1 | PromiseLike<TResult1>) | undefined | null,
10+
onrejected?: ((reason: RejectType) => TResult2 | PromiseLike<TResult2>) | undefined | null,
11+
): TPromise<TResult1 | TResult2, RejectType>;
12+
catch<TResult = never>(
13+
onrejected?: ((reason: RejectType) => TResult | PromiseLike<TResult>) | undefined | null,
14+
): TPromise<ResolveType | TResult, RejectType>;
15+
};
16+
17+
export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
18+
data: D;
19+
error: E;
20+
}
21+
22+
export class HttpClient<SecurityDataType = unknown> {
23+
public request = <T = any, E = any>(): TPromise<HttpResponse<T, E>> => {
24+
return '' as any;
25+
};
26+
}
27+
==== tests/cases/compiler/Api.ts (3 errors) ====
28+
import { HttpClient } from "./http-client";
29+
30+
export class Api<SecurityDataType = unknown> {
31+
constructor(private http: HttpClient<SecurityDataType>) { }
32+
33+
abc1 = () => this.http.request();
34+
~~~~
35+
!!! error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.
36+
abc2 = () => this.http.request();
37+
~~~~
38+
!!! error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.
39+
abc3 = () => this.http.request();
40+
~~~~
41+
!!! error TS7056: The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.
42+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//// [tests/cases/compiler/declarationEmitPrivatePromiseLikeInterface.ts] ////
2+
3+
//// [http-client.ts]
4+
type TPromise<ResolveType, RejectType = any> = Omit<Promise<ResolveType>, "then" | "catch"> & {
5+
then<TResult1 = ResolveType, TResult2 = never>(
6+
onfulfilled?: ((value: ResolveType) => TResult1 | PromiseLike<TResult1>) | undefined | null,
7+
onrejected?: ((reason: RejectType) => TResult2 | PromiseLike<TResult2>) | undefined | null,
8+
): TPromise<TResult1 | TResult2, RejectType>;
9+
catch<TResult = never>(
10+
onrejected?: ((reason: RejectType) => TResult | PromiseLike<TResult>) | undefined | null,
11+
): TPromise<ResolveType | TResult, RejectType>;
12+
};
13+
14+
export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
15+
data: D;
16+
error: E;
17+
}
18+
19+
export class HttpClient<SecurityDataType = unknown> {
20+
public request = <T = any, E = any>(): TPromise<HttpResponse<T, E>> => {
21+
return '' as any;
22+
};
23+
}
24+
//// [Api.ts]
25+
import { HttpClient } from "./http-client";
26+
27+
export class Api<SecurityDataType = unknown> {
28+
constructor(private http: HttpClient<SecurityDataType>) { }
29+
30+
abc1 = () => this.http.request();
31+
abc2 = () => this.http.request();
32+
abc3 = () => this.http.request();
33+
}
34+
35+
//// [http-client.js]
36+
"use strict";
37+
exports.__esModule = true;
38+
exports.HttpClient = void 0;
39+
var HttpClient = /** @class */ (function () {
40+
function HttpClient() {
41+
this.request = function () {
42+
return '';
43+
};
44+
}
45+
return HttpClient;
46+
}());
47+
exports.HttpClient = HttpClient;
48+
//// [Api.js]
49+
"use strict";
50+
exports.__esModule = true;
51+
exports.Api = void 0;
52+
var Api = /** @class */ (function () {
53+
function Api(http) {
54+
var _this = this;
55+
this.http = http;
56+
this.abc1 = function () { return _this.http.request(); };
57+
this.abc2 = function () { return _this.http.request(); };
58+
this.abc3 = function () { return _this.http.request(); };
59+
}
60+
return Api;
61+
}());
62+
exports.Api = Api;
63+
64+
65+
//// [http-client.d.ts]
66+
declare type TPromise<ResolveType, RejectType = any> = Omit<Promise<ResolveType>, "then" | "catch"> & {
67+
then<TResult1 = ResolveType, TResult2 = never>(onfulfilled?: ((value: ResolveType) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: RejectType) => TResult2 | PromiseLike<TResult2>) | undefined | null): TPromise<TResult1 | TResult2, RejectType>;
68+
catch<TResult = never>(onrejected?: ((reason: RejectType) => TResult | PromiseLike<TResult>) | undefined | null): TPromise<ResolveType | TResult, RejectType>;
69+
};
70+
export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
71+
data: D;
72+
error: E;
73+
}
74+
export declare class HttpClient<SecurityDataType = unknown> {
75+
request: <T = any, E = any>() => TPromise<HttpResponse<T, E>, any>;
76+
}
77+
export {};

0 commit comments

Comments
 (0)