diff --git a/.gitignore b/.gitignore index 54604cdbd35a3..d2cfe64b56bfc 100644 --- a/.gitignore +++ b/.gitignore @@ -63,4 +63,6 @@ TEST-results.xml package-lock.json .eslintcache *v8.log +*.heapsnapshot +*.heapprofile /lib/ diff --git a/.vscode/launch.template.json b/.vscode/launch.template.json index de410368e9a37..9baf0233a7328 100644 --- a/.vscode/launch.template.json +++ b/.vscode/launch.template.json @@ -49,16 +49,14 @@ "sourceMaps": true, "smartStep": true, "preLaunchTask": "npm: build:tests", - "console": "integratedTerminal", - "customDescriptionGenerator": "'__tsDebuggerDisplay' in this ? this.__tsDebuggerDisplay(defaultValue) : defaultValue" + "console": "integratedTerminal" }, { // See: https://github.com/microsoft/TypeScript/wiki/Debugging-Language-Service-in-VS-Code "type": "node", "request": "attach", "name": "Attach to VS Code TS Server via Port", - "processId": "${command:PickProcess}", - "customDescriptionGenerator": "'__tsDebuggerDisplay' in this ? this.__tsDebuggerDisplay(defaultValue) : defaultValue" + "processId": "${command:PickProcess}" } ] } diff --git a/scripts/build/options.mjs b/scripts/build/options.mjs index c4cb9c47233cd..e9da458f0c4cc 100644 --- a/scripts/build/options.mjs +++ b/scripts/build/options.mjs @@ -4,7 +4,7 @@ import os from "os"; const ci = ["1", "true"].includes(process.env.CI ?? ""); const parsed = minimist(process.argv.slice(2), { - boolean: ["dirty", "light", "colors", "lkg", "soft", "fix", "failed", "keepFailed", "force", "built", "ci", "bundle", "typecheck", "lint", "coverage"], + boolean: ["dirty", "light", "colors", "lkg", "soft", "fix", "failed", "keepFailed", "force", "built", "ci", "bundle", "typecheck", "lint", "coverage", "bail"], string: ["browser", "tests", "break", "host", "reporter", "stackTraceLimit", "timeout", "shards", "shardId"], alias: { b: "browser", @@ -41,6 +41,7 @@ const parsed = minimist(process.argv.slice(2), { typecheck: true, lint: true, coverage: false, + bail: false, }, }); @@ -86,5 +87,6 @@ export default options; * @property {boolean} typecheck * @property {boolean} lint * @property {boolean} coverage + * @property {boolean} bail */ void 0; diff --git a/scripts/build/tests.mjs b/scripts/build/tests.mjs index d247694dcb314..47dd96871ca47 100644 --- a/scripts/build/tests.mjs +++ b/scripts/build/tests.mjs @@ -41,6 +41,7 @@ export async function runConsoleTests(runJs, defaultReporter, runInParallel, opt const shards = +cmdLineOptions.shards || undefined; const shardId = +cmdLineOptions.shardId || undefined; const coverage = cmdLineOptions.coverage; + const bail = cmdLineOptions.bail; if (!cmdLineOptions.dirty) { if (options.watching) { console.log(chalk.yellowBright(`[watch] cleaning test directories...`)); @@ -90,6 +91,9 @@ export async function runConsoleTests(runJs, defaultReporter, runInParallel, opt args.push(mochaJs); args.push("-R", findUpFile("scripts/failed-tests.cjs")); args.push("-O", '"reporter=' + reporter + (keepFailed ? ",keepFailed=true" : "") + '"'); + if (bail) { + args.push("--bail"); + } if (tests) { args.push("-g", `"${tests}"`); } diff --git a/src/compiler/_namespaces/ts.ts b/src/compiler/_namespaces/ts.ts index 85c71d4d7f1f6..78699f8ac767e 100644 --- a/src/compiler/_namespaces/ts.ts +++ b/src/compiler/_namespaces/ts.ts @@ -14,7 +14,6 @@ export * from "../diagnosticInformationMap.generated"; export * from "../scanner"; export * from "../utilitiesPublic"; export * from "../utilities"; -export * from "../factory/baseNodeFactory"; export * from "../factory/parenthesizerRules"; export * from "../factory/nodeConverters"; export * from "../factory/nodeFactory"; diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d1c9c196f252e..a0c018d561541 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -261,7 +261,6 @@ import { nodeIsPresent, NonNullChain, NonNullExpression, - objectAllocator, ObjectLiteralExpression, OptionalChain, ParameterDeclaration, @@ -322,6 +321,7 @@ import { WithStatement, } from "./_namespaces/ts"; import * as performance from "./_namespaces/ts.performance"; +import { SymbolObject } from "./objectConstructors"; /** @internal */ export const enum ModuleInstanceState { @@ -559,7 +559,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { var symbolCount = 0; - var Symbol: new (flags: SymbolFlags, name: __String) => Symbol; var classifiableNames: Set<__String>; var unreachableFlow = createFlowNode(FlowFlags.Unreachable, /*node*/ undefined, /*antecedent*/ undefined); @@ -586,8 +585,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { classifiableNames = new Set(); symbolCount = 0; - Symbol = objectAllocator.getSymbolConstructor(); - // Attach debugging information if necessary Debug.attachFlowNodeDebugInfo(unreachableFlow); Debug.attachFlowNodeDebugInfo(reportedUnreachableFlow); @@ -639,7 +636,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { function createSymbol(flags: SymbolFlags, name: __String): Symbol { symbolCount++; - return new Symbol(flags, name); + return new SymbolObject(flags, name); } function addDeclarationToSymbol(symbol: Symbol, node: Declaration, symbolFlags: SymbolFlags) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cc5d7b0cdd664..5e817ad3a3144 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -887,7 +887,6 @@ import { noTruncationMaximumTruncationLength, NumberLiteralType, NumericLiteral, - objectAllocator, ObjectBindingPattern, ObjectFlags, ObjectFlagsType, @@ -1100,6 +1099,11 @@ import { } from "./_namespaces/ts"; import * as moduleSpecifiers from "./_namespaces/ts.moduleSpecifiers"; import * as performance from "./_namespaces/ts.performance"; +import { + SignatureObject, + SymbolObject, + TypeObject, +} from "./objectConstructors"; const ambientModuleSymbolRegex = /^".+"$/; const anon = "(anonymous)" as __String & string; @@ -1445,10 +1449,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { var requestedExternalEmitHelpers: ExternalEmitHelpers; var externalHelpersModule: Symbol; - var Symbol = objectAllocator.getSymbolConstructor(); - var Type = objectAllocator.getTypeConstructor(); - var Signature = objectAllocator.getSignatureConstructor(); - var typeCount = 0; var symbolCount = 0; var totalInstantiationCount = 0; @@ -2510,7 +2510,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function createSymbol(flags: SymbolFlags, name: __String, checkFlags?: CheckFlags) { symbolCount++; - const symbol = new Symbol(flags | SymbolFlags.Transient, name) as TransientSymbol; + const symbol = new SymbolObject(flags | SymbolFlags.Transient, name) as Symbol as TransientSymbol; symbol.links = new SymbolLinks() as TransientSymbolLinks; symbol.links.checkFlags = checkFlags || CheckFlags.None; return symbol; @@ -5291,7 +5291,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function createType(flags: TypeFlags): Type { - const result = new Type(checker, flags); + const result = new TypeObject(checker, flags); typeCount++; result.id = typeCount; tracing?.recordType(result); @@ -5305,7 +5305,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function createOriginType(flags: TypeFlags): Type { - return new Type(checker, flags); + return new TypeObject(checker, flags); } function createIntrinsicType(kind: TypeFlags, intrinsicName: string, objectFlags = ObjectFlags.None, debugIntrinsicName?: string): IntrinsicType { @@ -13016,7 +13016,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { minArgumentCount: number, flags: SignatureFlags, ): Signature { - const sig = new Signature(checker, flags); + const sig = new SignatureObject(checker, flags); sig.declaration = declaration; sig.typeParameters = typeParameters; sig.parameters = parameters; diff --git a/src/compiler/debug.ts b/src/compiler/debug.ts index 5f516bbfbc321..205d137d6dd8a 100644 --- a/src/compiler/debug.ts +++ b/src/compiler/debug.ts @@ -67,9 +67,9 @@ import { NodeFlags, nodeIsSynthesized, noop, - objectAllocator, ObjectFlags, ObjectType, + positionIsSynthesized, RelationComparisonResult, ScriptKind, Signature, @@ -78,10 +78,11 @@ import { SnippetKind, SortedReadonlyArray, stableSort, - Symbol, + type Symbol, SymbolFlags, symbolName, SyntaxKind, + TextRange, TransformFlags, Type, TypeFacts, @@ -92,6 +93,15 @@ import { VarianceFlags, zipWith, } from "./_namespaces/ts"; +import { + NodeObject, + TokenOrIdentifierObject, +} from "./nodeConstructors"; +import { + SignatureObject, + SymbolObject, + TypeObject, +} from "./objectConstructors"; /** @internal */ export enum LogLevel { @@ -355,6 +365,15 @@ export namespace Debug { } } + export function assertValidTextRange(range: TextRange, message?: string, stackCrawlMark?: AnyFunction) { + assert( + !positionIsSynthesized(range.pos) && !positionIsSynthesized(range.end), + message ?? "Node must have a real position for this operation", + /*verboseDebugInfo*/ undefined, + stackCrawlMark, + ); + } + /** * Asserts a value has the specified type in typespace only (does not perform a runtime assertion). * This is useful in cases where we switch on `node.kind` and can be reasonably sure the type is accurate, and @@ -503,15 +522,16 @@ export namespace Debug { return formatEnum(facts, (ts as any).TypeFacts, /*isFlags*/ true); } + const debugDescriptionSymbol = Symbol.for("debug.description"); let isDebugInfoEnabled = false; let flowNodeProto: FlowNode | undefined; function attachFlowNodeDebugInfoWorker(flowNode: FlowNode) { - if (!("__debugFlowFlags" in flowNode)) { // eslint-disable-line local/no-in-operator + if (!(debugDescriptionSymbol in flowNode)) { // eslint-disable-line local/no-in-operator Object.defineProperties(flowNode, { // for use with vscode-js-debug's new customDescriptionGenerator in launch.json - __tsDebuggerDisplay: { + [debugDescriptionSymbol]: { value(this: FlowNode) { const flowHeader = this.flags & FlowFlags.Start ? "FlowStart" : this.flags & FlowFlags.BranchLabel ? "FlowBranchLabel" : @@ -565,9 +585,9 @@ export namespace Debug { let nodeArrayProto: NodeArray | undefined; function attachNodeArrayDebugInfoWorker(array: NodeArray) { - if (!("__tsDebuggerDisplay" in array)) { // eslint-disable-line local/no-in-operator + if (!(debugDescriptionSymbol in array)) { // eslint-disable-line local/no-in-operator Object.defineProperties(array, { - __tsDebuggerDisplay: { + [debugDescriptionSymbol]: { value(this: NodeArray, defaultValue: string) { // An `Array` with extra properties is rendered as `[A, B, prop1: 1, prop2: 2]`. Most of // these aren't immediately useful so we trim off the `prop1: ..., prop2: ...` part from the @@ -612,9 +632,8 @@ export namespace Debug { const weakNodeTextMap = new WeakMap(); // Add additional properties in debug mode to assist with debugging. - Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, { - // for use with vscode-js-debug's new customDescriptionGenerator in launch.json - __tsDebuggerDisplay: { + Object.defineProperties(SymbolObject.prototype, { + [debugDescriptionSymbol]: { value(this: Symbol) { const symbolHeader = this.flags & SymbolFlags.Transient ? "TransientSymbol" : "Symbol"; @@ -629,9 +648,8 @@ export namespace Debug { }, }); - Object.defineProperties(objectAllocator.getTypeConstructor().prototype, { - // for use with vscode-js-debug's new customDescriptionGenerator in launch.json - __tsDebuggerDisplay: { + Object.defineProperties(TypeObject.prototype, { + [debugDescriptionSymbol]: { value(this: Type) { const typeHeader = this.flags & TypeFlags.Intrinsic ? `IntrinsicType ${(this as IntrinsicType).intrinsicName}${(this as IntrinsicType).debugIntrinsicName ? ` (${(this as IntrinsicType).debugIntrinsicName})` : ""}` : this.flags & TypeFlags.Nullable ? "NullableType" : @@ -683,7 +701,7 @@ export namespace Debug { }, }); - Object.defineProperties(objectAllocator.getSignatureConstructor().prototype, { + Object.defineProperties(SignatureObject.prototype, { __debugFlags: { get(this: Signature) { return formatSignatureFlags(this.flags); @@ -696,105 +714,95 @@ export namespace Debug { }, }); - const nodeConstructors = [ - objectAllocator.getNodeConstructor(), - objectAllocator.getIdentifierConstructor(), - objectAllocator.getTokenConstructor(), - objectAllocator.getSourceFileConstructor(), - ]; - - for (const ctor of nodeConstructors) { - if (!hasProperty(ctor.prototype, "__debugKind")) { - Object.defineProperties(ctor.prototype, { - // for use with vscode-js-debug's new customDescriptionGenerator in launch.json - __tsDebuggerDisplay: { - value(this: Node) { - const nodeHeader = isGeneratedIdentifier(this) ? "GeneratedIdentifier" : - isIdentifier(this) ? `Identifier '${idText(this)}'` : - isPrivateIdentifier(this) ? `PrivateIdentifier '${idText(this)}'` : - isStringLiteral(this) ? `StringLiteral ${JSON.stringify(this.text.length < 10 ? this.text : this.text.slice(10) + "...")}` : - isNumericLiteral(this) ? `NumericLiteral ${this.text}` : - isBigIntLiteral(this) ? `BigIntLiteral ${this.text}n` : - isTypeParameterDeclaration(this) ? "TypeParameterDeclaration" : - isParameter(this) ? "ParameterDeclaration" : - isConstructorDeclaration(this) ? "ConstructorDeclaration" : - isGetAccessorDeclaration(this) ? "GetAccessorDeclaration" : - isSetAccessorDeclaration(this) ? "SetAccessorDeclaration" : - isCallSignatureDeclaration(this) ? "CallSignatureDeclaration" : - isConstructSignatureDeclaration(this) ? "ConstructSignatureDeclaration" : - isIndexSignatureDeclaration(this) ? "IndexSignatureDeclaration" : - isTypePredicateNode(this) ? "TypePredicateNode" : - isTypeReferenceNode(this) ? "TypeReferenceNode" : - isFunctionTypeNode(this) ? "FunctionTypeNode" : - isConstructorTypeNode(this) ? "ConstructorTypeNode" : - isTypeQueryNode(this) ? "TypeQueryNode" : - isTypeLiteralNode(this) ? "TypeLiteralNode" : - isArrayTypeNode(this) ? "ArrayTypeNode" : - isTupleTypeNode(this) ? "TupleTypeNode" : - isOptionalTypeNode(this) ? "OptionalTypeNode" : - isRestTypeNode(this) ? "RestTypeNode" : - isUnionTypeNode(this) ? "UnionTypeNode" : - isIntersectionTypeNode(this) ? "IntersectionTypeNode" : - isConditionalTypeNode(this) ? "ConditionalTypeNode" : - isInferTypeNode(this) ? "InferTypeNode" : - isParenthesizedTypeNode(this) ? "ParenthesizedTypeNode" : - isThisTypeNode(this) ? "ThisTypeNode" : - isTypeOperatorNode(this) ? "TypeOperatorNode" : - isIndexedAccessTypeNode(this) ? "IndexedAccessTypeNode" : - isMappedTypeNode(this) ? "MappedTypeNode" : - isLiteralTypeNode(this) ? "LiteralTypeNode" : - isNamedTupleMember(this) ? "NamedTupleMember" : - isImportTypeNode(this) ? "ImportTypeNode" : - formatSyntaxKind(this.kind); - return `${nodeHeader}${this.flags ? ` (${formatNodeFlags(this.flags)})` : ""}`; - }, + for (const prototype of [NodeObject.prototype, TokenOrIdentifierObject.prototype]) { + Object.defineProperties(prototype, { + [debugDescriptionSymbol]: { + value(this: Node) { + const nodeHeader = isGeneratedIdentifier(this) ? "GeneratedIdentifier" : + isIdentifier(this) ? `Identifier '${idText(this)}'` : + isPrivateIdentifier(this) ? `PrivateIdentifier '${idText(this)}'` : + isStringLiteral(this) ? `StringLiteral ${JSON.stringify(this.text.length < 10 ? this.text : this.text.slice(10) + "...")}` : + isNumericLiteral(this) ? `NumericLiteral ${this.text}` : + isBigIntLiteral(this) ? `BigIntLiteral ${this.text}n` : + isTypeParameterDeclaration(this) ? "TypeParameterDeclaration" : + isParameter(this) ? "ParameterDeclaration" : + isConstructorDeclaration(this) ? "ConstructorDeclaration" : + isGetAccessorDeclaration(this) ? "GetAccessorDeclaration" : + isSetAccessorDeclaration(this) ? "SetAccessorDeclaration" : + isCallSignatureDeclaration(this) ? "CallSignatureDeclaration" : + isConstructSignatureDeclaration(this) ? "ConstructSignatureDeclaration" : + isIndexSignatureDeclaration(this) ? "IndexSignatureDeclaration" : + isTypePredicateNode(this) ? "TypePredicateNode" : + isTypeReferenceNode(this) ? "TypeReferenceNode" : + isFunctionTypeNode(this) ? "FunctionTypeNode" : + isConstructorTypeNode(this) ? "ConstructorTypeNode" : + isTypeQueryNode(this) ? "TypeQueryNode" : + isTypeLiteralNode(this) ? "TypeLiteralNode" : + isArrayTypeNode(this) ? "ArrayTypeNode" : + isTupleTypeNode(this) ? "TupleTypeNode" : + isOptionalTypeNode(this) ? "OptionalTypeNode" : + isRestTypeNode(this) ? "RestTypeNode" : + isUnionTypeNode(this) ? "UnionTypeNode" : + isIntersectionTypeNode(this) ? "IntersectionTypeNode" : + isConditionalTypeNode(this) ? "ConditionalTypeNode" : + isInferTypeNode(this) ? "InferTypeNode" : + isParenthesizedTypeNode(this) ? "ParenthesizedTypeNode" : + isThisTypeNode(this) ? "ThisTypeNode" : + isTypeOperatorNode(this) ? "TypeOperatorNode" : + isIndexedAccessTypeNode(this) ? "IndexedAccessTypeNode" : + isMappedTypeNode(this) ? "MappedTypeNode" : + isLiteralTypeNode(this) ? "LiteralTypeNode" : + isNamedTupleMember(this) ? "NamedTupleMember" : + isImportTypeNode(this) ? "ImportTypeNode" : + formatSyntaxKind(this.kind); + return `${nodeHeader}${this.flags ? ` (${formatNodeFlags(this.flags)})` : ""}`; }, - __debugKind: { - get(this: Node) { - return formatSyntaxKind(this.kind); - }, + }, + __debugKind: { + get(this: Node) { + return formatSyntaxKind(this.kind); }, - __debugNodeFlags: { - get(this: Node) { - return formatNodeFlags(this.flags); - }, + }, + __debugNodeFlags: { + get(this: Node) { + return formatNodeFlags(this.flags); }, - __debugModifierFlags: { - get(this: Node) { - return formatModifierFlags(getEffectiveModifierFlagsNoCache(this)); - }, + }, + __debugModifierFlags: { + get(this: Node) { + return formatModifierFlags(getEffectiveModifierFlagsNoCache(this)); }, - __debugTransformFlags: { - get(this: Node) { - return formatTransformFlags(this.transformFlags); - }, + }, + __debugTransformFlags: { + get(this: Node) { + return formatTransformFlags(this.transformFlags); }, - __debugIsParseTreeNode: { - get(this: Node) { - return isParseTreeNode(this); - }, + }, + __debugIsParseTreeNode: { + get(this: Node) { + return isParseTreeNode(this); }, - __debugEmitFlags: { - get(this: Node) { - return formatEmitFlags(getEmitFlags(this)); - }, + }, + __debugEmitFlags: { + get(this: Node) { + return formatEmitFlags(getEmitFlags(this)); }, - __debugGetText: { - value(this: Node, includeTrivia?: boolean) { - if (nodeIsSynthesized(this)) return ""; - // avoid recomputing - let text = weakNodeTextMap.get(this); - if (text === undefined) { - const parseNode = getParseTreeNode(this); - const sourceFile = parseNode && getSourceFileOfNode(parseNode); - text = sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : ""; - weakNodeTextMap.set(this, text); - } - return text; - }, + }, + __debugGetText: { + value(this: Node, includeTrivia?: boolean) { + if (nodeIsSynthesized(this)) return ""; + // avoid recomputing + let text = weakNodeTextMap.get(this); + if (text === undefined) { + const parseNode = getParseTreeNode(this); + const sourceFile = parseNode && getSourceFileOfNode(parseNode); + text = sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : ""; + weakNodeTextMap.set(this, text); + } + return text; }, - }); - } + }, + }); } isDebugInfoEnabled = true; diff --git a/src/compiler/factory/baseNodeFactory.ts b/src/compiler/factory/baseNodeFactory.ts deleted file mode 100644 index 9ea81cdf5632b..0000000000000 --- a/src/compiler/factory/baseNodeFactory.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { - Node, - objectAllocator, - SyntaxKind, -} from "../_namespaces/ts"; - -/** - * A `BaseNodeFactory` is an abstraction over an `ObjectAllocator` that handles caching `Node` constructors - * and allocating `Node` instances based on a set of predefined types. - * - * @internal - */ -export interface BaseNodeFactory { - createBaseSourceFileNode(kind: SyntaxKind.SourceFile): Node; - createBaseIdentifierNode(kind: SyntaxKind.Identifier): Node; - createBasePrivateIdentifierNode(kind: SyntaxKind.PrivateIdentifier): Node; - createBaseTokenNode(kind: SyntaxKind): Node; - createBaseNode(kind: SyntaxKind): Node; -} - -/** - * Creates a `BaseNodeFactory` which can be used to create `Node` instances from the constructors provided by the object allocator. - * - * @internal - */ -export function createBaseNodeFactory(): BaseNodeFactory { - let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; - let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; - let IdentifierConstructor: new (kind: SyntaxKind.Identifier, pos: number, end: number) => Node; - let PrivateIdentifierConstructor: new (kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) => Node; - let SourceFileConstructor: new (kind: SyntaxKind.SourceFile, pos: number, end: number) => Node; - - return { - createBaseSourceFileNode, - createBaseIdentifierNode, - createBasePrivateIdentifierNode, - createBaseTokenNode, - createBaseNode, - }; - - function createBaseSourceFileNode(kind: SyntaxKind.SourceFile): Node { - return new (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))(kind, /*pos*/ -1, /*end*/ -1); - } - - function createBaseIdentifierNode(kind: SyntaxKind.Identifier): Node { - return new (IdentifierConstructor || (IdentifierConstructor = objectAllocator.getIdentifierConstructor()))(kind, /*pos*/ -1, /*end*/ -1); - } - - function createBasePrivateIdentifierNode(kind: SyntaxKind.PrivateIdentifier): Node { - return new (PrivateIdentifierConstructor || (PrivateIdentifierConstructor = objectAllocator.getPrivateIdentifierConstructor()))(kind, /*pos*/ -1, /*end*/ -1); - } - - function createBaseTokenNode(kind: SyntaxKind): Node { - return new (TokenConstructor || (TokenConstructor = objectAllocator.getTokenConstructor()))(kind, /*pos*/ -1, /*end*/ -1); - } - - function createBaseNode(kind: SyntaxKind): Node { - return new (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()))(kind, /*pos*/ -1, /*end*/ -1); - } -} diff --git a/src/compiler/factory/nodeChildren.ts b/src/compiler/factory/nodeChildren.ts index 89fa8383b281d..eb1652a400902 100644 --- a/src/compiler/factory/nodeChildren.ts +++ b/src/compiler/factory/nodeChildren.ts @@ -1,14 +1,14 @@ import { Node } from "../_namespaces/ts"; -const nodeChildren = new WeakMap(); +const nodeChildren = new WeakMap(); /** @internal */ -export function getNodeChildren(node: Node): Node[] | undefined { +export function getNodeChildren(node: Node): readonly Node[] | undefined { return nodeChildren.get(node); } /** @internal */ -export function setNodeChildren(node: Node, children: Node[]): Node[] { +export function setNodeChildren(node: Node, children: readonly Node[]): readonly Node[] { nodeChildren.set(node, children); return children; } diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index c2b9f0d615005..79a74264020f2 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -18,7 +18,6 @@ import { AsteriskToken, AwaitExpression, AwaitKeyword, - BaseNodeFactory, BigIntLiteral, BinaryExpression, BinaryOperator, @@ -55,7 +54,6 @@ import { ConstructSignatureDeclaration, containsObjectRestOrSpread, ContinueStatement, - createBaseNodeFactory, createNodeConverters, createParenthesizerRules, createScanner, @@ -335,7 +333,6 @@ import { nullNodeConverters, nullParenthesizerRules, NumericLiteral, - objectAllocator, ObjectBindingPattern, ObjectLiteralElementLike, ObjectLiteralExpression, @@ -457,6 +454,14 @@ import { WithStatement, YieldExpression, } from "../_namespaces/ts"; +import { + IdentifierObject, + NodeObject, + PrivateIdentifierObject, + SourceFileObject, + TokenObject, +} from "../nodeConstructors"; +import { SourceMapSourceObject } from "../objectConstructors"; let nextAutoGenerateId = 0; @@ -471,6 +476,8 @@ export const enum NodeFactoryFlags { NoIndentationOnFreshPropertyAccess = 1 << 2, // Do not set an `original` pointer when updating a node. NoOriginalNode = 1 << 3, + // Mark nodes as synthetic + MarkSynthetic = 1 << 4, } const nodeFactoryPatchers: ((factory: NodeFactory) => void)[] = []; @@ -487,7 +494,8 @@ export function addNodeFactoryPatcher(fn: (factory: NodeFactory) => void) { * * @internal */ -export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNodeFactory): NodeFactory { +export function createNodeFactory(flags: NodeFactoryFlags): NodeFactory { + const markSynthetic = (flags & NodeFactoryFlags.MarkSynthetic) === NodeFactoryFlags.MarkSynthetic; const setOriginal = flags & NodeFactoryFlags.NoOriginalNode ? identity : setOriginalNode; // Lazily load the parenthesizer, node converters, and some factory methods until they are used. @@ -515,7 +523,6 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode get converters() { return converters(); }, - baseFactory, flags, createNodeArray, createNumericLiteral, @@ -1204,7 +1211,9 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } function createBaseNode(kind: T["kind"]) { - return baseFactory.createBaseNode(kind) as Mutable; + const node = new NodeObject(kind) as Node as Mutable; + if (markSynthetic) node.flags |= NodeFlags.Synthesized; + return node; } function createBaseDeclaration(kind: T["kind"]) { @@ -1299,7 +1308,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode // function createBaseIdentifier(escapedText: __String) { - const node = baseFactory.createBaseIdentifierNode(SyntaxKind.Identifier) as Mutable; + const node = new IdentifierObject() as Mutable; + if (markSynthetic) node.flags |= NodeFlags.Synthesized; node.escapedText = escapedText; node.jsDoc = undefined; // initialized by parser (JsDocContainer) node.flowNode = undefined; // initialized by binder (FlowContainer) @@ -1383,7 +1393,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } function createBasePrivateIdentifier(escapedText: __String) { - const node = baseFactory.createBasePrivateIdentifierNode(SyntaxKind.PrivateIdentifier) as Mutable; + const node = new PrivateIdentifierObject() as Mutable; + if (markSynthetic) node.flags |= NodeFlags.Synthesized; node.escapedText = escapedText; node.transformFlags |= TransformFlags.ContainsClassFields; return node; @@ -1431,7 +1442,9 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode // function createBaseToken(kind: T["kind"]) { - return baseFactory.createBaseTokenNode(kind) as Mutable; + const node = new TokenObject(kind) as Node as Mutable; + if (markSynthetic) node.flags |= NodeFlags.Synthesized; + return node; } // @api @@ -6026,7 +6039,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode endOfFileToken: EndOfFileToken, flags: NodeFlags, ) { - const node = baseFactory.createBaseSourceFileNode(SyntaxKind.SourceFile) as Mutable; + const node = new SourceFileObject() as Mutable; + if (markSynthetic) node.flags |= NodeFlags.Synthesized; node.statements = createNodeArray(statements); node.endOfFileToken = endOfFileToken; node.flags |= flags; @@ -6115,7 +6129,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode function cloneSourceFileWorker(source: SourceFile) { // TODO: This mechanism for cloning results in megamorphic property reads and writes. In future perf-related // work, we should consider switching explicit property assignments instead of using `for..in`. - const node = baseFactory.createBaseSourceFileNode(SyntaxKind.SourceFile) as Mutable; + const node = new SourceFileObject() as Mutable; + if (markSynthetic) node.flags |= NodeFlags.Synthesized; node.flags |= source.flags & ~NodeFlags.Synthesized; for (const p in source) { if (hasProperty(node, p) || !hasProperty(source, p)) { @@ -6209,7 +6224,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } // @api - function createSyntaxList(children: Node[]) { + function createSyntaxList(children: readonly Node[]) { const node = createBaseNode(SyntaxKind.SyntaxList); setNodeChildren(node, children); return node; @@ -6369,11 +6384,12 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode return clonePrivateIdentifier(node) as T & PrivateIdentifier; } - const clone = !isNodeKind(node.kind) ? baseFactory.createBaseTokenNode(node.kind) as T : - baseFactory.createBaseNode(node.kind) as T; + const clone = !isNodeKind(node.kind) ? new TokenObject(node.kind) as Node as Mutable : + new NodeObject(node.kind) as Node as Mutable; - (clone as Mutable).flags |= node.flags & ~NodeFlags.Synthesized; - (clone as Mutable).transformFlags = node.transformFlags; + if (markSynthetic) clone.flags |= NodeFlags.Synthesized; + clone.flags |= node.flags & ~NodeFlags.Synthesized; + clone.transformFlags = node.transformFlags; setOriginal(clone, node); for (const key in node) { @@ -7369,22 +7385,7 @@ export function getTransformFlagsSubtreeExclusions(kind: SyntaxKind) { } } -const baseFactory = createBaseNodeFactory(); - -function makeSynthetic(node: Node) { - (node as Mutable).flags |= NodeFlags.Synthesized; - return node; -} - -const syntheticFactory: BaseNodeFactory = { - createBaseSourceFileNode: kind => makeSynthetic(baseFactory.createBaseSourceFileNode(kind)), - createBaseIdentifierNode: kind => makeSynthetic(baseFactory.createBaseIdentifierNode(kind)), - createBasePrivateIdentifierNode: kind => makeSynthetic(baseFactory.createBasePrivateIdentifierNode(kind)), - createBaseTokenNode: kind => makeSynthetic(baseFactory.createBaseTokenNode(kind)), - createBaseNode: kind => makeSynthetic(baseFactory.createBaseNode(kind)), -}; - -export const factory = createNodeFactory(NodeFactoryFlags.NoIndentationOnFreshPropertyAccess, syntheticFactory); +export const factory = createNodeFactory(NodeFactoryFlags.NoIndentationOnFreshPropertyAccess | NodeFactoryFlags.MarkSynthetic); let SourceMapSource: new (fileName: string, text: string, skipTrivia?: (pos: number) => number) => SourceMapSource; @@ -7392,7 +7393,7 @@ let SourceMapSource: new (fileName: string, text: string, skipTrivia?: (pos: num * Create an external source map source file reference */ export function createSourceMapSource(fileName: string, text: string, skipTrivia?: (pos: number) => number): SourceMapSource { - return new (SourceMapSource || (SourceMapSource = objectAllocator.getSourceMapSourceConstructor()))(fileName, text, skipTrivia); + return new SourceMapSourceObject(fileName, text, skipTrivia); } // Utilities diff --git a/src/compiler/nodeConstructors.ts b/src/compiler/nodeConstructors.ts new file mode 100644 index 0000000000000..28dde59d74a91 --- /dev/null +++ b/src/compiler/nodeConstructors.ts @@ -0,0 +1,728 @@ +import { + __String, + AmdDependency, + append, + AssignmentDeclarationKind, + BinaryExpression, + canHaveJSDoc, + CheckJsDirective, + CommentDirective, + computePositionOfLineAndCharacter, + createMultiMap, + createScanner, + Debug, + Declaration, + DiagnosticWithLocation, + EmitNode, + emptyArray, + EndOfFileToken, + EntityName, + ExportDeclaration, + factory, + FileReference, + find, + FlowNode, + forEach, + forEachChild, + FunctionLikeDeclaration, + getAssignmentDeclarationKind, + getLineAndCharacterOfPosition, + getLineStarts, + getNameFromPropertyName, + getNodeChildren, + getNonAssignedNameOfDeclaration, + getSourceFileOfNode, + getTokenPosOfNode, + HasLocals, + hasSyntacticModifier, + hasTabstop, + Identifier, + idText, + ImportDeclaration, + isBindingPattern, + isComputedPropertyName, + IScriptSnapshot, + isJSDocCommentContainingNode, + isNamedExports, + isNodeKind, + isPropertyAccessExpression, + isPropertyName, + isTokenKind, + JSDoc, + LanguageVariant, + lastOrUndefined, + LineAndCharacter, + ModeAwareCache, + ModifierFlags, + Mutable, + Node, + NodeArray, + NodeFlags, + PackageJsonInfo, + Path, + PatternAmbientModule, + PrivateIdentifier, + ReadonlyPragmaMap, + RedirectInfo, + ResolutionMode, + ResolvedModuleWithFailedLookupLocations, + ResolvedTypeReferenceDirectiveWithFailedLookupLocations, + Scanner, + ScriptKind, + ScriptTarget, + setNodeChildren, + SourceFile, + SourceFileLike, + Statement, + StringLiteral, + StringLiteralLike, + Symbol, + SymbolTable, + SyntaxKind, + SyntaxList, + TextChangeRange, + Token, + TransformFlags, + UnderscoreEscapedMap, + updateSourceFile, + VariableDeclaration, +} from "./_namespaces/ts"; + +/** @internal */ +export class NodeObject implements Node { + pos: number; + end: number; + kind: SyntaxKind; + id: number; + flags: NodeFlags; + transformFlags: TransformFlags; + parent: Node; + original: Node | undefined; + // NOTE: Non-token nodes often need emitNode entries, so they are defined to reduce polymorphism. + emitNode: EmitNode | undefined; + // NOTE: Non-token nodes may have modifiers, so they are defined to reduce polymorphism + modifierFlagsCache: ModifierFlags; // TODO: move this off `Node` + + constructor(kind: SyntaxKind) { + this.pos = -1; + this.end = -1; + this.kind = kind; + this.id = 0; + this.flags = NodeFlags.None; + this.modifierFlagsCache = ModifierFlags.None; + this.transformFlags = TransformFlags.None; + this.parent = undefined!; + this.original = undefined; + this.emitNode = undefined; + } + + getSourceFile(): SourceFile { + return getSourceFileOfNode(this); + } + + getStart(sourceFile?: SourceFileLike, includeJsDocComment?: boolean): number { + Debug.assertValidTextRange(this); + return getTokenPosOfNode(this, sourceFile, includeJsDocComment); + } + + getFullStart(): number { + Debug.assertValidTextRange(this); + return this.pos; + } + + getEnd(): number { + Debug.assertValidTextRange(this); + return this.end; + } + + getWidth(sourceFile?: SourceFile): number { + Debug.assertValidTextRange(this); + return this.getEnd() - this.getStart(sourceFile); + } + + getFullWidth(): number { + Debug.assertValidTextRange(this); + return this.end - this.pos; + } + + getLeadingTriviaWidth(sourceFile?: SourceFile): number { + return this.getStart(sourceFile) - this.pos; + } + + getFullText(sourceFile?: SourceFile): string { + Debug.assertValidTextRange(this); + sourceFile ??= this.getSourceFile(); + return sourceFile.text.substring(this.pos, this.end); + } + + getText(sourceFile?: SourceFile): string { + Debug.assertValidTextRange(this); + sourceFile ??= this.getSourceFile(); + return sourceFile.text.substring(this.getStart(sourceFile), this.getEnd()); + } + + getChildCount(sourceFile?: SourceFile): number { + return this.getChildren(sourceFile).length; + } + + getChildAt(index: number, sourceFile?: SourceFile): Node { + return this.getChildren(sourceFile)[index]; + } + + forEachChild(cbNode: (node: Node) => T, cbNodeArray?: (nodes: NodeArray) => T): T | undefined { + return forEachChild(this, cbNode, cbNodeArray); + } + + getChildren(sourceFile?: SourceFileLike): readonly Node[] { + Debug.assertValidTextRange(this, "Node without a real position cannot be scanned and thus has no token nodes - use forEachChild and collect the result if that's fine"); + return getNodeChildren(this) ?? setNodeChildren(this, createChildren(this, sourceFile)); + } + + getFirstToken(sourceFile?: SourceFileLike): Node | undefined { + Debug.assertValidTextRange(this); + const children = this.getChildren(sourceFile); + if (!children.length) { + return undefined; + } + + const child = find(children, child => child.kind < SyntaxKind.FirstJSDocNode || child.kind > SyntaxKind.LastJSDocNode); + if (!child) { + return undefined; + } + + return child.kind < SyntaxKind.FirstNode ? child : child.getFirstToken(sourceFile); + } + + getLastToken(sourceFile?: SourceFileLike): Node | undefined { + Debug.assertValidTextRange(this); + const children = this.getChildren(sourceFile); + if (!children.length) { + return undefined; + } + + const child = lastOrUndefined(children); + if (!child) { + return undefined; + } + + return child.kind < SyntaxKind.FirstNode ? child : child.getLastToken(sourceFile); + } +} + +/** @internal */ +export abstract class TokenOrIdentifierObject implements Node { + pos: number; + end: number; + kind: TKind; + id: number; + flags: NodeFlags; + transformFlags: TransformFlags; + parent: Node; + original: Node | undefined; + emitNode: EmitNode | undefined; + // NOTE: Tokens cannot have modifiers, so they are declared but not defined to reduce memory footprint + declare modifierFlagsCache: ModifierFlags; + + constructor(kind: TKind) { + this.pos = -1; + this.end = -1; + this.kind = kind; + this.id = 0; + this.flags = NodeFlags.None; + this.transformFlags = TransformFlags.None; + this.parent = undefined!; + this.original = undefined; + this.emitNode = undefined; + } + + getSourceFile(): SourceFile { + return getSourceFileOfNode(this); + } + + getStart(sourceFile?: SourceFileLike, includeJsDocComment?: boolean): number { + Debug.assertValidTextRange(this); + return getTokenPosOfNode(this, sourceFile, includeJsDocComment); + } + + getFullStart(): number { + Debug.assertValidTextRange(this); + return this.pos; + } + + getEnd(): number { + Debug.assertValidTextRange(this); + return this.end; + } + + getWidth(sourceFile?: SourceFile): number { + Debug.assertValidTextRange(this); + return this.getEnd() - this.getStart(sourceFile); + } + + getFullWidth(): number { + Debug.assertValidTextRange(this); + return this.end - this.pos; + } + + getLeadingTriviaWidth(sourceFile?: SourceFile): number { + return this.getStart(sourceFile) - this.pos; + } + + getFullText(sourceFile?: SourceFile): string { + Debug.assertValidTextRange(this); + sourceFile ??= this.getSourceFile(); + return sourceFile.text.substring(this.pos, this.end); + } + + getText(sourceFile?: SourceFile): string { + Debug.assertValidTextRange(this); + sourceFile ??= this.getSourceFile(); + return sourceFile.text.substring(this.getStart(sourceFile), this.getEnd()); + } + + getChildCount(sourceFile?: SourceFile): number { + return this.getChildren(sourceFile).length; + } + + getChildAt(index: number, sourceFile?: SourceFile): Node { + return this.getChildren(sourceFile)[index]; + } + + forEachChild(_cbNode: (node: Node) => T, _cbNodeArray?: (nodes: NodeArray) => T): T | undefined { + // Tokens cannot have source element children + return undefined; + } + + getChildren(_sourceFile?: SourceFileLike): readonly Node[] { + return this.kind === SyntaxKind.EndOfFileToken ? (this as Node as EndOfFileToken).jsDoc ?? emptyArray : emptyArray; + } + + getFirstToken(_sourceFile?: SourceFileLike): Node | undefined { + // Tokens cannot have source element children + return undefined!; + } + + getLastToken(_sourceFile?: SourceFileLike): Node | undefined { + // Tokens cannot have source element children + return undefined!; + } +} + +/** @internal */ +export class TokenObject extends TokenOrIdentifierObject implements Token { + constructor(kind: TKind) { + super(kind); + } +} + +/** @internal */ +export class IdentifierObject extends TokenOrIdentifierObject implements Identifier { + // #region Brands + declare _primaryExpressionBrand: any; + declare _memberExpressionBrand: any; + declare _leftHandSideExpressionBrand: any; + declare _updateExpressionBrand: any; + declare _unaryExpressionBrand: any; + declare _expressionBrand: any; + declare _declarationBrand: any; + declare _jsdocContainerBrand: any; + declare _flowContainerBrand: any; + // #endregion Brands + + escapedText: __String; + symbol: Symbol; // initialized by checker + jsDoc?: JSDoc[]; // initialized by parser (JsDocContainer) + flowNode?: FlowNode; // initialized by binder (FlowContainer) + + constructor() { + super(SyntaxKind.Identifier); + this.escapedText = "" as __String; + this.symbol = undefined!; + this.jsDoc = undefined; + this.flowNode = undefined; + } + + get text(): string { + return idText(this); + } +} + +/** @internal */ +export class PrivateIdentifierObject extends TokenOrIdentifierObject implements PrivateIdentifier { + // #region Brands + declare _primaryExpressionBrand: any; + declare _memberExpressionBrand: any; + declare _leftHandSideExpressionBrand: any; + declare _updateExpressionBrand: any; + declare _unaryExpressionBrand: any; + declare _expressionBrand: any; + // #endregion Brands + + escapedText: __String; + + constructor() { + super(SyntaxKind.PrivateIdentifier); + this.escapedText = "" as __String; + } + + get text(): string { + return idText(this); + } +} + +/** @internal */ +export class SourceFileObject extends NodeObject implements SourceFile { + // #region Brands + declare _declarationBrand: any; + declare _localsContainerBrand: any; + // #endregion Brands + + declare kind: SyntaxKind.SourceFile; + declare modifierFlagsCache: ModifierFlags; + declare statements: NodeArray; + declare endOfFileToken: Token; + declare fileName: string; + declare path: Path; + declare text: string; + declare resolvedPath: Path; + declare originalFileName: string; + declare redirectInfo?: RedirectInfo; + declare amdDependencies: readonly AmdDependency[]; + declare moduleName?: string; + declare referencedFiles: readonly FileReference[]; + declare typeReferenceDirectives: readonly FileReference[]; + declare libReferenceDirectives: readonly FileReference[]; + declare languageVariant: LanguageVariant; + declare isDeclarationFile: boolean; + declare renamedDependencies?: ReadonlyMap; + declare hasNoDefaultLib: boolean; + declare languageVersion: ScriptTarget; + declare impliedNodeFormat?: ResolutionMode; + declare packageJsonLocations?: readonly string[]; + declare packageJsonScope?: PackageJsonInfo; + declare scriptKind: ScriptKind; + declare externalModuleIndicator?: true | Node; + declare setExternalModuleIndicator?: (file: SourceFile) => void; + declare commonJsModuleIndicator?: Node; + declare jsGlobalAugmentations?: SymbolTable; + declare identifiers: ReadonlyMap; + declare nodeCount: number; + declare identifierCount: number; + declare symbolCount: number; + declare parseDiagnostics: DiagnosticWithLocation[]; + declare bindDiagnostics: DiagnosticWithLocation[]; + declare bindSuggestionDiagnostics?: DiagnosticWithLocation[]; + declare jsDocDiagnostics?: DiagnosticWithLocation[]; + declare additionalSyntacticDiagnostics?: readonly DiagnosticWithLocation[]; + declare lineMap: readonly number[]; + declare classifiableNames?: ReadonlySet<__String>; + declare commentDirectives?: CommentDirective[]; + declare resolvedModules?: ModeAwareCache; + declare resolvedTypeReferenceDirectiveNames?: ModeAwareCache; + declare imports: readonly StringLiteralLike[]; + declare moduleAugmentations: readonly (Identifier | StringLiteral)[]; + declare patternAmbientModules?: PatternAmbientModule[]; + declare ambientModuleNames: readonly string[]; + declare checkJsDirective?: CheckJsDirective; + declare version: string; + declare pragmas: ReadonlyPragmaMap; + declare localJsxNamespace?: __String; + declare localJsxFragmentNamespace?: __String; + declare localJsxFactory?: EntityName; + declare localJsxFragmentFactory?: EntityName; + declare endFlowNode?: FlowNode; + declare symbol: Symbol; + declare localSymbol?: Symbol; + declare locals?: SymbolTable; + declare nextContainer?: HasLocals; + declare scriptSnapshot: IScriptSnapshot; + declare nameTable: UnderscoreEscapedMap | undefined; + + declare private namedDeclarations: Map | undefined; + + constructor() { + super(SyntaxKind.SourceFile); + } + + public update(newText: string, textChangeRange: TextChangeRange): SourceFile { + return updateSourceFile(this, newText, textChangeRange); + } + + public getLineAndCharacterOfPosition(position: number): LineAndCharacter { + return getLineAndCharacterOfPosition(this, position); + } + + public getLineStarts(): readonly number[] { + return getLineStarts(this); + } + + public getPositionOfLineAndCharacter(line: number, character: number, allowEdits?: true): number { + return computePositionOfLineAndCharacter(getLineStarts(this), line, character, this.text, allowEdits); + } + + public getLineEndOfPosition(pos: number): number { + const { line } = this.getLineAndCharacterOfPosition(pos); + const lineStarts = this.getLineStarts(); + + let lastCharPos: number | undefined; + if (line + 1 >= lineStarts.length) { + lastCharPos = this.getEnd(); + } + if (!lastCharPos) { + lastCharPos = lineStarts[line + 1] - 1; + } + + const fullText = this.getFullText(); + // if the new line is "\r\n", we should return the last non-new-line-character position + return fullText[lastCharPos] === "\n" && fullText[lastCharPos - 1] === "\r" ? lastCharPos - 1 : lastCharPos; + } + + public getNamedDeclarations(): Map { + if (!this.namedDeclarations) { + this.namedDeclarations = this.computeNamedDeclarations(); + } + + return this.namedDeclarations; + } + + private computeNamedDeclarations(): Map { + const result = createMultiMap(); + + this.forEachChild(visit); + + return result; + + function addDeclaration(declaration: Declaration) { + const name = getDeclarationName(declaration); + if (name) { + result.add(name, declaration); + } + } + + function getDeclarations(name: string) { + let declarations = result.get(name); + if (!declarations) { + result.set(name, declarations = []); + } + return declarations; + } + + function getDeclarationName(declaration: Declaration) { + const name = getNonAssignedNameOfDeclaration(declaration); + return name && (isComputedPropertyName(name) && isPropertyAccessExpression(name.expression) ? idText(name.expression.name) + : isPropertyName(name) ? getNameFromPropertyName(name) : undefined); + } + + function visit(node: Node): void { + switch (node.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + const functionDeclaration = node as FunctionLikeDeclaration; + const declarationName = getDeclarationName(functionDeclaration); + + if (declarationName) { + const declarations = getDeclarations(declarationName); + const lastDeclaration = lastOrUndefined(declarations); + + // Check whether this declaration belongs to an "overload group". + if (lastDeclaration && functionDeclaration.parent === lastDeclaration.parent && functionDeclaration.symbol === lastDeclaration.symbol) { + // Overwrite the last declaration if it was an overload + // and this one is an implementation. + if (functionDeclaration.body && !(lastDeclaration as FunctionLikeDeclaration).body) { + declarations[declarations.length - 1] = functionDeclaration; + } + } + else { + declarations.push(functionDeclaration); + } + } + forEachChild(node, visit); + break; + + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ImportEqualsDeclaration: + case SyntaxKind.ExportSpecifier: + case SyntaxKind.ImportSpecifier: + case SyntaxKind.ImportClause: + case SyntaxKind.NamespaceImport: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.TypeLiteral: + addDeclaration(node as Declaration); + forEachChild(node, visit); + break; + + case SyntaxKind.Parameter: + // Only consider parameter properties + if (!hasSyntacticModifier(node, ModifierFlags.ParameterPropertyModifier)) { + break; + } + // falls through + + case SyntaxKind.VariableDeclaration: + case SyntaxKind.BindingElement: { + const decl = node as VariableDeclaration; + if (isBindingPattern(decl.name)) { + forEachChild(decl.name, visit); + break; + } + if (decl.initializer) { + visit(decl.initializer); + } + } + // falls through + case SyntaxKind.EnumMember: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + addDeclaration(node as Declaration); + break; + + case SyntaxKind.ExportDeclaration: + // Handle named exports case e.g.: + // export {a, b as B} from "mod"; + const exportDeclaration = node as ExportDeclaration; + if (exportDeclaration.exportClause) { + if (isNamedExports(exportDeclaration.exportClause)) { + forEach(exportDeclaration.exportClause.elements, visit); + } + else { + visit(exportDeclaration.exportClause.name); + } + } + break; + + case SyntaxKind.ImportDeclaration: + const importClause = (node as ImportDeclaration).importClause; + if (importClause) { + // Handle default import case e.g.: + // import d from "mod"; + if (importClause.name) { + addDeclaration(importClause.name); + } + + // Handle named bindings in imports e.g.: + // import * as NS from "mod"; + // import {a, b as B} from "mod"; + if (importClause.namedBindings) { + if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { + addDeclaration(importClause.namedBindings); + } + else { + forEach(importClause.namedBindings.elements, visit); + } + } + } + break; + + case SyntaxKind.BinaryExpression: + if (getAssignmentDeclarationKind(node as BinaryExpression) !== AssignmentDeclarationKind.None) { + addDeclaration(node as BinaryExpression); + } + // falls through + + default: + forEachChild(node, visit); + } + } + } +} + +let scanner: Scanner | undefined; + +function createChildren(node: Node, sourceFile: SourceFileLike | undefined): readonly Node[] { + if (!isNodeKind(node.kind)) { + return emptyArray; + } + + let children: Node[] | undefined; + + if (isJSDocCommentContainingNode(node)) { + /** Don't add trivia for "tokens" since this is in a comment. */ + forEachChild(node, child => { + children = append(children, child); + }); + return children ?? emptyArray; + } + + scanner ??= createScanner(ScriptTarget.Latest, /*skipTrivia*/ true); + scanner.setText((sourceFile || getSourceFileOfNode(node)).text); + + let pos = node.pos; + const processNode = (child: Node) => { + children = addSyntheticNodes(children, pos, child.pos, node); + children = append(children, child); + pos = child.end; + }; + const processNodes = (nodes: NodeArray) => { + children = addSyntheticNodes(children, pos, nodes.pos, node); + children = append(children, createSyntaxList(nodes, node)); + pos = nodes.end; + }; + + // jsDocComments need to be the first children + if (canHaveJSDoc(node)) { + forEach(node.jsDoc, processNode); + } + + // For syntactic classifications, all trivia are classified together, including jsdoc comments. + // For that to work, the jsdoc comments should still be the leading trivia of the first child. + // Restoring the scanner position ensures that. + pos = node.pos; + forEachChild(node, processNode, processNodes); + children = addSyntheticNodes(children, pos, node.end, node); + scanner.setText(undefined); + return children ?? emptyArray; +} + +function addSyntheticNodes(nodes: Node[] | undefined, pos: number, end: number, parent: Node): Node[] | undefined { + Debug.assertIsDefined(scanner); + scanner.resetTokenState(pos); + while (pos < end) { + const kind = scanner.scan(); + const textPos = scanner.getTokenEnd(); + if (textPos <= end) { + if (kind === SyntaxKind.Identifier) { + if (hasTabstop(parent)) { + continue; + } + Debug.fail(`Did not expect ${Debug.formatSyntaxKind(parent.kind)} to have an Identifier in its trivia`); + } + Debug.assert(isTokenKind(kind)); + const token = factory.createToken(kind) as Mutable>; + token.pos = pos; + token.end = textPos; + token.parent = parent; + token.flags = parent.flags & NodeFlags.ContextFlags; + nodes = append(nodes, token); + } + pos = textPos; + if (kind === SyntaxKind.EndOfFileToken) { + break; + } + } + return nodes; +} + +function createSyntaxList(nodes: NodeArray, parent: Node): Node { + let children: Node[] | undefined; + let pos = nodes.pos; + for (const node of nodes) { + children = addSyntheticNodes(children, pos, node.pos, parent); + children = append(children, node); + pos = node.end; + } + children = addSyntheticNodes(children, pos, nodes.end, parent); + const list = factory.createSyntaxList(children ?? emptyArray) as Mutable; + list.pos = nodes.pos; + list.end = nodes.end; + list.parent = parent; + list.flags = parent.flags & NodeFlags.ContextFlags; + return list; +} diff --git a/src/compiler/objectConstructors.ts b/src/compiler/objectConstructors.ts new file mode 100644 index 0000000000000..c1a2a1654c006 --- /dev/null +++ b/src/compiler/objectConstructors.ts @@ -0,0 +1,350 @@ +import { + __String, + BaseType, + Debug, + Declaration, + DestructuringPattern, + getLineAndCharacterOfPosition, + getObjectFlags, + identity, + IndexKind, + IndexType, + InterfaceType, + IntersectionType, + isThisTypeParameter, + JSDocSignature, + JSDocTagInfo, + LineAndCharacter, + LiteralType, + Node, + NumberLiteralType, + ObjectFlags, + ObjectType, + Signature, + SignatureDeclaration, + SignatureFlags, + SignatureKind, + SourceMapSource, + StringLiteralType, + Symbol, + SymbolDisplayPart, + SymbolFlags, + SymbolLinks, + symbolName, + SymbolTable, + Type, + TypeChecker, + TypeFlags, + TypeMapper, + TypeParameter, + TypePredicate, + TypeReference, + UnionOrIntersectionType, + UnionType, +} from "./_namespaces/ts"; +import { SignatureObjectInternals } from "./signatureObjectInternals"; +import { SymbolObjectInternals } from "./symbolObjectInternals"; + +/** @internal */ +export class SymbolObject implements Symbol { + flags: SymbolFlags = 0; + escapedName: __String = "" as __String; + declarations?: Declaration[] = undefined; + valueDeclaration?: Declaration = undefined; + + // TODO: Can we remove this property? Possibly not, but most call sites use it as a key in a Map, but we could store + // the symbol itself. + id = 0; + + // TODO: Can we remove this property? We could possibly just change `mergedSymbols` in checker to be a Map with this + // symbol as the key. + mergeId = 0; + parent?: Symbol = undefined; + members?: SymbolTable = undefined; + exports?: SymbolTable = undefined; + exportSymbol?: Symbol = undefined; + + // TODO: Can we remove this property or turn it into a flag along with `isReplaceableByMethod`? + constEnumOnlyModule: boolean | undefined = undefined; + // TODO: Does this property require all symbol flags or a subset? Could we use a different enum and combine it with + // `constEnumOnlyModule` and `isReplaceableByMethod`? + isReferenced?: SymbolFlags = undefined; + // TODO: This is set by checker and not the binder. It should be moved to `SymbolLinks`. + lastAssignmentPos?: number = undefined; + links?: SymbolLinks = undefined; // used by TransientSymbol + + // TODO: Review these for polymorphism: + declare isReplaceableByMethod?: boolean; + declare assignmentDeclarationMembers?: Map; + declare globalExports?: SymbolTable; + + constructor(flags: SymbolFlags, name: __String) { + this.flags = flags; + this.escapedName = name; + } + + getFlags(): SymbolFlags { + return this.flags; + } + + get name(): string { + return symbolName(this); + } + + getEscapedName(): __String { + return this.escapedName; + } + + getName(): string { + return this.name; + } + + getDeclarations(): Declaration[] | undefined { + return this.declarations; + } + + getDocumentationComment(checker: TypeChecker | undefined): SymbolDisplayPart[] { + return SymbolObjectInternals.internals.getDocumentationComment(this, checker); + } + + getContextualDocumentationComment(context: Node | undefined, checker: TypeChecker | undefined): SymbolDisplayPart[] { + return SymbolObjectInternals.internals.getContextualDocumentationComment(this, context, checker); + } + + getJsDocTags(checker?: TypeChecker): JSDocTagInfo[] { + return SymbolObjectInternals.internals.getJsDocTags(this, checker); + } + + getContextualJsDocTags(context: Node | undefined, checker: TypeChecker | undefined): JSDocTagInfo[] { + return SymbolObjectInternals.internals.getContextualJsDocTags(this, context, checker); + } +} + +/** @internal */ +export class TypeObject implements Type { + flags: TypeFlags; + checker: TypeChecker; + id = 0; + + // TODO: Review for polymorphism + declare symbol: Symbol; + declare pattern?: DestructuringPattern; + declare aliasSymbol?: Symbol; + declare aliasTypeArguments?: readonly Type[]; + declare permissiveInstantiation?: Type; + declare restrictiveInstantiation?: Type; + declare uniqueLiteralFilledInstantiation?: Type; + declare immediateBaseConstraint?: Type; + declare widened?: Type; + + constructor(checker: TypeChecker, flags: TypeFlags) { + this.flags = flags; + this.checker = checker; + } + + getFlags(): TypeFlags { + return this.flags; + } + + getSymbol(): Symbol | undefined { + return this.symbol; + } + + getProperties(): Symbol[] { + return this.checker.getPropertiesOfType(this); + } + + getProperty(propertyName: string): Symbol | undefined { + return this.checker.getPropertyOfType(this, propertyName); + } + + getApparentProperties(): Symbol[] { + return this.checker.getAugmentedPropertiesOfType(this); + } + + getCallSignatures(): readonly Signature[] { + return this.checker.getSignaturesOfType(this, SignatureKind.Call); + } + + getConstructSignatures(): readonly Signature[] { + return this.checker.getSignaturesOfType(this, SignatureKind.Construct); + } + + getStringIndexType(): Type | undefined { + return this.checker.getIndexTypeOfType(this, IndexKind.String); + } + + getNumberIndexType(): Type | undefined { + return this.checker.getIndexTypeOfType(this, IndexKind.Number); + } + + getBaseTypes(): BaseType[] | undefined { + return this.isClassOrInterface() ? this.checker.getBaseTypes(this) : undefined; + } + + isNullableType(): boolean { + return this.checker.isNullableType(this); + } + + getNonNullableType(): Type { + return this.checker.getNonNullableType(this); + } + + getNonOptionalType(): Type { + return this.checker.getNonOptionalType(this); + } + + getConstraint(): Type | undefined { + return this.checker.getBaseConstraintOfType(this); + } + + getDefault(): Type | undefined { + return this.checker.getDefaultFromTypeParameter(this); + } + + isUnion(): this is UnionType { + return !!(this.flags & TypeFlags.Union); + } + + isIntersection(): this is IntersectionType { + return !!(this.flags & TypeFlags.Intersection); + } + + isUnionOrIntersection(): this is UnionOrIntersectionType { + return !!(this.flags & TypeFlags.UnionOrIntersection); + } + + isLiteral(): this is LiteralType { + return !!(this.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.BigIntLiteral)); + } + + isStringLiteral(): this is StringLiteralType { + return !!(this.flags & TypeFlags.StringLiteral); + } + + isNumberLiteral(): this is NumberLiteralType { + return !!(this.flags & TypeFlags.NumberLiteral); + } + + isTypeParameter(): this is TypeParameter { + return !!(this.flags & TypeFlags.TypeParameter); + } + + isClassOrInterface(): this is InterfaceType { + return !!(getObjectFlags(this) & ObjectFlags.ClassOrInterface); + } + + isClass(): this is InterfaceType { + return !!(getObjectFlags(this) & ObjectFlags.Class); + } + + isIndexType(): this is IndexType { + return isIndexType(this); + } + + /** + * This polyfills `referenceType.typeArguments` for API consumers + */ + get typeArguments() { + if (getObjectFlags(this) & ObjectFlags.Reference) { + return this.checker.getTypeArguments(this as Type as TypeReference); + } + return undefined; + } +} + +/** @internal */ +export class SignatureObject implements Signature { + flags: SignatureFlags; + checker: TypeChecker; + + // TODO: Review for polymorphism: + declare declaration?: JSDocSignature | SignatureDeclaration; + declare typeParameters?: readonly TypeParameter[]; + declare parameters: readonly Symbol[]; + declare thisParameter?: Symbol; + declare resolvedReturnType?: Type; + declare resolvedTypePredicate?: TypePredicate; + // TODO: Could we combine `minArgumentCount` and `resolvedMinArgumentCount` as two int16 values? How much slower + // would getters/setters be for these? + declare minArgumentCount: number; + declare resolvedMinArgumentCount?: number; + declare target?: Signature; + declare mapper?: TypeMapper; + declare compositeSignatures?: Signature[]; + declare compositeKind?: TypeFlags; + declare erasedSignatureCache?: Signature; + declare canonicalSignatureCache?: Signature; + declare baseSignatureCache?: Signature; + declare optionalCallSignatureCache?: { inner?: Signature; outer?: Signature; }; + declare isolatedSignatureType?: ObjectType; + declare instantiations?: Map; + + // TODO: Added by services, review for migration/polymorhpism: + // documentationComment?: SymbolDisplayPart[]; + // jsDocTags?: JSDocTagInfo[]; // same + + constructor(checker: TypeChecker, flags: SignatureFlags) { + this.flags = flags; + this.checker = checker; + } + + getDeclaration(): JSDocSignature | SignatureDeclaration { + return this.declaration ?? Debug.fail(); + } + + getTypeParameters(): readonly TypeParameter[] | undefined { + return this.typeParameters; + } + + getParameters(): readonly Symbol[] { + return this.parameters; + } + + getReturnType(): Type { + return this.checker.getReturnTypeOfSignature(this); + } + + getTypeParameterAtPosition(pos: number): Type { + const type = this.checker.getParameterType(this, pos); + if (isIndexType(type) && isThisTypeParameter(type.type)) { + const constraint = type.type.checker.getBaseConstraintOfType(type.type); + if (constraint) { + return this.checker.getIndexType(constraint); + } + } + return type; + } + + getDocumentationComment(): SymbolDisplayPart[] { + return SignatureObjectInternals.internals.getDocumentationComment(this); + } + + getJsDocTags(): JSDocTagInfo[] { + return SignatureObjectInternals.internals.getJsDocTags(this); + } +} + +/** @internal */ +export class SourceMapSourceObject implements SourceMapSource { + fileName: string; + text: string; + skipTrivia: (pos: number) => number; + + // TODO: Review for polymorphism: + declare lineMap: readonly number[]; + + constructor(fileName: string, text: string, skipTrivia: (pos: number) => number = identity) { + this.fileName = fileName; + this.text = text; + this.skipTrivia = skipTrivia; + } + + public getLineAndCharacterOfPosition(pos: number): LineAndCharacter { + return getLineAndCharacterOfPosition(this, pos); + } +} + +function isIndexType(type: Type): type is IndexType { + return !!(type.flags & TypeFlags.Index); +} diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 35366ac9d6513..0acc708c2c0a7 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -13,7 +13,6 @@ import { AsteriskToken, attachFileToDiagnostics, AwaitExpression, - BaseNodeFactory, BinaryExpression, BinaryOperatorToken, BindingElement, @@ -280,7 +279,6 @@ import { NoSubstitutionTemplateLiteral, NullLiteral, NumericLiteral, - objectAllocator, ObjectBindingPattern, ObjectLiteralElementLike, ObjectLiteralExpression, @@ -414,27 +412,8 @@ const enum SpeculationKind { Reparse, } -let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; -let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; -let IdentifierConstructor: new (kind: SyntaxKind.Identifier, pos: number, end: number) => Node; -let PrivateIdentifierConstructor: new (kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) => Node; -let SourceFileConstructor: new (kind: SyntaxKind.SourceFile, pos: number, end: number) => Node; - -/** - * NOTE: You should not use this, it is only exported to support `createNode` in `~/src/deprecatedCompat/deprecations.ts`. - * - * @internal - */ -export const parseBaseNodeFactory: BaseNodeFactory = { - createBaseSourceFileNode: kind => new (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))(kind, -1, -1), - createBaseIdentifierNode: kind => new (IdentifierConstructor || (IdentifierConstructor = objectAllocator.getIdentifierConstructor()))(kind, -1, -1), - createBasePrivateIdentifierNode: kind => new (PrivateIdentifierConstructor || (PrivateIdentifierConstructor = objectAllocator.getPrivateIdentifierConstructor()))(kind, -1, -1), - createBaseTokenNode: kind => new (TokenConstructor || (TokenConstructor = objectAllocator.getTokenConstructor()))(kind, -1, -1), - createBaseNode: kind => new (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()))(kind, -1, -1), -}; - /** @internal */ -export const parseNodeFactory: NodeFactory = createNodeFactory(NodeFactoryFlags.NoParenthesizerRules, parseBaseNodeFactory); +export const parseNodeFactory: NodeFactory = createNodeFactory(NodeFactoryFlags.NoParenthesizerRules); function visitNode(cbNode: (node: Node) => T, node: Node | undefined): T | undefined { return node && cbNode(node); @@ -1434,29 +1413,7 @@ namespace Parser { var disallowInAndDecoratorContext = NodeFlags.DisallowInContext | NodeFlags.DecoratorContext; - // capture constructors in 'initializeState' to avoid null checks - var NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; - var TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; - var IdentifierConstructor: new (kind: SyntaxKind.Identifier, pos: number, end: number) => Identifier; - var PrivateIdentifierConstructor: new (kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) => PrivateIdentifier; - var SourceFileConstructor: new (kind: SyntaxKind.SourceFile, pos: number, end: number) => SourceFile; - - function countNode(node: Node) { - nodeCount++; - return node; - } - - // Rather than using `createBaseNodeFactory` here, we establish a `BaseNodeFactory` that closes over the - // constructors above, which are reset each time `initializeState` is called. - var baseNodeFactory: BaseNodeFactory = { - createBaseSourceFileNode: kind => countNode(new SourceFileConstructor(kind, /*pos*/ 0, /*end*/ 0)), - createBaseIdentifierNode: kind => countNode(new IdentifierConstructor(kind, /*pos*/ 0, /*end*/ 0)), - createBasePrivateIdentifierNode: kind => countNode(new PrivateIdentifierConstructor(kind, /*pos*/ 0, /*end*/ 0)), - createBaseTokenNode: kind => countNode(new TokenConstructor(kind, /*pos*/ 0, /*end*/ 0)), - createBaseNode: kind => countNode(new NodeConstructor(kind, /*pos*/ 0, /*end*/ 0)), - }; - - var factory = createNodeFactory(NodeFactoryFlags.NoParenthesizerRules | NodeFactoryFlags.NoNodeConverters | NodeFactoryFlags.NoOriginalNode, baseNodeFactory); + var factory = createNodeFactory(NodeFactoryFlags.NoParenthesizerRules | NodeFactoryFlags.NoNodeConverters | NodeFactoryFlags.NoOriginalNode); var { createNodeArray: factoryCreateNodeArray, @@ -1722,12 +1679,6 @@ namespace Parser { } function initializeState(_fileName: string, _sourceText: string, _languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor | undefined, _scriptKind: ScriptKind, _jsDocParsingMode: JSDocParsingMode) { - NodeConstructor = objectAllocator.getNodeConstructor(); - TokenConstructor = objectAllocator.getTokenConstructor(); - IdentifierConstructor = objectAllocator.getIdentifierConstructor(); - PrivateIdentifierConstructor = objectAllocator.getPrivateIdentifierConstructor(); - SourceFileConstructor = objectAllocator.getSourceFileConstructor(); - fileName = normalizePath(_fileName); sourceText = _sourceText; languageVersion = _languageVersion; @@ -1980,10 +1931,17 @@ namespace Parser { setTextRangePosWidth(sourceFile, 0, sourceText.length); setFields(sourceFile); + // include the file in the count + nodeCount++; + // If we parsed this as an external module, it may contain top-level await if (!isDeclarationFile && isExternalModule(sourceFile) && sourceFile.transformFlags & TransformFlags.ContainsPossibleTopLevelAwait) { const oldSourceFile = sourceFile; - sourceFile = reparseTopLevelAwait(sourceFile); + const updated = reparseTopLevelAwait(sourceFile); + if (sourceFile !== updated) { + nodeCount++; + sourceFile = updated; + } if (oldSourceFile !== sourceFile) setFields(sourceFile); } @@ -2600,6 +2558,7 @@ namespace Parser { (node as Mutable).flags |= NodeFlags.ThisNodeHasError; } + nodeCount++; return node; } diff --git a/src/compiler/signatureObjectInternals.ts b/src/compiler/signatureObjectInternals.ts new file mode 100644 index 0000000000000..0437809e7ff1c --- /dev/null +++ b/src/compiler/signatureObjectInternals.ts @@ -0,0 +1,20 @@ +import { + JSDocTagInfo, + Signature, + SymbolDisplayPart, +} from "./types"; + +/** @internal */ +export class SignatureObjectInternals { + static internals = new SignatureObjectInternals(); + + getDocumentationComment(signature: Signature): SymbolDisplayPart[]; + getDocumentationComment(_signature: Signature): SymbolDisplayPart[] { + throw new TypeError("Not implemented."); + } + + getJsDocTags(signature: Signature): JSDocTagInfo[]; + getJsDocTags(_signature: Signature): JSDocTagInfo[] { + throw new TypeError("Not implemented."); + } +} diff --git a/src/compiler/symbolObjectInternals.ts b/src/compiler/symbolObjectInternals.ts new file mode 100644 index 0000000000000..c0167849b20b8 --- /dev/null +++ b/src/compiler/symbolObjectInternals.ts @@ -0,0 +1,32 @@ +import { SymbolObject } from "./objectConstructors"; +import { + JSDocTagInfo, + Node, + SymbolDisplayPart, + TypeChecker, +} from "./types"; + +/** @internal */ +export class SymbolObjectInternals { + static internals = new SymbolObjectInternals(); + + getDocumentationComment(symbol: SymbolObject, typeChecker: TypeChecker | undefined): SymbolDisplayPart[]; + getDocumentationComment(_symbol: SymbolObject, _typeChecker: TypeChecker | undefined): SymbolDisplayPart[] { + throw new TypeError("Not implemented."); + } + + getContextualDocumentationComment(symbol: SymbolObject, context: Node | undefined, checker: TypeChecker | undefined): SymbolDisplayPart[]; + getContextualDocumentationComment(_symbol: SymbolObject, _context: Node | undefined, _checker: TypeChecker | undefined): SymbolDisplayPart[] { + throw new TypeError("Not implemented."); + } + + getJsDocTags(symbol: SymbolObject, checker?: TypeChecker): JSDocTagInfo[]; + getJsDocTags(_symbol: SymbolObject, _checker?: TypeChecker): JSDocTagInfo[] { + throw new TypeError("Not implemented."); + } + + getContextualJsDocTags(symbol: SymbolObject, context: Node | undefined, checker: TypeChecker | undefined): JSDocTagInfo[]; + getContextualJsDocTags(_symbol: SymbolObject, _context: Node | undefined, _checker: TypeChecker | undefined): JSDocTagInfo[] { + throw new TypeError("Not implemented."); + } +} diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d97dc608adcbf..0bf15fb2dea1c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1,5 +1,4 @@ import { - BaseNodeFactory, CreateSourceFileOptions, EmitHelperFactory, GetCanonicalFileName, @@ -931,12 +930,37 @@ export interface Node extends ReadonlyTextRange { /** @internal */ readonly transformFlags: TransformFlags; // Flags for transforms /** @internal */ id?: NodeId; // Unique id (used to look up NodeLinks) readonly parent: Node; // Parent node (initialized by binding) - /** @internal */ original?: Node; // The original node if this is an updated node. - /** @internal */ emitNode?: EmitNode; // Associated EmitNode (initialized by transforms) + /** @internal */ original?: Node | undefined; // The original node if this is an updated node. + /** @internal */ emitNode?: EmitNode | undefined; // Associated EmitNode (initialized by transforms) // NOTE: `symbol` and `localSymbol` have been moved to `Declaration` // `locals` and `nextContainer` have been moved to `LocalsContainer` // `flowNode` has been moved to `FlowContainer` // see: https://github.com/microsoft/TypeScript/pull/51682 + + getSourceFile(): SourceFile; + getChildCount(sourceFile?: SourceFile): number; + getChildAt(index: number, sourceFile?: SourceFile): Node; + getChildren(sourceFile?: SourceFile): readonly Node[]; + /** @internal */ + getChildren(sourceFile?: SourceFileLike): readonly Node[]; // eslint-disable-line @typescript-eslint/unified-signatures + getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number; + /** @internal */ + getStart(sourceFile?: SourceFileLike, includeJsDocComment?: boolean): number; // eslint-disable-line @typescript-eslint/unified-signatures + getFullStart(): number; + getEnd(): number; + getWidth(sourceFile?: SourceFileLike): number; + getFullWidth(): number; + getLeadingTriviaWidth(sourceFile?: SourceFile): number; + getFullText(sourceFile?: SourceFile): string; + getText(sourceFile?: SourceFile): string; + getFirstToken(sourceFile?: SourceFile): Node | undefined; + /** @internal */ + getFirstToken(sourceFile?: SourceFileLike): Node | undefined; // eslint-disable-line @typescript-eslint/unified-signatures + getLastToken(sourceFile?: SourceFile): Node | undefined; + /** @internal */ + getLastToken(sourceFile?: SourceFileLike): Node | undefined; // eslint-disable-line @typescript-eslint/unified-signatures + // See ts.forEachChild for documentation. + forEachChild(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray) => T | undefined): T | undefined; } export interface JSDocContainer extends Node { @@ -1680,6 +1704,7 @@ export interface Identifier extends PrimaryExpression, Declaration, JSDocContain * Text of identifier, but if the identifier begins with two underscores, this will begin with three. */ readonly escapedText: __String; + readonly text: string; } // Transient identifier node (marked by id === -1) @@ -1773,6 +1798,7 @@ export interface PrivateIdentifier extends PrimaryExpression { // escaping not strictly necessary // avoids gotchas in transforms and utils readonly escapedText: __String; + readonly text: string; } /** @internal */ @@ -4215,6 +4241,7 @@ export interface SourceFileLike { lineMap?: readonly number[]; /** @internal */ getPositionOfLineAndCharacter?(line: number, character: number, allowEdits?: true): number; + getLineAndCharacterOfPosition(pos: number): LineAndCharacter; } /** @internal */ @@ -4386,6 +4413,42 @@ export interface SourceFile extends Declaration, LocalsContainer { /** @internal */ endFlowNode?: FlowNode; /** @internal */ jsDocParsingMode?: JSDocParsingMode; + + /** @internal */ scriptSnapshot: IScriptSnapshot | undefined; + /** @internal */ nameTable: Map<__String, number> | undefined; + /** @internal */ sourceMapper?: DocumentPositionMapper; + /** @internal */ getNamedDeclarations(): Map; + getLineAndCharacterOfPosition(pos: number): LineAndCharacter; + getLineEndOfPosition(pos: number): number; + getLineStarts(): readonly number[]; + getPositionOfLineAndCharacter(line: number, character: number): number; + update(newText: string, textChangeRange: TextChangeRange): SourceFile; +} + +/** + * Represents an immutable snapshot of a script at a specified time. Once acquired, the + * snapshot is observably immutable. i.e. the same calls with the same parameters will return + * the same values. + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export interface IScriptSnapshot { + /** Gets a portion of the script snapshot specified by [start, end). */ + getText(start: number, end: number): string; + + /** Gets the length of this script snapshot. */ + getLength(): number; + + /** + * Gets the TextChangeRange that describe how the text changed between this text and + * an older version. This information is used by the incremental parser to determine + * what sections of the script need to be re-parsed. 'undefined' can be returned if the + * change range cannot be determined. However, in that case, incremental parsing will + * not happen and the entire document will be re - parsed. + */ + getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange | undefined; + + /** Releases all resources held by this script snapshot */ + dispose?(): void; } /** @internal */ @@ -5846,6 +5909,45 @@ export const enum SymbolFlags { LateBindingContainer = Class | Interface | TypeLiteral | ObjectLiteral | Function, } +export interface SymbolDisplayPart { + /** + * Text of an item describing the symbol. + */ + text: string; + /** + * The symbol's kind (such as 'className' or 'parameterName' or plain 'text'). + */ + kind: string; +} + +export interface DocumentSpan { + textSpan: TextSpan; + fileName: string; + + /** + * If the span represents a location that was remapped (e.g. via a .d.ts.map file), + * then the original filename and span will be specified here + */ + originalTextSpan?: TextSpan; + originalFileName?: string; + + /** + * If DocumentSpan.textSpan is the span for name of the declaration, + * then this is the span for relevant declaration + */ + contextSpan?: TextSpan; + originalContextSpan?: TextSpan; +} + +export interface JSDocLinkDisplayPart extends SymbolDisplayPart { + target: DocumentSpan; +} + +export interface JSDocTagInfo { + name: string; + text?: SymbolDisplayPart[]; +} + /** @internal */ export type SymbolId = number; @@ -5867,6 +5969,18 @@ export interface Symbol { /** @internal */ lastAssignmentPos?: number; // Source position of last node that assigns value to symbol /** @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? /** @internal */ assignmentDeclarationMembers?: Map; // detected late-bound assignment declarations associated with the symbol + + readonly name: string; + getFlags(): SymbolFlags; + getEscapedName(): __String; + getName(): string; + getDeclarations(): Declaration[] | undefined; + getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[]; // implemented in services, not supported in tsc. + /** @internal */ + getContextualDocumentationComment(context: Node | undefined, checker: TypeChecker | undefined): SymbolDisplayPart[]; // implemented in services, not supported in tsc. + getJsDocTags(checker?: TypeChecker): JSDocTagInfo[]; // implemented in services, not supported in tsc. + /** @internal */ + getContextualJsDocTags(context: Node | undefined, checker: TypeChecker | undefined): JSDocTagInfo[]; // implemented in services, not supported in tsc. } // dprint-ignore @@ -6245,6 +6359,32 @@ export interface Type { immediateBaseConstraint?: Type; // Immediate base constraint cache /** @internal */ widened?: Type; // Cached widened form of the type + + getFlags(): TypeFlags; + getSymbol(): Symbol | undefined; + getProperties(): Symbol[]; + getProperty(propertyName: string): Symbol | undefined; + getApparentProperties(): Symbol[]; + getCallSignatures(): readonly Signature[]; + getConstructSignatures(): readonly Signature[]; + getStringIndexType(): Type | undefined; + getNumberIndexType(): Type | undefined; + getBaseTypes(): BaseType[] | undefined; + getNonNullableType(): Type; + /** @internal */ getNonOptionalType(): Type; + /** @internal */ isNullableType(): boolean; + getConstraint(): Type | undefined; + getDefault(): Type | undefined; + isUnion(): this is UnionType; + isIntersection(): this is IntersectionType; + isUnionOrIntersection(): this is UnionOrIntersectionType; + isLiteral(): this is LiteralType; + isStringLiteral(): this is StringLiteralType; + isNumberLiteral(): this is NumberLiteralType; + isTypeParameter(): this is TypeParameter; + isClassOrInterface(): this is InterfaceType; + isClass(): this is InterfaceType; + isIndexType(): this is IndexType; } /** @internal */ @@ -6446,6 +6586,8 @@ export interface TypeReference extends ObjectType { literalType?: TypeReference; // Clone of type with ObjectFlags.ArrayLiteral set /** @internal */ cachedEquivalentBaseType?: Type; // Only set on references to class or interfaces with a single base type and no augmentations + + readonly typeArguments?: readonly Type[]; } export interface DeferredTypeReference extends TypeReference { @@ -6851,6 +6993,14 @@ export interface Signature { instantiations?: Map; // Generic signature instantiation cache /** @internal */ implementationSignatureCache?: Signature; // Copy of the signature with fresh type parameters to use in checking the body of a potentially self-referential generic function (deferred) + + getDeclaration(): JSDocSignature | SignatureDeclaration; + getTypeParameters(): readonly TypeParameter[] | undefined; + getParameters(): readonly Symbol[]; + getTypeParameterAtPosition(pos: number): Type; + getReturnType(): Type; + getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[]; // implemented in services, not supported in tsc. + getJsDocTags(): JSDocTagInfo[]; // implemented in services, not supported in tsc. } export const enum IndexKind { @@ -8057,6 +8207,7 @@ export interface SourceMapSource { text: string; /** @internal */ lineMap: readonly number[]; skipTrivia?: (pos: number) => number; + getLineAndCharacterOfPosition(pos: number): LineAndCharacter; } /** @internal */ @@ -8447,7 +8598,6 @@ export type ImmediatelyInvokedArrowFunction = CallExpression & { readonly expres export interface NodeFactory { /** @internal */ readonly parenthesizer: ParenthesizerRules; /** @internal */ readonly converters: NodeConverters; - /** @internal */ readonly baseFactory: BaseNodeFactory; /** @internal */ readonly flags: NodeFactoryFlags; createNodeArray(elements?: readonly T[], hasTrailingComma?: boolean): NodeArray; @@ -9006,7 +9156,7 @@ export interface NodeFactory { // Synthetic Nodes // /** @internal */ createSyntheticExpression(type: Type, isSpread?: boolean, tupleNameSource?: ParameterDeclaration | NamedTupleMember): SyntheticExpression; - /** @internal */ createSyntaxList(children: Node[]): SyntaxList; + /** @internal */ createSyntaxList(children: readonly Node[]): SyntaxList; // // Transformation nodes diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4bbd3a80951b6..efbcf6c3175cc 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -497,9 +497,7 @@ import { SetAccessorDeclaration, ShorthandPropertyAssignment, shouldAllowImportingTsExtension, - Signature, SignatureDeclaration, - SignatureFlags, singleElementArray, singleOrUndefined, skipOuterExpressions, @@ -511,7 +509,6 @@ import { SourceFile, SourceFileLike, SourceFileMayBeEmittedHost, - SourceMapSource, startsWith, startsWithUseStrict, Statement, @@ -541,7 +538,6 @@ import { TokenFlags, tokenToString, toPath, - tracing, TransformFlags, TransientSymbol, TriviaSyntaxKind, @@ -5340,6 +5336,14 @@ export function isPushOrUnshiftIdentifier(node: Identifier) { return node.escapedText === "push" || node.escapedText === "unshift"; } +/** @internal */ +export function getNameFromPropertyName(name: PropertyName): string | undefined { + return name.kind === SyntaxKind.ComputedPropertyName + // treat computed property names where expression is string/numeric literal as just string/numeric literal + ? isStringOrNumericLiteralLike(name.expression) ? name.expression.text : undefined + : isPrivateIdentifier(name) ? idText(name) : getTextOfIdentifierOrLiteral(name); +} + // TODO(jakebailey): this function should not be named this. While it does technically // return true if the argument is a ParameterDeclaration, it also returns true for nodes // that are children of ParameterDeclarations inside binding elements. @@ -8194,131 +8198,8 @@ export function getLeftmostExpression(node: Expression, stopAtCallExpressions: b } /** @internal */ -export interface ObjectAllocator { - getNodeConstructor(): new (kind: SyntaxKind, pos: number, end: number) => Node; - getTokenConstructor(): new (kind: TKind, pos: number, end: number) => Token; - getIdentifierConstructor(): new (kind: SyntaxKind.Identifier, pos: number, end: number) => Identifier; - getPrivateIdentifierConstructor(): new (kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) => PrivateIdentifier; - getSourceFileConstructor(): new (kind: SyntaxKind.SourceFile, pos: number, end: number) => SourceFile; - getSymbolConstructor(): new (flags: SymbolFlags, name: __String) => Symbol; - getTypeConstructor(): new (checker: TypeChecker, flags: TypeFlags) => Type; - getSignatureConstructor(): new (checker: TypeChecker, flags: SignatureFlags) => Signature; - getSourceMapSourceConstructor(): new (fileName: string, text: string, skipTrivia?: (pos: number) => number) => SourceMapSource; -} - -function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { - // Note: if modifying this, be sure to update SymbolObject in src/services/services.ts - this.flags = flags; - this.escapedName = name; - this.declarations = undefined; - this.valueDeclaration = undefined; - this.id = 0; - this.mergeId = 0; - this.parent = undefined; - this.members = undefined; - this.exports = undefined; - this.exportSymbol = undefined; - this.constEnumOnlyModule = undefined; - this.isReferenced = undefined; - this.lastAssignmentPos = undefined; - (this as any).links = undefined; // used by TransientSymbol -} - -function Type(this: Type, checker: TypeChecker, flags: TypeFlags) { - // Note: if modifying this, be sure to update TypeObject in src/services/services.ts - this.flags = flags; - if (Debug.isDebugging || tracing) { - this.checker = checker; - } -} - -function Signature(this: Signature, checker: TypeChecker, flags: SignatureFlags) { - // Note: if modifying this, be sure to update SignatureObject in src/services/services.ts - this.flags = flags; - if (Debug.isDebugging) { - this.checker = checker; - } -} - -function Node(this: Mutable, kind: SyntaxKind, pos: number, end: number) { - // Note: if modifying this, be sure to update NodeObject in src/services/services.ts - this.pos = pos; - this.end = end; - this.kind = kind; - this.id = 0; - this.flags = NodeFlags.None; - this.modifierFlagsCache = ModifierFlags.None; - this.transformFlags = TransformFlags.None; - this.parent = undefined!; - this.original = undefined; - this.emitNode = undefined; -} - -function Token(this: Mutable, kind: SyntaxKind, pos: number, end: number) { - // Note: if modifying this, be sure to update TokenOrIdentifierObject in src/services/services.ts - this.pos = pos; - this.end = end; - this.kind = kind; - this.id = 0; - this.flags = NodeFlags.None; - this.transformFlags = TransformFlags.None; - this.parent = undefined!; - this.emitNode = undefined; -} - -function Identifier(this: Mutable, kind: SyntaxKind, pos: number, end: number) { - // Note: if modifying this, be sure to update TokenOrIdentifierObject in src/services/services.ts - this.pos = pos; - this.end = end; - this.kind = kind; - this.id = 0; - this.flags = NodeFlags.None; - this.transformFlags = TransformFlags.None; - this.parent = undefined!; - this.original = undefined; - this.emitNode = undefined; -} - -function SourceMapSource(this: SourceMapSource, fileName: string, text: string, skipTrivia?: (pos: number) => number) { - // Note: if modifying this, be sure to update SourceMapSourceObject in src/services/services.ts - this.fileName = fileName; - this.text = text; - this.skipTrivia = skipTrivia || (pos => pos); -} - -/** @internal */ -export const objectAllocator: ObjectAllocator = { - getNodeConstructor: () => Node as any, - getTokenConstructor: () => Token as any, - getIdentifierConstructor: () => Identifier as any, - getPrivateIdentifierConstructor: () => Node as any, - getSourceFileConstructor: () => Node as any, - getSymbolConstructor: () => Symbol as any, - getTypeConstructor: () => Type as any, - getSignatureConstructor: () => Signature as any, - getSourceMapSourceConstructor: () => SourceMapSource as any, -}; - -const objectAllocatorPatchers: ((objectAllocator: ObjectAllocator) => void)[] = []; - -/** - * Used by `deprecatedCompat` to patch the object allocator to apply deprecations. - * @internal - */ -export function addObjectAllocatorPatcher(fn: (objectAllocator: ObjectAllocator) => void) { - objectAllocatorPatchers.push(fn); - fn(objectAllocator); -} - -/** @internal */ -export function setObjectAllocator(alloc: ObjectAllocator) { - Object.assign(objectAllocator, alloc); - forEach(objectAllocatorPatchers, fn => fn(objectAllocator)); -} - -/** @internal */ -export function formatStringFromArgs(text: string, args: DiagnosticArguments): string { - return text.replace(/{(\d+)}/g, (_match, index: string) => "" + Debug.checkDefined(args[+index])); +export function formatStringFromArgs(text: string, args: ArrayLike, baseIndex = 0): string { + return text.replace(/{(\d+)}/g, (_match, index: string) => "" + Debug.checkDefined(args[+index + baseIndex])); } let localizedDiagnosticMessages: MapLike | undefined; diff --git a/src/services/services.ts b/src/services/services.ts index 18f0b4d151439..40c5788485b6c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1,10 +1,12 @@ +import { + SymbolObject, +} from "../compiler/objectConstructors"; +import { SignatureObjectInternals } from "../compiler/signatureObjectInternals"; +import { SymbolObjectInternals } from "../compiler/symbolObjectInternals"; import { __String, ApplicableRefactorInfo, ApplyCodeActionCommandResult, - AssignmentDeclarationKind, - BaseType, - BinaryExpression, BreakpointResolver, CallHierarchy, CallHierarchyIncomingCall, @@ -13,7 +15,6 @@ import { CancellationToken, changeCompilerHostLikeToUseCache, CharacterCodes, - CheckJsDirective, Classifications, ClassifiedSpan, ClassifiedSpan2020, @@ -30,12 +31,10 @@ import { CompletionEntryDetails, CompletionInfo, Completions, - computePositionOfLineAndCharacter, computeSuggestionDiagnostics, containsParseError, createDocumentRegistry, createGetCanonicalFileName, - createMultiMap, createProgram, CreateProgramOptions, createSourceFile, @@ -58,20 +57,14 @@ import { EditorOptions, EditorSettings, ElementAccessExpression, - EmitNode, EmitTextWriter, emptyArray, emptyOptions, - EndOfFileToken, - EntityName, equateValues, - ExportDeclaration, Extension, extensionFromPath, - FileReference, FileTextChanges, filter, - find, FindAllReferences, findAncestor, findChildOfKind, @@ -85,10 +78,8 @@ import { FormatCodeOptions, FormatCodeSettings, formatting, - FunctionLikeDeclaration, getAdjustedRenameLocation, getAllSuperTypeNodes, - getAssignmentDeclarationKind, getBaseFileName, GetCompletionsAtPositionOptions, getContainerNode, @@ -100,23 +91,16 @@ import { getFileEmitOutput, getImpliedNodeFormatForFile, getJSDocTags, - getLineAndCharacterOfPosition, - getLineStarts, getMappedDocumentSpan, getNameFromPropertyName, getNewLineCharacter, getNewLineOrDefaultFromHost, - getNodeChildren, - getNonAssignedNameOfDeclaration, getNormalizedAbsolutePath, - getObjectFlags, getQuotePreference, getScriptKind, getSetExternalModuleIndicator, getSnapshotText, - getSourceFileOfNode, getSourceMapper, - getTokenPosOfNode, getTouchingPropertyName, getTouchingToken, GoToDefinition, @@ -124,28 +108,17 @@ import { hasJSDocNodes, hasProperty, hasStaticModifier, - hasSyntacticModifier, - hasTabstop, HostCancellationToken, hostGetCanonicalFileName, hostUsesCaseSensitiveFileNames, - Identifier, identity, - idText, ImplementationLocation, - ImportDeclaration, - IndexKind, - IndexType, InlayHint, InlayHints, InlayHintsContext, insertSorted, InteractiveRefactorArguments, - InterfaceType, - IntersectionType, isArray, - isBindingPattern, - isComputedPropertyName, isConstTypeReference, IScriptSnapshot, isDeclarationName, @@ -159,7 +132,6 @@ import { isInString, isInTemplateString, isIntrinsicJsxName, - isJSDocCommentContainingNode, isJsxAttributes, isJsxClosingElement, isJsxElement, @@ -170,28 +142,21 @@ import { isJsxText, isLabelName, isLiteralComputedPropertyDeclarationName, - isNamedExports, isNamedTupleMember, isNameOfModuleDeclaration, isNewExpression, - isNodeKind, isObjectLiteralElement, isObjectLiteralExpression, isPrivateIdentifier, isProgramUptoDate, - isPropertyAccessExpression, - isPropertyName, isRightSideOfPropertyAccess, isRightSideOfQualifiedName, isSetAccessor, isStringOrNumericLiteralLike, isTagName, isTextWhiteSpaceLike, - isThisTypeParameter, isTransientSymbol, - JSDoc, JsDoc, - JSDocContainer, JSDocParsingMode, JSDocTagInfo, JsonSourceFile, @@ -203,33 +168,25 @@ import { LanguageService, LanguageServiceHost, LanguageServiceMode, - LanguageVariant, - lastOrUndefined, length, LineAndCharacter, lineBreakPart, LinkedEditingInfo, - LiteralType, map, mapDefined, MapLike, mapOneOrMany, maybeBind, maybeSetLocalizedDiagnosticMessages, - ModifierFlags, ModuleDeclaration, NavigateToItem, NavigationBarItem, NavigationTree, Node, - NodeArray, NodeFlags, noop, normalizePath, - NumberLiteralType, NumericLiteral, - ObjectAllocator, - ObjectFlags, ObjectLiteralElement, ObjectLiteralExpression, OperationCanceledException, @@ -242,10 +199,7 @@ import { ParsedCommandLine, parseJsonSourceFileConfigFileContent, Path, - positionIsSynthesized, PossibleProgramFileInfo, - PragmaMap, - PrivateIdentifier, Program, PropertyName, PropertySignature, @@ -262,42 +216,26 @@ import { RenameLocation, ResolvedProjectReference, returnFalse, - scanner, ScriptElementKind, ScriptElementKindModifier, ScriptKind, ScriptTarget, SelectionRange, SemanticClassificationFormat, - setNodeChildren, - setObjectAllocator, Signature, - SignatureDeclaration, - SignatureFlags, SignatureHelp, SignatureHelpItems, SignatureHelpItemsOptions, - SignatureKind, singleElementArray, SmartSelectionRange, SortedArray, SourceFile, - SourceFileLike, - SourceMapSource, startsWith, - Statement, - StringLiteral, StringLiteralLike, - StringLiteralType, Symbol, SymbolDisplay, SymbolDisplayPart, - SymbolFlags, - SymbolLinks, - symbolName, - SymbolTable, SyntaxKind, - SyntaxList, sys, tagNamesAreEquivalent, TextChange, @@ -309,23 +247,13 @@ import { timestamp, TodoComment, TodoCommentDescriptor, - Token, toPath, tracing, - TransformFlags, Type, TypeChecker, - TypeFlags, - TypeNode, - TypeParameter, - TypePredicate, - TypeReference, typeToDisplayParts, - UnionOrIntersectionType, - UnionType, updateSourceFile, UserPreferences, - VariableDeclaration, } from "./_namespaces/ts"; import * as NavigateTo from "./_namespaces/ts.NavigateTo"; import * as NavigationBar from "./_namespaces/ts.NavigationBar"; @@ -340,318 +268,8 @@ import * as classifier2020 from "./classifier2020"; /** The version of the language service API */ export const servicesVersion = "0.8"; -function createNode(kind: TKind, pos: number, end: number, parent: Node): NodeObject | TokenObject | IdentifierObject | PrivateIdentifierObject { - const node = isNodeKind(kind) ? new NodeObject(kind, pos, end) : - kind === SyntaxKind.Identifier ? new IdentifierObject(SyntaxKind.Identifier, pos, end) : - kind === SyntaxKind.PrivateIdentifier ? new PrivateIdentifierObject(SyntaxKind.PrivateIdentifier, pos, end) : - new TokenObject(kind, pos, end); - node.parent = parent; - node.flags = parent.flags & NodeFlags.ContextFlags; - return node; -} - -class NodeObject implements Node { - public kind: TKind; - public pos: number; - public end: number; - public flags: NodeFlags; - public modifierFlagsCache: ModifierFlags; - public transformFlags: TransformFlags; - public parent: Node; - public symbol!: Symbol; // Actually optional, but it was too annoying to access `node.symbol!` everywhere since in many cases we know it must be defined - public jsDoc?: JSDoc[]; - public original?: Node; - public id?: number; - public emitNode?: EmitNode; - - constructor(kind: TKind, pos: number, end: number) { - // Note: if modifying this, be sure to update Node in src/compiler/utilities.ts - this.pos = pos; - this.end = end; - this.kind = kind; - this.id = 0; - this.flags = NodeFlags.None; - this.modifierFlagsCache = ModifierFlags.None; - this.transformFlags = TransformFlags.None; - this.parent = undefined!; - this.original = undefined; - this.emitNode = undefined; - } - - private assertHasRealPosition(message?: string) { - // eslint-disable-next-line local/debug-assert - Debug.assert(!positionIsSynthesized(this.pos) && !positionIsSynthesized(this.end), message || "Node must have a real position for this operation"); - } - - public getSourceFile(): SourceFile { - return getSourceFileOfNode(this); - } - - public getStart(sourceFile?: SourceFileLike, includeJsDocComment?: boolean): number { - this.assertHasRealPosition(); - return getTokenPosOfNode(this, sourceFile, includeJsDocComment); - } - - public getFullStart(): number { - this.assertHasRealPosition(); - return this.pos; - } - - public getEnd(): number { - this.assertHasRealPosition(); - return this.end; - } - - public getWidth(sourceFile?: SourceFile): number { - this.assertHasRealPosition(); - return this.getEnd() - this.getStart(sourceFile); - } - - public getFullWidth(): number { - this.assertHasRealPosition(); - return this.end - this.pos; - } - - public getLeadingTriviaWidth(sourceFile?: SourceFile): number { - this.assertHasRealPosition(); - return this.getStart(sourceFile) - this.pos; - } - - public getFullText(sourceFile?: SourceFile): string { - this.assertHasRealPosition(); - return (sourceFile || this.getSourceFile()).text.substring(this.pos, this.end); - } - - public getText(sourceFile?: SourceFile): string { - this.assertHasRealPosition(); - if (!sourceFile) { - sourceFile = this.getSourceFile(); - } - return sourceFile.text.substring(this.getStart(sourceFile), this.getEnd()); - } - - public getChildCount(sourceFile?: SourceFile): number { - return this.getChildren(sourceFile).length; - } - - public getChildAt(index: number, sourceFile?: SourceFile): Node { - return this.getChildren(sourceFile)[index]; - } - - public getChildren(sourceFile?: SourceFileLike): Node[] { - this.assertHasRealPosition("Node without a real position cannot be scanned and thus has no token nodes - use forEachChild and collect the result if that's fine"); - return getNodeChildren(this) ?? setNodeChildren(this, createChildren(this, sourceFile)); - } - - public getFirstToken(sourceFile?: SourceFileLike): Node | undefined { - this.assertHasRealPosition(); - const children = this.getChildren(sourceFile); - if (!children.length) { - return undefined; - } - - const child = find(children, kid => kid.kind < SyntaxKind.FirstJSDocNode || kid.kind > SyntaxKind.LastJSDocNode)!; - return child.kind < SyntaxKind.FirstNode ? - child : - child.getFirstToken(sourceFile); - } - - public getLastToken(sourceFile?: SourceFileLike): Node | undefined { - this.assertHasRealPosition(); - const children = this.getChildren(sourceFile); - - const child = lastOrUndefined(children); - if (!child) { - return undefined; - } - - return child.kind < SyntaxKind.FirstNode ? child : child.getLastToken(sourceFile); - } - - public forEachChild(cbNode: (node: Node) => T, cbNodeArray?: (nodes: NodeArray) => T): T | undefined { - return forEachChild(this, cbNode, cbNodeArray); - } -} - -function createChildren(node: Node, sourceFile: SourceFileLike | undefined): Node[] { - if (!isNodeKind(node.kind)) { - return emptyArray; - } - - const children: Node[] = []; - - if (isJSDocCommentContainingNode(node)) { - /** Don't add trivia for "tokens" since this is in a comment. */ - node.forEachChild(child => { - children.push(child); - }); - return children; - } - - scanner.setText((sourceFile || node.getSourceFile()).text); - let pos = node.pos; - const processNode = (child: Node) => { - addSyntheticNodes(children, pos, child.pos, node); - children.push(child); - pos = child.end; - }; - const processNodes = (nodes: NodeArray) => { - addSyntheticNodes(children, pos, nodes.pos, node); - children.push(createSyntaxList(nodes, node)); - pos = nodes.end; - }; - // jsDocComments need to be the first children - forEach((node as JSDocContainer).jsDoc, processNode); - // For syntactic classifications, all trivia are classified together, including jsdoc comments. - // For that to work, the jsdoc comments should still be the leading trivia of the first child. - // Restoring the scanner position ensures that. - pos = node.pos; - node.forEachChild(processNode, processNodes); - addSyntheticNodes(children, pos, node.end, node); - scanner.setText(undefined); - return children; -} - -function addSyntheticNodes(nodes: Node[], pos: number, end: number, parent: Node): void { - scanner.resetTokenState(pos); - while (pos < end) { - const token = scanner.scan(); - const textPos = scanner.getTokenEnd(); - if (textPos <= end) { - if (token === SyntaxKind.Identifier) { - if (hasTabstop(parent)) { - continue; - } - Debug.fail(`Did not expect ${Debug.formatSyntaxKind(parent.kind)} to have an Identifier in its trivia`); - } - nodes.push(createNode(token, pos, textPos, parent)); - } - pos = textPos; - if (token === SyntaxKind.EndOfFileToken) { - break; - } - } -} - -function createSyntaxList(nodes: NodeArray, parent: Node): Node { - const list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, parent) as any as SyntaxList; - const children: Node[] = []; - let pos = nodes.pos; - for (const node of nodes) { - addSyntheticNodes(children, pos, node.pos, parent); - children.push(node); - pos = node.end; - } - addSyntheticNodes(children, pos, nodes.end, parent); - setNodeChildren(list, children); - return list; -} - -class TokenOrIdentifierObject implements Node { - public kind: TKind; - public pos: number; - public end: number; - public flags: NodeFlags; - public modifierFlagsCache!: ModifierFlags; - public transformFlags: TransformFlags; - public parent: Node; - public symbol!: Symbol; - public jsDocComments?: JSDoc[]; - public id?: number; - public emitNode?: EmitNode | undefined; - - constructor(kind: TKind, pos: number, end: number) { - // Note: if modifying this, be sure to update Token and Identifier in src/compiler/utilities.ts - this.pos = pos; - this.end = end; - this.kind = kind; - this.id = 0; - this.flags = NodeFlags.None; - this.transformFlags = TransformFlags.None; - this.parent = undefined!; - this.emitNode = undefined; - } - - public getSourceFile(): SourceFile { - return getSourceFileOfNode(this); - } - - public getStart(sourceFile?: SourceFileLike, includeJsDocComment?: boolean): number { - return getTokenPosOfNode(this, sourceFile, includeJsDocComment); - } - - public getFullStart(): number { - return this.pos; - } - - public getEnd(): number { - return this.end; - } - - public getWidth(sourceFile?: SourceFile): number { - return this.getEnd() - this.getStart(sourceFile); - } - - public getFullWidth(): number { - return this.end - this.pos; - } - - public getLeadingTriviaWidth(sourceFile?: SourceFile): number { - return this.getStart(sourceFile) - this.pos; - } - - public getFullText(sourceFile?: SourceFile): string { - return (sourceFile || this.getSourceFile()).text.substring(this.pos, this.end); - } - - public getText(sourceFile?: SourceFile): string { - if (!sourceFile) { - sourceFile = this.getSourceFile(); - } - return sourceFile.text.substring(this.getStart(sourceFile), this.getEnd()); - } - - public getChildCount(): number { - return this.getChildren().length; - } - - public getChildAt(index: number): Node { - return this.getChildren()[index]; - } - - public getChildren(): Node[] { - return this.kind === SyntaxKind.EndOfFileToken ? (this as Node as EndOfFileToken).jsDoc || emptyArray : emptyArray; - } - - public getFirstToken(): Node | undefined { - return undefined; - } - - public getLastToken(): Node | undefined { - return undefined; - } - - public forEachChild(): T | undefined { - return undefined; - } -} - -class SymbolObject implements Symbol { - flags: SymbolFlags; - escapedName: __String; - declarations?: Declaration[]; - valueDeclaration?: Declaration; - members?: SymbolTable; - exports?: SymbolTable; - id: number; - mergeId: number; - parent?: Symbol; - exportSymbol?: Symbol; - constEnumOnlyModule: boolean | undefined; - isReferenced?: SymbolFlags; - lastAssignmentPos?: number; - links?: SymbolLinks; - +// Extra fields added by services are stored in a WeakMap +interface SymbolExtraFields { // Undefined is used to indicate the value has not been computed. If, after computing, the // symbol has no doc comment, then the empty array will be returned. documentationComment?: SymbolDisplayPart[]; @@ -662,311 +280,114 @@ class SymbolObject implements Symbol { contextualGetAccessorTags?: JSDocTagInfo[]; contextualSetAccessorTags?: JSDocTagInfo[]; +} - constructor(flags: SymbolFlags, name: __String) { - // Note: if modifying this, be sure to update Symbol in src/compiler/types.ts - this.flags = flags; - this.escapedName = name; - this.declarations = undefined; - this.valueDeclaration = undefined; - this.id = 0; - this.mergeId = 0; - this.parent = undefined; - this.members = undefined; - this.exports = undefined; - this.exportSymbol = undefined; - this.constEnumOnlyModule = undefined; - this.isReferenced = undefined; - this.lastAssignmentPos = undefined; - this.links = undefined; // used by TransientSymbol - } - - getFlags(): SymbolFlags { - return this.flags; - } - - get name(): string { - return symbolName(this); - } - - getEscapedName(): __String { - return this.escapedName; - } - - getName(): string { - return this.name; - } +const symbolExtraFields = new WeakMap(); - getDeclarations(): Declaration[] | undefined { - return this.declarations; - } +function ensureSymbolExtraFields(symbol: Symbol) { + let extra = symbolExtraFields.get(symbol); + if (!extra) symbolExtraFields.set(symbol, extra = {}); + return extra; +} - getDocumentationComment(checker: TypeChecker | undefined): SymbolDisplayPart[] { - if (!this.documentationComment) { - this.documentationComment = emptyArray; // Set temporarily to avoid an infinite loop finding inherited docs +class ServicesSymbolObjectInternals extends SymbolObjectInternals { + override getDocumentationComment(symbol: SymbolObject, checker: TypeChecker | undefined): SymbolDisplayPart[] { + const extra = ensureSymbolExtraFields(symbol); + if (!extra.documentationComment) { + extra.documentationComment = emptyArray; // Set temporarily to avoid an infinite loop finding inherited docs - if (!this.declarations && isTransientSymbol(this) && this.links.target && isTransientSymbol(this.links.target) && this.links.target.links.tupleLabelDeclaration) { - const labelDecl = this.links.target.links.tupleLabelDeclaration; - this.documentationComment = getDocumentationComment([labelDecl], checker); + if (!symbol.declarations && isTransientSymbol(symbol) && symbol.links.target && isTransientSymbol(symbol.links.target) && symbol.links.target.links.tupleLabelDeclaration) { + const labelDecl = symbol.links.target.links.tupleLabelDeclaration; + extra.documentationComment = getDocumentationComment([labelDecl], checker); } else { - this.documentationComment = getDocumentationComment(this.declarations, checker); + extra.documentationComment = getDocumentationComment(symbol.declarations, checker); } } - return this.documentationComment; + return extra.documentationComment; } - getContextualDocumentationComment(context: Node | undefined, checker: TypeChecker | undefined): SymbolDisplayPart[] { + override getContextualDocumentationComment(symbol: SymbolObject, context: Node | undefined, checker: TypeChecker | undefined): SymbolDisplayPart[] { if (context) { + const extra = ensureSymbolExtraFields(symbol); if (isGetAccessor(context)) { - if (!this.contextualGetAccessorDocumentationComment) { - this.contextualGetAccessorDocumentationComment = getDocumentationComment(filter(this.declarations, isGetAccessor), checker); - } - if (length(this.contextualGetAccessorDocumentationComment)) { - return this.contextualGetAccessorDocumentationComment; + extra.contextualGetAccessorDocumentationComment ??= getDocumentationComment(filter(symbol.declarations, isGetAccessor), checker); + if (length(extra.contextualGetAccessorDocumentationComment)) { + return extra.contextualGetAccessorDocumentationComment; } } - if (isSetAccessor(context)) { - if (!this.contextualSetAccessorDocumentationComment) { - this.contextualSetAccessorDocumentationComment = getDocumentationComment(filter(this.declarations, isSetAccessor), checker); - } - if (length(this.contextualSetAccessorDocumentationComment)) { - return this.contextualSetAccessorDocumentationComment; + else if (isSetAccessor(context)) { + extra.contextualSetAccessorDocumentationComment ??= getDocumentationComment(filter(symbol.declarations, isSetAccessor), checker); + if (length(extra.contextualSetAccessorDocumentationComment)) { + return extra.contextualSetAccessorDocumentationComment; } } } - return this.getDocumentationComment(checker); + return this.getDocumentationComment(symbol, checker); } - getJsDocTags(checker?: TypeChecker): JSDocTagInfo[] { - if (this.tags === undefined) { - this.tags = emptyArray; // Set temporarily to avoid an infinite loop finding inherited tags - this.tags = getJsDocTagsOfDeclarations(this.declarations, checker); + override getJsDocTags(symbol: SymbolObject, checker?: TypeChecker): JSDocTagInfo[] { + const extra = ensureSymbolExtraFields(symbol); + if (!extra.tags) { + extra.tags = emptyArray; // Set temporarily to avoid an infinite loop finding inherited tags + extra.tags = getJsDocTagsOfDeclarations(symbol.declarations, checker); } - - return this.tags; + return extra.tags; } - getContextualJsDocTags(context: Node | undefined, checker: TypeChecker | undefined): JSDocTagInfo[] { + override getContextualJsDocTags(symbol: SymbolObject, context: Node | undefined, checker: TypeChecker | undefined): JSDocTagInfo[] { if (context) { + const extra = ensureSymbolExtraFields(symbol); if (isGetAccessor(context)) { - if (!this.contextualGetAccessorTags) { - this.contextualGetAccessorTags = getJsDocTagsOfDeclarations(filter(this.declarations, isGetAccessor), checker); - } - if (length(this.contextualGetAccessorTags)) { - return this.contextualGetAccessorTags; + extra.contextualGetAccessorTags ??= getJsDocTagsOfDeclarations(filter(symbol.declarations, isGetAccessor), checker); + if (length(extra.contextualGetAccessorTags)) { + return extra.contextualGetAccessorTags; } } - if (isSetAccessor(context)) { - if (!this.contextualSetAccessorTags) { - this.contextualSetAccessorTags = getJsDocTagsOfDeclarations(filter(this.declarations, isSetAccessor), checker); - } - if (length(this.contextualSetAccessorTags)) { - return this.contextualSetAccessorTags; + else if (isSetAccessor(context)) { + extra.contextualSetAccessorTags ??= getJsDocTagsOfDeclarations(filter(symbol.declarations, isSetAccessor), checker); + if (length(extra.contextualSetAccessorTags)) { + return extra.contextualSetAccessorTags; } } } - return this.getJsDocTags(checker); - } -} - -class TokenObject extends TokenOrIdentifierObject implements Token { - constructor(kind: TKind, pos: number, end: number) { - super(kind, pos, end); - } -} - -class IdentifierObject extends TokenOrIdentifierObject implements Identifier { - public escapedText!: __String; - declare _primaryExpressionBrand: any; - declare _memberExpressionBrand: any; - declare _leftHandSideExpressionBrand: any; - declare _updateExpressionBrand: any; - declare _unaryExpressionBrand: any; - declare _expressionBrand: any; - declare _declarationBrand: any; - declare _jsdocContainerBrand: any; - declare _flowContainerBrand: any; - typeArguments!: NodeArray; - constructor(kind: SyntaxKind.Identifier, pos: number, end: number) { - super(kind, pos, end); - } - - get text(): string { - return idText(this); - } -} - -class PrivateIdentifierObject extends TokenOrIdentifierObject implements PrivateIdentifier { - public escapedText!: __String; - declare _primaryExpressionBrand: any; - declare _memberExpressionBrand: any; - declare _leftHandSideExpressionBrand: any; - declare _updateExpressionBrand: any; - declare _unaryExpressionBrand: any; - declare _expressionBrand: any; - constructor(kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) { - super(kind, pos, end); - } - - get text(): string { - return idText(this); - } -} - -class TypeObject implements Type { - checker: TypeChecker; - flags: TypeFlags; - objectFlags?: ObjectFlags; - id!: number; - symbol!: Symbol; - constructor(checker: TypeChecker, flags: TypeFlags) { - // Note: if modifying this, be sure to update Type in src/compiler/types.ts - this.flags = flags; - this.checker = checker; - } - getFlags(): TypeFlags { - return this.flags; - } - getSymbol(): Symbol | undefined { - return this.symbol; - } - getProperties(): Symbol[] { - return this.checker.getPropertiesOfType(this); - } - getProperty(propertyName: string): Symbol | undefined { - return this.checker.getPropertyOfType(this, propertyName); - } - getApparentProperties(): Symbol[] { - return this.checker.getAugmentedPropertiesOfType(this); - } - getCallSignatures(): readonly Signature[] { - return this.checker.getSignaturesOfType(this, SignatureKind.Call); - } - getConstructSignatures(): readonly Signature[] { - return this.checker.getSignaturesOfType(this, SignatureKind.Construct); - } - getStringIndexType(): Type | undefined { - return this.checker.getIndexTypeOfType(this, IndexKind.String); - } - getNumberIndexType(): Type | undefined { - return this.checker.getIndexTypeOfType(this, IndexKind.Number); - } - getBaseTypes(): BaseType[] | undefined { - return this.isClassOrInterface() ? this.checker.getBaseTypes(this) : undefined; - } - isNullableType(): boolean { - return this.checker.isNullableType(this); - } - getNonNullableType(): Type { - return this.checker.getNonNullableType(this); - } - getNonOptionalType(): Type { - return this.checker.getNonOptionalType(this); - } - getConstraint(): Type | undefined { - return this.checker.getBaseConstraintOfType(this); - } - getDefault(): Type | undefined { - return this.checker.getDefaultFromTypeParameter(this); - } - - isUnion(): this is UnionType { - return !!(this.flags & TypeFlags.Union); - } - isIntersection(): this is IntersectionType { - return !!(this.flags & TypeFlags.Intersection); - } - isUnionOrIntersection(): this is UnionOrIntersectionType { - return !!(this.flags & TypeFlags.UnionOrIntersection); - } - isLiteral(): this is LiteralType { - return !!(this.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.BigIntLiteral)); - } - isStringLiteral(): this is StringLiteralType { - return !!(this.flags & TypeFlags.StringLiteral); - } - isNumberLiteral(): this is NumberLiteralType { - return !!(this.flags & TypeFlags.NumberLiteral); - } - isTypeParameter(): this is TypeParameter { - return !!(this.flags & TypeFlags.TypeParameter); - } - isClassOrInterface(): this is InterfaceType { - return !!(getObjectFlags(this) & ObjectFlags.ClassOrInterface); - } - isClass(): this is InterfaceType { - return !!(getObjectFlags(this) & ObjectFlags.Class); - } - isIndexType(): this is IndexType { - return !!(this.flags & TypeFlags.Index); - } - /** - * This polyfills `referenceType.typeArguments` for API consumers - */ - get typeArguments() { - if (getObjectFlags(this) & ObjectFlags.Reference) { - return this.checker.getTypeArguments(this as Type as TypeReference); - } - return undefined; + return this.getJsDocTags(symbol, checker); } } -class SignatureObject implements Signature { - flags: SignatureFlags; - checker: TypeChecker; - declaration!: SignatureDeclaration; - typeParameters?: TypeParameter[]; - parameters!: Symbol[]; - thisParameter!: Symbol; - resolvedReturnType!: Type; - resolvedTypePredicate: TypePredicate | undefined; - minTypeArgumentCount!: number; - minArgumentCount!: number; +// Override the internals for symbols +SymbolObjectInternals.internals = new ServicesSymbolObjectInternals(); +interface SignatureExtraFields { // Undefined is used to indicate the value has not been computed. If, after computing, the // symbol has no doc comment, then the empty array will be returned. documentationComment?: SymbolDisplayPart[]; jsDocTags?: JSDocTagInfo[]; // same +} - constructor(checker: TypeChecker, flags: SignatureFlags) { - // Note: if modifying this, be sure to update Signature in src/compiler/types.ts - this.flags = flags; - this.checker = checker; - } +const signatureExtraFields = new WeakMap(); - getDeclaration(): SignatureDeclaration { - return this.declaration; - } - getTypeParameters(): TypeParameter[] | undefined { - return this.typeParameters; - } - getParameters(): Symbol[] { - return this.parameters; - } - getReturnType(): Type { - return this.checker.getReturnTypeOfSignature(this); - } - getTypeParameterAtPosition(pos: number): Type { - const type = this.checker.getParameterType(this, pos); - if (type.isIndexType() && isThisTypeParameter(type.type)) { - const constraint = type.type.getConstraint(); - if (constraint) { - return this.checker.getIndexType(constraint); - } - } - return type; - } +function ensureSignatureExtraFields(signature: Signature) { + let extra = signatureExtraFields.get(signature); + if (!extra) signatureExtraFields.set(signature, extra = {}); + return extra; +} - getDocumentationComment(): SymbolDisplayPart[] { - return this.documentationComment || (this.documentationComment = getDocumentationComment(singleElementArray(this.declaration), this.checker)); +class ServicesSignatureObjectInternals extends SignatureObjectInternals { + override getDocumentationComment(signature: Signature): SymbolDisplayPart[] { + const extra = ensureSignatureExtraFields(signature); + return extra.documentationComment ??= getDocumentationComment(singleElementArray(signature.declaration), signature.checker); } - getJsDocTags(): JSDocTagInfo[] { - return this.jsDocTags || (this.jsDocTags = getJsDocTagsOfDeclarations(singleElementArray(this.declaration), this.checker)); + override getJsDocTags(signature: Signature): JSDocTagInfo[] { + const extra = ensureSignatureExtraFields(signature); + return extra.jsDocTags ??= getJsDocTagsOfDeclarations(singleElementArray(signature.declaration), signature.checker); } } +// Override the internals for signatures +SignatureObjectInternals.internals = new ServicesSignatureObjectInternals(); + /** * Returns whether or not the given node has a JSDoc "inheritDoc" tag on it. * @param node the Node in question. @@ -1036,283 +457,6 @@ function findBaseOfDeclaration(checker: TypeChecker, declaration: Declaration }); } -class SourceFileObject extends NodeObject implements SourceFile { - declare _declarationBrand: any; - declare _localsContainerBrand: any; - public fileName!: string; - public path!: Path; - public resolvedPath!: Path; - public originalFileName!: string; - public text!: string; - public scriptSnapshot!: IScriptSnapshot; - public lineMap!: readonly number[]; - - public statements!: NodeArray; - public endOfFileToken!: Token; - - public amdDependencies!: { name: string; path: string; }[]; - public moduleName!: string; - public referencedFiles!: FileReference[]; - public typeReferenceDirectives!: FileReference[]; - public libReferenceDirectives!: FileReference[]; - - public syntacticDiagnostics!: DiagnosticWithLocation[]; - public parseDiagnostics!: DiagnosticWithLocation[]; - public bindDiagnostics!: DiagnosticWithLocation[]; - public bindSuggestionDiagnostics?: DiagnosticWithLocation[]; - - public isDeclarationFile!: boolean; - public isDefaultLib!: boolean; - public hasNoDefaultLib!: boolean; - public externalModuleIndicator!: Node; // The first node that causes this file to be an external module - public commonJsModuleIndicator!: Node; // The first node that causes this file to be a CommonJS module - public nodeCount!: number; - public identifierCount!: number; - public symbolCount!: number; - public version!: string; - public scriptKind!: ScriptKind; - public languageVersion!: ScriptTarget; - public languageVariant!: LanguageVariant; - public identifiers!: Map; - public nameTable: Map<__String, number> | undefined; - public imports!: readonly StringLiteralLike[]; - public moduleAugmentations!: StringLiteral[]; - private namedDeclarations: Map | undefined; - public ambientModuleNames!: string[]; - public checkJsDirective: CheckJsDirective | undefined; - public errorExpectations: TextRange[] | undefined; - public possiblyContainDynamicImport?: boolean; - public pragmas!: PragmaMap; - public localJsxFactory: EntityName | undefined; - public localJsxNamespace: __String | undefined; - - constructor(kind: SyntaxKind.SourceFile, pos: number, end: number) { - super(kind, pos, end); - } - - public update(newText: string, textChangeRange: TextChangeRange): SourceFile { - return updateSourceFile(this, newText, textChangeRange); - } - - public getLineAndCharacterOfPosition(position: number): LineAndCharacter { - return getLineAndCharacterOfPosition(this, position); - } - - public getLineStarts(): readonly number[] { - return getLineStarts(this); - } - - public getPositionOfLineAndCharacter(line: number, character: number, allowEdits?: true): number { - return computePositionOfLineAndCharacter(getLineStarts(this), line, character, this.text, allowEdits); - } - - public getLineEndOfPosition(pos: number): number { - const { line } = this.getLineAndCharacterOfPosition(pos); - const lineStarts = this.getLineStarts(); - - let lastCharPos: number | undefined; - if (line + 1 >= lineStarts.length) { - lastCharPos = this.getEnd(); - } - if (!lastCharPos) { - lastCharPos = lineStarts[line + 1] - 1; - } - - const fullText = this.getFullText(); - // if the new line is "\r\n", we should return the last non-new-line-character position - return fullText[lastCharPos] === "\n" && fullText[lastCharPos - 1] === "\r" ? lastCharPos - 1 : lastCharPos; - } - - public getNamedDeclarations(): Map { - if (!this.namedDeclarations) { - this.namedDeclarations = this.computeNamedDeclarations(); - } - - return this.namedDeclarations; - } - - private computeNamedDeclarations(): Map { - const result = createMultiMap(); - - this.forEachChild(visit); - - return result; - - function addDeclaration(declaration: Declaration) { - const name = getDeclarationName(declaration); - if (name) { - result.add(name, declaration); - } - } - - function getDeclarations(name: string) { - let declarations = result.get(name); - if (!declarations) { - result.set(name, declarations = []); - } - return declarations; - } - - function getDeclarationName(declaration: Declaration) { - const name = getNonAssignedNameOfDeclaration(declaration); - return name && (isComputedPropertyName(name) && isPropertyAccessExpression(name.expression) ? name.expression.name.text - : isPropertyName(name) ? getNameFromPropertyName(name) : undefined); - } - - function visit(node: Node): void { - switch (node.kind) { - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - const functionDeclaration = node as FunctionLikeDeclaration; - const declarationName = getDeclarationName(functionDeclaration); - - if (declarationName) { - const declarations = getDeclarations(declarationName); - const lastDeclaration = lastOrUndefined(declarations); - - // Check whether this declaration belongs to an "overload group". - if (lastDeclaration && functionDeclaration.parent === lastDeclaration.parent && functionDeclaration.symbol === lastDeclaration.symbol) { - // Overwrite the last declaration if it was an overload - // and this one is an implementation. - if (functionDeclaration.body && !(lastDeclaration as FunctionLikeDeclaration).body) { - declarations[declarations.length - 1] = functionDeclaration; - } - } - else { - declarations.push(functionDeclaration); - } - } - forEachChild(node, visit); - break; - - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ExportSpecifier: - case SyntaxKind.ImportSpecifier: - case SyntaxKind.ImportClause: - case SyntaxKind.NamespaceImport: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.TypeLiteral: - addDeclaration(node as Declaration); - forEachChild(node, visit); - break; - - case SyntaxKind.Parameter: - // Only consider parameter properties - if (!hasSyntacticModifier(node, ModifierFlags.ParameterPropertyModifier)) { - break; - } - // falls through - - case SyntaxKind.VariableDeclaration: - case SyntaxKind.BindingElement: { - const decl = node as VariableDeclaration; - if (isBindingPattern(decl.name)) { - forEachChild(decl.name, visit); - break; - } - if (decl.initializer) { - visit(decl.initializer); - } - } - // falls through - case SyntaxKind.EnumMember: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - addDeclaration(node as Declaration); - break; - - case SyntaxKind.ExportDeclaration: - // Handle named exports case e.g.: - // export {a, b as B} from "mod"; - const exportDeclaration = node as ExportDeclaration; - if (exportDeclaration.exportClause) { - if (isNamedExports(exportDeclaration.exportClause)) { - forEach(exportDeclaration.exportClause.elements, visit); - } - else { - visit(exportDeclaration.exportClause.name); - } - } - break; - - case SyntaxKind.ImportDeclaration: - const importClause = (node as ImportDeclaration).importClause; - if (importClause) { - // Handle default import case e.g.: - // import d from "mod"; - if (importClause.name) { - addDeclaration(importClause.name); - } - - // Handle named bindings in imports e.g.: - // import * as NS from "mod"; - // import {a, b as B} from "mod"; - if (importClause.namedBindings) { - if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { - addDeclaration(importClause.namedBindings); - } - else { - forEach(importClause.namedBindings.elements, visit); - } - } - } - break; - - case SyntaxKind.BinaryExpression: - if (getAssignmentDeclarationKind(node as BinaryExpression) !== AssignmentDeclarationKind.None) { - addDeclaration(node as BinaryExpression); - } - // falls through - - default: - forEachChild(node, visit); - } - } - } -} - -class SourceMapSourceObject implements SourceMapSource { - fileName: string; - text: string; - skipTrivia?: ((pos: number) => number) | undefined; - lineMap!: number[]; - - constructor(fileName: string, text: string, skipTrivia?: (pos: number) => number) { - // Note: if modifying this, be sure to update SourceMapSource in src/compiler/types.ts - this.fileName = fileName; - this.text = text; - this.skipTrivia = skipTrivia || (pos => pos); - } - - public getLineAndCharacterOfPosition(pos: number): LineAndCharacter { - return getLineAndCharacterOfPosition(this, pos); - } -} - -function getServicesObjectAllocator(): ObjectAllocator { - return { - getNodeConstructor: () => NodeObject, - getTokenConstructor: () => TokenObject, - - getIdentifierConstructor: () => IdentifierObject, - getPrivateIdentifierConstructor: () => PrivateIdentifierObject, - getSourceFileConstructor: () => SourceFileObject, - getSymbolConstructor: () => SymbolObject, - getTypeConstructor: () => TypeObject, - getSignatureConstructor: () => SignatureObject, - getSourceMapSourceConstructor: () => SourceMapSourceObject, - }; -} - /// Language Service /** @internal */ @@ -3381,5 +2525,3 @@ export function getDefaultLibFilePath(options: CompilerOptions): string { throw new Error("getDefaultLibFilePath is only supported when consumed as a node module. "); } - -setObjectAllocator(getServicesObjectAllocator()); diff --git a/src/services/smartSelection.ts b/src/services/smartSelection.ts index d1cc5ca92472b..d726d177e682a 100644 --- a/src/services/smartSelection.ts +++ b/src/services/smartSelection.ts @@ -279,7 +279,7 @@ function getSelectionChildren(node: Node): readonly Node[] { * Groups sibling nodes together into their own SyntaxList if they * a) are adjacent, AND b) match a predicate function. */ -function groupChildren(children: Node[], groupOn: (child: Node) => boolean): Node[] { +function groupChildren(children: readonly Node[], groupOn: (child: Node) => boolean): Node[] { const result: Node[] = []; let group: Node[] | undefined; for (const child of children) { @@ -315,7 +315,7 @@ function groupChildren(children: Node[], groupOn: (child: Node) => boolean): Nod * @param separateTrailingSemicolon If the last token is a semicolon, it will be returned as a separate * child rather than be included in the right-hand group. */ -function splitChildren(children: Node[], pivotOn: (child: Node) => boolean, separateTrailingSemicolon = true): Node[] { +function splitChildren(children: readonly Node[], pivotOn: (child: Node) => boolean, separateTrailingSemicolon = true): readonly Node[] { if (children.length < 2) { return children; } @@ -336,7 +336,7 @@ function splitChildren(children: Node[], pivotOn: (child: Node) => boolean, sepa return separateLastToken ? result.concat(lastToken) : result; } -function createSyntaxList(children: Node[]): SyntaxList { +function createSyntaxList(children: readonly Node[]): SyntaxList { Debug.assertGreaterThanOrEqual(children.length, 1); return setTextRangePosEnd(parseNodeFactory.createSyntaxList(children), children[0].pos, last(children).end); } diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 5887fba6ae2e6..0db5287165258 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -1374,10 +1374,7 @@ function isTrivia(s: string) { // are more aggressive than is strictly necessary. const textChangesTransformationContext: TransformationContext = { ...nullTransformationContext, - factory: createNodeFactory( - nullTransformationContext.factory.flags | NodeFactoryFlags.NoParenthesizerRules, - nullTransformationContext.factory.baseFactory, - ), + factory: createNodeFactory(nullTransformationContext.factory.flags | NodeFactoryFlags.NoParenthesizerRules), }; /** @internal */ diff --git a/src/services/types.ts b/src/services/types.ts index f98d317d61dd5..f9af89795d234 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -7,6 +7,7 @@ import { DiagnosticWithLocation, DocumentHighlights, DocumentPositionMapper, + DocumentSpan, EmitOutput, ExportInfoMap, ExportMapInfoKey, @@ -14,7 +15,9 @@ import { GetEffectiveTypeRootsHost, HasChangedAutomaticTypeDirectiveNames, HasInvalidatedResolutions, + IScriptSnapshot, JSDocParsingMode, + JSDocTagInfo, LineAndCharacter, MinimalResolutionCacheHost, ModuleResolutionCache, @@ -35,6 +38,7 @@ import { SourceMapper, StringLiteralLike, Symbol, + SymbolDisplayPart, SymlinkCache, TextChangeRange, textChanges, @@ -43,178 +47,6 @@ import { UserPreferences, } from "./_namespaces/ts"; -declare module "../compiler/types" { - // Module transform: converted from interface augmentation - export interface Node { - getSourceFile(): SourceFile; - getChildCount(sourceFile?: SourceFile): number; - getChildAt(index: number, sourceFile?: SourceFile): Node; - getChildren(sourceFile?: SourceFile): Node[]; - /** @internal */ - getChildren(sourceFile?: SourceFileLike): Node[]; // eslint-disable-line @typescript-eslint/unified-signatures - getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number; - /** @internal */ - getStart(sourceFile?: SourceFileLike, includeJsDocComment?: boolean): number; // eslint-disable-line @typescript-eslint/unified-signatures - getFullStart(): number; - getEnd(): number; - getWidth(sourceFile?: SourceFileLike): number; - getFullWidth(): number; - getLeadingTriviaWidth(sourceFile?: SourceFile): number; - getFullText(sourceFile?: SourceFile): string; - getText(sourceFile?: SourceFile): string; - getFirstToken(sourceFile?: SourceFile): Node | undefined; - /** @internal */ - getFirstToken(sourceFile?: SourceFileLike): Node | undefined; // eslint-disable-line @typescript-eslint/unified-signatures - getLastToken(sourceFile?: SourceFile): Node | undefined; - /** @internal */ - getLastToken(sourceFile?: SourceFileLike): Node | undefined; // eslint-disable-line @typescript-eslint/unified-signatures - // See ts.forEachChild for documentation. - forEachChild(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray) => T | undefined): T | undefined; - } -} - -declare module "../compiler/types" { - // Module transform: converted from interface augmentation - export interface Identifier { - readonly text: string; - } -} - -declare module "../compiler/types" { - // Module transform: converted from interface augmentation - export interface PrivateIdentifier { - readonly text: string; - } -} - -declare module "../compiler/types" { - // Module transform: converted from interface augmentation - export interface Symbol { - readonly name: string; - getFlags(): SymbolFlags; - getEscapedName(): __String; - getName(): string; - getDeclarations(): Declaration[] | undefined; - getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[]; - /** @internal */ - getContextualDocumentationComment(context: Node | undefined, checker: TypeChecker | undefined): SymbolDisplayPart[]; - getJsDocTags(checker?: TypeChecker): JSDocTagInfo[]; - /** @internal */ - getContextualJsDocTags(context: Node | undefined, checker: TypeChecker | undefined): JSDocTagInfo[]; - } -} - -declare module "../compiler/types" { - // Module transform: converted from interface augmentation - export interface Type { - getFlags(): TypeFlags; - getSymbol(): Symbol | undefined; - getProperties(): Symbol[]; - getProperty(propertyName: string): Symbol | undefined; - getApparentProperties(): Symbol[]; - getCallSignatures(): readonly Signature[]; - getConstructSignatures(): readonly Signature[]; - getStringIndexType(): Type | undefined; - getNumberIndexType(): Type | undefined; - getBaseTypes(): BaseType[] | undefined; - getNonNullableType(): Type; - /** @internal */ getNonOptionalType(): Type; - /** @internal */ isNullableType(): boolean; - getConstraint(): Type | undefined; - getDefault(): Type | undefined; - - isUnion(): this is UnionType; - isIntersection(): this is IntersectionType; - isUnionOrIntersection(): this is UnionOrIntersectionType; - isLiteral(): this is LiteralType; - isStringLiteral(): this is StringLiteralType; - isNumberLiteral(): this is NumberLiteralType; - isTypeParameter(): this is TypeParameter; - isClassOrInterface(): this is InterfaceType; - isClass(): this is InterfaceType; - isIndexType(): this is IndexType; - } -} - -declare module "../compiler/types" { - // Module transform: converted from interface augmentation - export interface TypeReference { - typeArguments?: readonly Type[]; - } -} - -declare module "../compiler/types" { - // Module transform: converted from interface augmentation - export interface Signature { - getDeclaration(): SignatureDeclaration; - getTypeParameters(): TypeParameter[] | undefined; - getParameters(): Symbol[]; - getTypeParameterAtPosition(pos: number): Type; - getReturnType(): Type; - getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[]; - getJsDocTags(): JSDocTagInfo[]; - } -} - -declare module "../compiler/types" { - // Module transform: converted from interface augmentation - export interface SourceFile { - /** @internal */ version: string; - /** @internal */ scriptSnapshot: IScriptSnapshot | undefined; - /** @internal */ nameTable: Map<__String, number> | undefined; - - /** @internal */ getNamedDeclarations(): Map; - - getLineAndCharacterOfPosition(pos: number): LineAndCharacter; - getLineEndOfPosition(pos: number): number; - getLineStarts(): readonly number[]; - getPositionOfLineAndCharacter(line: number, character: number): number; - update(newText: string, textChangeRange: TextChangeRange): SourceFile; - - /** @internal */ sourceMapper?: DocumentPositionMapper; - } -} - -declare module "../compiler/types" { - // Module transform: converted from interface augmentation - export interface SourceFileLike { - getLineAndCharacterOfPosition(pos: number): LineAndCharacter; - } -} - -declare module "../compiler/types" { - // Module transform: converted from interface augmentation - export interface SourceMapSource { - getLineAndCharacterOfPosition(pos: number): LineAndCharacter; - } -} - -/** - * Represents an immutable snapshot of a script at a specified time.Once acquired, the - * snapshot is observably immutable. i.e. the same calls with the same parameters will return - * the same values. - */ -// eslint-disable-next-line @typescript-eslint/naming-convention -export interface IScriptSnapshot { - /** Gets a portion of the script snapshot specified by [start, end). */ - getText(start: number, end: number): string; - - /** Gets the length of this script snapshot. */ - getLength(): number; - - /** - * Gets the TextChangeRange that describe how the text changed between this text and - * an older version. This information is used by the incremental parser to determine - * what sections of the script need to be re-parsed. 'undefined' can be returned if the - * change range cannot be determined. However, in that case, incremental parsing will - * not happen and the entire document will be re - parsed. - */ - getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange | undefined; - - /** Releases all resources held by this script snapshot */ - dispose?(): void; -} - export namespace ScriptSnapshot { class StringScriptSnapshot implements IScriptSnapshot { constructor(private text: string) { @@ -1035,25 +867,6 @@ export interface TextInsertion { caretOffset: number; } -export interface DocumentSpan { - textSpan: TextSpan; - fileName: string; - - /** - * If the span represents a location that was remapped (e.g. via a .d.ts.map file), - * then the original filename and span will be specified here - */ - originalTextSpan?: TextSpan; - originalFileName?: string; - - /** - * If DocumentSpan.textSpan is the span for name of the declaration, - * then this is the span for relevant declaration - */ - contextSpan?: TextSpan; - originalContextSpan?: TextSpan; -} - export interface RenameLocation extends DocumentSpan { readonly prefixText?: string; readonly suffixText?: string; @@ -1264,26 +1077,6 @@ export enum SymbolDisplayPartKind { linkText, } -export interface SymbolDisplayPart { - /** - * Text of an item describing the symbol. - */ - text: string; - /** - * The symbol's kind (such as 'className' or 'parameterName' or plain 'text'). - */ - kind: string; -} - -export interface JSDocLinkDisplayPart extends SymbolDisplayPart { - target: DocumentSpan; -} - -export interface JSDocTagInfo { - name: string; - text?: SymbolDisplayPart[]; -} - export interface QuickInfo { kind: ScriptElementKind; kindModifiers: string; diff --git a/src/services/utilities.ts b/src/services/utilities.ts index dade1fe37bdd3..ba082b1e18e8b 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -116,7 +116,6 @@ import { getSourceFileOfNode, getSpanOfTokenAtPosition, getSymbolId, - getTextOfIdentifierOrLiteral, getTextOfNode, getTypesPackageName, hasJSFileExtension, @@ -126,7 +125,6 @@ import { Identifier, identifierIsThisKeyword, identity, - idText, IfStatement, ImportClause, ImportDeclaration, @@ -243,7 +241,6 @@ import { isStringDoubleQuoted, isStringLiteral, isStringLiteralLike, - isStringOrNumericLiteralLike, isStringTextContainingNode, isSyntaxList, isTaggedTemplateExpression, @@ -314,7 +311,6 @@ import { ProjectPackageJsonInfo, PropertyAccessExpression, PropertyAssignment, - PropertyName, PseudoBigInt, pseudoBigIntToString, QualifiedName, @@ -1830,7 +1826,7 @@ function findRightmostToken(n: Node, sourceFile: SourceFileLike): Node | undefin /** * Finds the rightmost child to the left of `children[exclusiveStartPosition]` which is a non-all-whitespace token or has constituent tokens. */ -function findRightmostChildNodeWithTokens(children: Node[], exclusiveStartPosition: number, sourceFile: SourceFileLike, parentKind: SyntaxKind): Node | undefined { +function findRightmostChildNodeWithTokens(children: readonly Node[], exclusiveStartPosition: number, sourceFile: SourceFileLike, parentKind: SyntaxKind): Node | undefined { for (let i = exclusiveStartPosition - 1; i >= 0; i--) { const child = children[i]; @@ -2446,14 +2442,6 @@ export function skipConstraint(type: Type): Type { return type.isTypeParameter() ? type.getConstraint() || type : type; } -/** @internal */ -export function getNameFromPropertyName(name: PropertyName): string | undefined { - return name.kind === SyntaxKind.ComputedPropertyName - // treat computed property names where expression is string/numeric literal as just string/numeric literal - ? isStringOrNumericLiteralLike(name.expression) ? name.expression.text : undefined - : isPrivateIdentifier(name) ? idText(name) : getTextOfIdentifierOrLiteral(name); -} - /** @internal */ export function programContainsModules(program: Program): boolean { return program.getSourceFiles().some(s => !s.isDeclarationFile && !program.isSourceFileFromExternalLibrary(s) && !!(s.externalModuleIndicator || s.commonJsModuleIndicator)); diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3fa0b1c91162f..a9780941e135a 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4210,12 +4210,10 @@ declare namespace ts { readonly kind: SyntaxKind; readonly flags: NodeFlags; readonly parent: Node; - } - interface Node { getSourceFile(): SourceFile; getChildCount(sourceFile?: SourceFile): number; getChildAt(index: number, sourceFile?: SourceFile): Node; - getChildren(sourceFile?: SourceFile): Node[]; + getChildren(sourceFile?: SourceFile): readonly Node[]; getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number; getFullStart(): number; getEnd(): number; @@ -4374,8 +4372,6 @@ declare namespace ts { * Text of identifier, but if the identifier begins with two underscores, this will begin with three. */ readonly escapedText: __String; - } - interface Identifier { readonly text: string; } interface TransientIdentifier extends Identifier { @@ -4407,8 +4403,6 @@ declare namespace ts { interface PrivateIdentifier extends PrimaryExpression { readonly kind: SyntaxKind.PrivateIdentifier; readonly escapedText: __String; - } - interface PrivateIdentifier { readonly text: string; } interface Decorator extends Node { @@ -5801,8 +5795,6 @@ declare namespace ts { */ interface SourceFileLike { readonly text: string; - } - interface SourceFileLike { getLineAndCharacterOfPosition(pos: number): LineAndCharacter; } type ResolutionMode = ModuleKind.ESNext | ModuleKind.CommonJS | undefined; @@ -5847,14 +5839,33 @@ declare namespace ts { * CommonJS-output-format by the node module transformer and type checker, regardless of extension or context. */ impliedNodeFormat?: ResolutionMode; - } - interface SourceFile { getLineAndCharacterOfPosition(pos: number): LineAndCharacter; getLineEndOfPosition(pos: number): number; getLineStarts(): readonly number[]; getPositionOfLineAndCharacter(line: number, character: number): number; update(newText: string, textChangeRange: TextChangeRange): SourceFile; } + /** + * Represents an immutable snapshot of a script at a specified time. Once acquired, the + * snapshot is observably immutable. i.e. the same calls with the same parameters will return + * the same values. + */ + interface IScriptSnapshot { + /** Gets a portion of the script snapshot specified by [start, end). */ + getText(start: number, end: number): string; + /** Gets the length of this script snapshot. */ + getLength(): number; + /** + * Gets the TextChangeRange that describe how the text changed between this text and + * an older version. This information is used by the incremental parser to determine + * what sections of the script need to be re-parsed. 'undefined' can be returned if the + * change range cannot be determined. However, in that case, incremental parsing will + * not happen and the entire document will be re - parsed. + */ + getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange | undefined; + /** Releases all resources held by this script snapshot */ + dispose?(): void; + } interface Bundle extends Node { readonly kind: SyntaxKind.Bundle; readonly sourceFiles: readonly SourceFile[]; @@ -6399,6 +6410,39 @@ declare namespace ts { PropertyOrAccessor = 98308, ClassMember = 106500, } + interface SymbolDisplayPart { + /** + * Text of an item describing the symbol. + */ + text: string; + /** + * The symbol's kind (such as 'className' or 'parameterName' or plain 'text'). + */ + kind: string; + } + interface DocumentSpan { + textSpan: TextSpan; + fileName: string; + /** + * If the span represents a location that was remapped (e.g. via a .d.ts.map file), + * then the original filename and span will be specified here + */ + originalTextSpan?: TextSpan; + originalFileName?: string; + /** + * If DocumentSpan.textSpan is the span for name of the declaration, + * then this is the span for relevant declaration + */ + contextSpan?: TextSpan; + originalContextSpan?: TextSpan; + } + interface JSDocLinkDisplayPart extends SymbolDisplayPart { + target: DocumentSpan; + } + interface JSDocTagInfo { + name: string; + text?: SymbolDisplayPart[]; + } interface Symbol { flags: SymbolFlags; escapedName: __String; @@ -6407,8 +6451,6 @@ declare namespace ts { members?: SymbolTable; exports?: SymbolTable; globalExports?: SymbolTable; - } - interface Symbol { readonly name: string; getFlags(): SymbolFlags; getEscapedName(): __String; @@ -6518,8 +6560,6 @@ declare namespace ts { pattern?: DestructuringPattern; aliasSymbol?: Symbol; aliasTypeArguments?: readonly Type[]; - } - interface Type { getFlags(): TypeFlags; getSymbol(): Symbol | undefined; getProperties(): Symbol[]; @@ -6619,9 +6659,7 @@ declare namespace ts { interface TypeReference extends ObjectType { target: GenericType; node?: TypeReferenceNode | ArrayTypeNode | TupleTypeNode; - } - interface TypeReference { - typeArguments?: readonly Type[]; + readonly typeArguments?: readonly Type[]; } interface DeferredTypeReference extends TypeReference { } @@ -6719,11 +6757,9 @@ declare namespace ts { typeParameters?: readonly TypeParameter[]; parameters: readonly Symbol[]; thisParameter?: Symbol; - } - interface Signature { - getDeclaration(): SignatureDeclaration; - getTypeParameters(): TypeParameter[] | undefined; - getParameters(): Symbol[]; + getDeclaration(): JSDocSignature | SignatureDeclaration; + getTypeParameters(): readonly TypeParameter[] | undefined; + getParameters(): readonly Symbol[]; getTypeParameterAtPosition(pos: number): Type; getReturnType(): Type; getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[]; @@ -7234,8 +7270,6 @@ declare namespace ts { fileName: string; text: string; skipTrivia?: (pos: number) => number; - } - interface SourceMapSource { getLineAndCharacterOfPosition(pos: number): LineAndCharacter; } enum EmitFlags { @@ -9828,27 +9862,6 @@ declare namespace ts { } type InvalidatedProject = UpdateOutputFileStampsProject | BuildInvalidedProject; function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings; - /** - * Represents an immutable snapshot of a script at a specified time.Once acquired, the - * snapshot is observably immutable. i.e. the same calls with the same parameters will return - * the same values. - */ - interface IScriptSnapshot { - /** Gets a portion of the script snapshot specified by [start, end). */ - getText(start: number, end: number): string; - /** Gets the length of this script snapshot. */ - getLength(): number; - /** - * Gets the TextChangeRange that describe how the text changed between this text and - * an older version. This information is used by the incremental parser to determine - * what sections of the script need to be re-parsed. 'undefined' can be returned if the - * change range cannot be determined. However, in that case, incremental parsing will - * not happen and the entire document will be re - parsed. - */ - getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange | undefined; - /** Releases all resources held by this script snapshot */ - dispose?(): void; - } namespace ScriptSnapshot { function fromString(text: string): IScriptSnapshot; } @@ -10404,22 +10417,6 @@ declare namespace ts { /** The position in newText the caret should point to after the insertion. */ caretOffset: number; } - interface DocumentSpan { - textSpan: TextSpan; - fileName: string; - /** - * If the span represents a location that was remapped (e.g. via a .d.ts.map file), - * then the original filename and span will be specified here - */ - originalTextSpan?: TextSpan; - originalFileName?: string; - /** - * If DocumentSpan.textSpan is the span for name of the declaration, - * then this is the span for relevant declaration - */ - contextSpan?: TextSpan; - originalContextSpan?: TextSpan; - } interface RenameLocation extends DocumentSpan { readonly prefixText?: string; readonly suffixText?: string; @@ -10573,23 +10570,6 @@ declare namespace ts { linkName = 23, linkText = 24, } - interface SymbolDisplayPart { - /** - * Text of an item describing the symbol. - */ - text: string; - /** - * The symbol's kind (such as 'className' or 'parameterName' or plain 'text'). - */ - kind: string; - } - interface JSDocLinkDisplayPart extends SymbolDisplayPart { - target: DocumentSpan; - } - interface JSDocTagInfo { - name: string; - text?: SymbolDisplayPart[]; - } interface QuickInfo { kind: ScriptElementKind; kindModifiers: string;