UNPKG

30.2 kBJavaScriptView Raw
1"use strict";
2// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3// See LICENSE in the project root for license information.
4var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5 if (k2 === undefined) k2 = k;
6 var desc = Object.getOwnPropertyDescriptor(m, k);
7 if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8 desc = { enumerable: true, get: function() { return m[k]; } };
9 }
10 Object.defineProperty(o, k2, desc);
11}) : (function(o, m, k, k2) {
12 if (k2 === undefined) k2 = k;
13 o[k2] = m[k];
14}));
15var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16 Object.defineProperty(o, "default", { enumerable: true, value: v });
17}) : function(o, v) {
18 o["default"] = v;
19});
20var __importStar = (this && this.__importStar) || function (mod) {
21 if (mod && mod.__esModule) return mod;
22 var result = {};
23 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
24 __setModuleDefault(result, mod);
25 return result;
26};
27Object.defineProperty(exports, "__esModule", { value: true });
28exports.AstSymbolTable = void 0;
29/* eslint-disable no-bitwise */ // for ts.SymbolFlags
30const ts = __importStar(require("typescript"));
31const node_core_library_1 = require("@rushstack/node-core-library");
32const AstDeclaration_1 = require("./AstDeclaration");
33const TypeScriptHelpers_1 = require("./TypeScriptHelpers");
34const AstSymbol_1 = require("./AstSymbol");
35const PackageMetadataManager_1 = require("./PackageMetadataManager");
36const ExportAnalyzer_1 = require("./ExportAnalyzer");
37const AstNamespaceImport_1 = require("./AstNamespaceImport");
38const TypeScriptInternals_1 = require("./TypeScriptInternals");
39const SyntaxHelpers_1 = require("./SyntaxHelpers");
40const SourceFileLocationFormatter_1 = require("./SourceFileLocationFormatter");
41/**
42 * AstSymbolTable is the workhorse that builds AstSymbol and AstDeclaration objects.
43 * It maintains a cache of already constructed objects. AstSymbolTable constructs
44 * AstModule objects, but otherwise the state that it maintains is agnostic of
45 * any particular entry point. (For example, it does not track whether a given AstSymbol
46 * is "exported" or not.)
47 *
48 * Internally, AstSymbolTable relies on ExportAnalyzer to crawl import statements and determine where symbols
49 * are declared (i.e. the AstImport information needed to import them).
50 */
51class AstSymbolTable {
52 constructor(program, typeChecker, packageJsonLookup, bundledPackageNames, messageRouter) {
53 /**
54 * A mapping from ts.Symbol --> AstSymbol
55 * NOTE: The AstSymbol.followedSymbol will always be a lookup key, but additional keys
56 * are possible.
57 *
58 * After following type aliases, we use this map to look up the corresponding AstSymbol.
59 */
60 this._astSymbolsBySymbol = new Map();
61 /**
62 * A mapping from ts.Declaration --> AstDeclaration
63 */
64 this._astDeclarationsByDeclaration = new Map();
65 // Note that this is a mapping from specific AST nodes that we analyzed, based on the underlying symbol
66 // for that node.
67 this._entitiesByNode = new Map();
68 this._program = program;
69 this._typeChecker = typeChecker;
70 this._messageRouter = messageRouter;
71 this._globalVariableAnalyzer = TypeScriptInternals_1.TypeScriptInternals.getGlobalVariableAnalyzer(program);
72 this._packageMetadataManager = new PackageMetadataManager_1.PackageMetadataManager(packageJsonLookup, messageRouter);
73 this._exportAnalyzer = new ExportAnalyzer_1.ExportAnalyzer(this._program, this._typeChecker, bundledPackageNames, {
74 analyze: this.analyze.bind(this),
75 fetchAstSymbol: this._fetchAstSymbol.bind(this)
76 });
77 this._alreadyWarnedGlobalNames = new Set();
78 }
79 /**
80 * Used to analyze an entry point that belongs to the working package.
81 */
82 fetchAstModuleFromWorkingPackage(sourceFile) {
83 return this._exportAnalyzer.fetchAstModuleFromSourceFile(sourceFile, undefined, false);
84 }
85 /**
86 * This crawls the specified entry point and collects the full set of exported AstSymbols.
87 */
88 fetchAstModuleExportInfo(astModule) {
89 return this._exportAnalyzer.fetchAstModuleExportInfo(astModule);
90 }
91 /**
92 * Attempts to retrieve an export by name from the specified `AstModule`.
93 * Returns undefined if no match was found.
94 */
95 tryGetExportOfAstModule(exportName, astModule) {
96 return this._exportAnalyzer.tryGetExportOfAstModule(exportName, astModule);
97 }
98 /**
99 * Ensures that AstSymbol.analyzed is true for the provided symbol. The operation
100 * starts from the root symbol and then fills out all children of all declarations, and
101 * also calculates AstDeclaration.referencedAstSymbols for all declarations.
102 * If the symbol is not imported, any non-imported references are also analyzed.
103 *
104 * @remarks
105 * This is an expensive operation, so we only perform it for top-level exports of an
106 * the AstModule. For example, if some code references a nested class inside
107 * a namespace from another library, we do not analyze any of that class's siblings
108 * or members. (We do always construct its parents however, since AstDefinition.parent
109 * is immutable, and needed e.g. to calculate release tag inheritance.)
110 */
111 analyze(astEntity) {
112 if (astEntity instanceof AstSymbol_1.AstSymbol) {
113 return this._analyzeAstSymbol(astEntity);
114 }
115 if (astEntity instanceof AstNamespaceImport_1.AstNamespaceImport) {
116 return this._analyzeAstNamespaceImport(astEntity);
117 }
118 }
119 /**
120 * For a given astDeclaration, this efficiently finds the child corresponding to the
121 * specified ts.Node. It is assumed that AstDeclaration.isSupportedSyntaxKind() would return true for
122 * that node type, and that the node is an immediate child of the provided AstDeclaration.
123 */
124 // NOTE: This could be a method of AstSymbol if it had a backpointer to its AstSymbolTable.
125 getChildAstDeclarationByNode(node, parentAstDeclaration) {
126 if (!parentAstDeclaration.astSymbol.analyzed) {
127 throw new Error('getChildDeclarationByNode() cannot be used for an AstSymbol that was not analyzed');
128 }
129 const childAstDeclaration = this._astDeclarationsByDeclaration.get(node);
130 if (!childAstDeclaration) {
131 throw new Error('Child declaration not found for the specified node');
132 }
133 if (childAstDeclaration.parent !== parentAstDeclaration) {
134 throw new node_core_library_1.InternalError('The found child is not attached to the parent AstDeclaration');
135 }
136 return childAstDeclaration;
137 }
138 /**
139 * For a given ts.Identifier that is part of an AstSymbol that we analyzed, return the AstEntity that
140 * it refers to. Returns undefined if it doesn't refer to anything interesting.
141 * @remarks
142 * Throws an Error if the ts.Identifier is not part of node tree that was analyzed.
143 */
144 tryGetEntityForNode(identifier) {
145 if (!this._entitiesByNode.has(identifier)) {
146 throw new node_core_library_1.InternalError('tryGetEntityForIdentifier() called for an identifier that was not analyzed');
147 }
148 return this._entitiesByNode.get(identifier);
149 }
150 /**
151 * Builds an AstSymbol.localName for a given ts.Symbol. In the current implementation, the localName is
152 * a TypeScript-like expression that may be a string literal or ECMAScript symbol expression.
153 *
154 * ```ts
155 * class X {
156 * // localName="identifier"
157 * public identifier: number = 1;
158 * // localName="\"identifier\""
159 * public "quoted string!": number = 2;
160 * // localName="[MyNamespace.MySymbol]"
161 * public [MyNamespace.MySymbol]: number = 3;
162 * }
163 * ```
164 */
165 static getLocalNameForSymbol(symbol) {
166 // TypeScript binds well-known ECMAScript symbols like "[Symbol.iterator]" as "__@iterator".
167 // Decode it back into "[Symbol.iterator]".
168 const wellKnownSymbolName = TypeScriptHelpers_1.TypeScriptHelpers.tryDecodeWellKnownSymbolName(symbol.escapedName);
169 if (wellKnownSymbolName) {
170 return wellKnownSymbolName;
171 }
172 const isUniqueSymbol = TypeScriptHelpers_1.TypeScriptHelpers.isUniqueSymbolName(symbol.escapedName);
173 // We will try to obtain the name from a declaration; otherwise we'll fall back to the symbol name.
174 let unquotedName = symbol.name;
175 for (const declaration of symbol.declarations || []) {
176 // Handle cases such as "export default class X { }" where the symbol name is "default"
177 // but the local name is "X".
178 const localSymbol = TypeScriptInternals_1.TypeScriptInternals.tryGetLocalSymbol(declaration);
179 if (localSymbol) {
180 unquotedName = localSymbol.name;
181 }
182 // If it is a non-well-known symbol, then return the late-bound name. For example, "X.Y.z" in this example:
183 //
184 // namespace X {
185 // export namespace Y {
186 // export const z: unique symbol = Symbol("z");
187 // }
188 // }
189 //
190 // class C {
191 // public [X.Y.z](): void { }
192 // }
193 //
194 if (isUniqueSymbol) {
195 const declarationName = ts.getNameOfDeclaration(declaration);
196 if (declarationName && ts.isComputedPropertyName(declarationName)) {
197 const lateBoundName = TypeScriptHelpers_1.TypeScriptHelpers.tryGetLateBoundName(declarationName);
198 if (lateBoundName) {
199 // Here the string may contain an expression such as "[X.Y.z]". Names starting with "[" are always
200 // expressions. If a string literal contains those characters, the code below will JSON.stringify() it
201 // to avoid a collision.
202 return lateBoundName;
203 }
204 }
205 }
206 }
207 // Otherwise that name may come from a quoted string or pseudonym like `__constructor`.
208 // If the string is not a safe identifier, then we must add quotes.
209 // Note that if it was quoted but did not need to be quoted, here we will remove the quotes.
210 if (!SyntaxHelpers_1.SyntaxHelpers.isSafeUnquotedMemberIdentifier(unquotedName)) {
211 // For API Extractor's purposes, a canonical form is more appropriate than trying to reflect whatever
212 // appeared in the source code. The code is not even guaranteed to be consistent, for example:
213 //
214 // class X {
215 // public "f1"(x: string): void;
216 // public f1(x: boolean): void;
217 // public 'f1'(x: string | boolean): void { }
218 // }
219 return JSON.stringify(unquotedName);
220 }
221 return unquotedName;
222 }
223 _analyzeAstNamespaceImport(astNamespaceImport) {
224 if (astNamespaceImport.analyzed) {
225 return;
226 }
227 // mark before actual analyzing, to handle module cyclic reexport
228 astNamespaceImport.analyzed = true;
229 const exportedLocalEntities = this.fetchAstModuleExportInfo(astNamespaceImport.astModule).exportedLocalEntities;
230 for (const exportedEntity of exportedLocalEntities.values()) {
231 this.analyze(exportedEntity);
232 }
233 }
234 _analyzeAstSymbol(astSymbol) {
235 if (astSymbol.analyzed) {
236 return;
237 }
238 if (astSymbol.nominalAnalysis) {
239 // We don't analyze nominal symbols
240 astSymbol._notifyAnalyzed();
241 return;
242 }
243 // Start at the root of the tree
244 const rootAstSymbol = astSymbol.rootAstSymbol;
245 // Calculate the full child tree for each definition
246 for (const astDeclaration of rootAstSymbol.astDeclarations) {
247 this._analyzeChildTree(astDeclaration.declaration, astDeclaration);
248 }
249 rootAstSymbol._notifyAnalyzed();
250 if (!astSymbol.isExternal) {
251 // If this symbol is non-external (i.e. it belongs to the working package), then we also analyze any
252 // referencedAstSymbols that are non-external. For example, this ensures that forgotten exports
253 // get analyzed.
254 rootAstSymbol.forEachDeclarationRecursive((astDeclaration) => {
255 for (const referencedAstEntity of astDeclaration.referencedAstEntities) {
256 // Walk up to the root of the tree, looking for any imports along the way
257 if (referencedAstEntity instanceof AstSymbol_1.AstSymbol) {
258 if (!referencedAstEntity.isExternal) {
259 this._analyzeAstSymbol(referencedAstEntity);
260 }
261 }
262 if (referencedAstEntity instanceof AstNamespaceImport_1.AstNamespaceImport) {
263 if (!referencedAstEntity.astModule.isExternal) {
264 this._analyzeAstNamespaceImport(referencedAstEntity);
265 }
266 }
267 }
268 });
269 }
270 }
271 /**
272 * Used by analyze to recursively analyze the entire child tree.
273 */
274 _analyzeChildTree(node, governingAstDeclaration) {
275 switch (node.kind) {
276 case ts.SyntaxKind.JSDocComment: // Skip JSDoc comments - TS considers @param tags TypeReference nodes
277 return;
278 // Is this a reference to another AstSymbol?
279 case ts.SyntaxKind.TypeReference: // general type references
280 case ts.SyntaxKind.ExpressionWithTypeArguments: // special case for e.g. the "extends" keyword
281 case ts.SyntaxKind.ComputedPropertyName: // used for EcmaScript "symbols", e.g. "[toPrimitive]".
282 case ts.SyntaxKind.TypeQuery: // represents for "typeof X" as a type
283 {
284 // Sometimes the type reference will involve multiple identifiers, e.g. "a.b.C".
285 // In this case, we only need to worry about importing the first identifier,
286 // so do a depth-first search for it:
287 const identifierNode = TypeScriptHelpers_1.TypeScriptHelpers.findFirstChildNode(node, ts.SyntaxKind.Identifier);
288 if (identifierNode) {
289 let referencedAstEntity = this._entitiesByNode.get(identifierNode);
290 if (!referencedAstEntity) {
291 const symbol = this._typeChecker.getSymbolAtLocation(identifierNode);
292 if (!symbol) {
293 throw new Error('Symbol not found for identifier: ' + identifierNode.getText());
294 }
295 // Normally we expect getSymbolAtLocation() to take us to a declaration within the same source
296 // file, or else to an explicit "import" statement within the same source file. But in certain
297 // situations (e.g. a global variable) the symbol will refer to a declaration in some other
298 // source file. We'll call that case a "displaced symbol".
299 //
300 // For more info, see this discussion:
301 // https://github.com/microsoft/rushstack/issues/1765#issuecomment-595559849
302 let displacedSymbol = true;
303 for (const declaration of symbol.declarations || []) {
304 if (declaration.getSourceFile() === identifierNode.getSourceFile()) {
305 displacedSymbol = false;
306 break;
307 }
308 }
309 if (displacedSymbol) {
310 if (this._globalVariableAnalyzer.hasGlobalName(identifierNode.text)) {
311 // If the displaced symbol is a global variable, then API Extractor simply ignores it.
312 // Ambient declarations typically describe the runtime environment (provided by an API consumer),
313 // so we don't bother analyzing them as an API contract. (There are probably some packages
314 // that include interesting global variables in their API, but API Extractor doesn't support
315 // that yet; it would be a feature request.)
316 if (this._messageRouter.showDiagnostics) {
317 if (!this._alreadyWarnedGlobalNames.has(identifierNode.text)) {
318 this._alreadyWarnedGlobalNames.add(identifierNode.text);
319 this._messageRouter.logDiagnostic(`Ignoring reference to global variable "${identifierNode.text}"` +
320 ` in ` +
321 SourceFileLocationFormatter_1.SourceFileLocationFormatter.formatDeclaration(identifierNode));
322 }
323 }
324 }
325 else {
326 // If you encounter this, please report a bug with a repro. We're interested to know
327 // how it can occur.
328 throw new node_core_library_1.InternalError(`Unable to follow symbol for "${identifierNode.text}"`);
329 }
330 }
331 else {
332 referencedAstEntity = this._exportAnalyzer.fetchReferencedAstEntity(symbol, governingAstDeclaration.astSymbol.isExternal);
333 this._entitiesByNode.set(identifierNode, referencedAstEntity);
334 }
335 }
336 if (referencedAstEntity) {
337 governingAstDeclaration._notifyReferencedAstEntity(referencedAstEntity);
338 }
339 }
340 }
341 break;
342 // Is this the identifier for the governingAstDeclaration?
343 case ts.SyntaxKind.Identifier:
344 {
345 const identifierNode = node;
346 if (!this._entitiesByNode.has(identifierNode)) {
347 const symbol = this._typeChecker.getSymbolAtLocation(identifierNode);
348 let referencedAstEntity = undefined;
349 if (symbol === governingAstDeclaration.astSymbol.followedSymbol) {
350 referencedAstEntity = this._fetchEntityForNode(identifierNode, governingAstDeclaration);
351 }
352 this._entitiesByNode.set(identifierNode, referencedAstEntity);
353 }
354 }
355 break;
356 case ts.SyntaxKind.ImportType:
357 {
358 const importTypeNode = node;
359 let referencedAstEntity = this._entitiesByNode.get(importTypeNode);
360 if (!this._entitiesByNode.has(importTypeNode)) {
361 referencedAstEntity = this._fetchEntityForNode(importTypeNode, governingAstDeclaration);
362 if (!referencedAstEntity) {
363 // This should never happen
364 throw new Error('Failed to fetch entity for import() type node: ' + importTypeNode.getText());
365 }
366 this._entitiesByNode.set(importTypeNode, referencedAstEntity);
367 }
368 if (referencedAstEntity) {
369 governingAstDeclaration._notifyReferencedAstEntity(referencedAstEntity);
370 }
371 }
372 break;
373 }
374 // Is this node declaring a new AstSymbol?
375 const newGoverningAstDeclaration = this._fetchAstDeclaration(node, governingAstDeclaration.astSymbol.isExternal);
376 for (const childNode of node.getChildren()) {
377 this._analyzeChildTree(childNode, newGoverningAstDeclaration || governingAstDeclaration);
378 }
379 }
380 _fetchEntityForNode(node, governingAstDeclaration) {
381 let referencedAstEntity = this._entitiesByNode.get(node);
382 if (!referencedAstEntity) {
383 if (node.kind === ts.SyntaxKind.ImportType) {
384 referencedAstEntity = this._exportAnalyzer.fetchReferencedAstEntityFromImportTypeNode(node, governingAstDeclaration.astSymbol.isExternal);
385 }
386 else {
387 const symbol = this._typeChecker.getSymbolAtLocation(node);
388 if (!symbol) {
389 throw new Error('Symbol not found for identifier: ' + node.getText());
390 }
391 referencedAstEntity = this._exportAnalyzer.fetchReferencedAstEntity(symbol, governingAstDeclaration.astSymbol.isExternal);
392 }
393 this._entitiesByNode.set(node, referencedAstEntity);
394 }
395 return referencedAstEntity;
396 }
397 _fetchAstDeclaration(node, isExternal) {
398 if (!AstDeclaration_1.AstDeclaration.isSupportedSyntaxKind(node.kind)) {
399 return undefined;
400 }
401 const symbol = TypeScriptHelpers_1.TypeScriptHelpers.getSymbolForDeclaration(node, this._typeChecker);
402 if (!symbol) {
403 throw new node_core_library_1.InternalError('Unable to find symbol for node');
404 }
405 const astSymbol = this._fetchAstSymbol({
406 followedSymbol: symbol,
407 isExternal: isExternal,
408 includeNominalAnalysis: true,
409 addIfMissing: true
410 });
411 if (!astSymbol) {
412 return undefined;
413 }
414 const astDeclaration = this._astDeclarationsByDeclaration.get(node);
415 if (!astDeclaration) {
416 throw new node_core_library_1.InternalError('Unable to find constructed AstDeclaration');
417 }
418 return astDeclaration;
419 }
420 _fetchAstSymbol(options) {
421 const followedSymbol = options.followedSymbol;
422 // Filter out symbols representing constructs that we don't care about
423 const arbitraryDeclaration = TypeScriptHelpers_1.TypeScriptHelpers.tryGetADeclaration(followedSymbol);
424 if (!arbitraryDeclaration) {
425 return undefined;
426 }
427 if (followedSymbol.flags &
428 (ts.SymbolFlags.TypeParameter | ts.SymbolFlags.TypeLiteral | ts.SymbolFlags.Transient)) {
429 if (!TypeScriptInternals_1.TypeScriptInternals.isLateBoundSymbol(followedSymbol)) {
430 return undefined;
431 }
432 }
433 // API Extractor doesn't analyze ambient declarations at all
434 if (TypeScriptHelpers_1.TypeScriptHelpers.isAmbient(followedSymbol, this._typeChecker)) {
435 // We make a special exemption for ambient declarations that appear in a source file containing
436 // an "export=" declaration that allows them to be imported as non-ambient.
437 if (!this._exportAnalyzer.isImportableAmbientSourceFile(arbitraryDeclaration.getSourceFile())) {
438 return undefined;
439 }
440 }
441 // Make sure followedSymbol isn't an alias for something else
442 if (TypeScriptHelpers_1.TypeScriptHelpers.isFollowableAlias(followedSymbol, this._typeChecker)) {
443 // We expect the caller to have already followed any aliases
444 throw new node_core_library_1.InternalError('AstSymbolTable._fetchAstSymbol() cannot be called with a symbol alias');
445 }
446 let astSymbol = this._astSymbolsBySymbol.get(followedSymbol);
447 if (!astSymbol) {
448 // None of the above lookups worked, so create a new entry...
449 let nominalAnalysis = false;
450 if (options.isExternal) {
451 // If the file is from an external package that does not support AEDoc, normally we ignore it completely.
452 // But in some cases (e.g. checking star exports of an external package) we need an AstSymbol to
453 // represent it, but we don't need to analyze its sibling/children.
454 const followedSymbolSourceFileName = arbitraryDeclaration.getSourceFile().fileName;
455 if (!this._packageMetadataManager.isAedocSupportedFor(followedSymbolSourceFileName)) {
456 nominalAnalysis = true;
457 if (!options.includeNominalAnalysis) {
458 return undefined;
459 }
460 }
461 }
462 let parentAstSymbol = undefined;
463 if (!nominalAnalysis) {
464 for (const declaration of followedSymbol.declarations || []) {
465 if (!AstDeclaration_1.AstDeclaration.isSupportedSyntaxKind(declaration.kind)) {
466 throw new node_core_library_1.InternalError(`The "${followedSymbol.name}" symbol has a` +
467 ` ts.SyntaxKind.${ts.SyntaxKind[declaration.kind]} declaration which is not (yet?)` +
468 ` supported by API Extractor`);
469 }
470 }
471 // We always fetch the entire chain of parents for each declaration.
472 // (Children/siblings are only analyzed on demand.)
473 // Key assumptions behind this squirrely logic:
474 //
475 // IF a given symbol has two declarations D1 and D2; AND
476 // If D1 has a parent P1, then
477 // - D2 will also have a parent P2; AND
478 // - P1 and P2's symbol will be the same
479 // - but P1 and P2 may be different (e.g. merged namespaces containing merged interfaces)
480 // Is there a parent AstSymbol? First we check to see if there is a parent declaration:
481 const arbitraryDeclaration = TypeScriptHelpers_1.TypeScriptHelpers.tryGetADeclaration(followedSymbol);
482 if (arbitraryDeclaration) {
483 const arbitraryParentDeclaration = this._tryFindFirstAstDeclarationParent(arbitraryDeclaration);
484 if (arbitraryParentDeclaration) {
485 const parentSymbol = TypeScriptHelpers_1.TypeScriptHelpers.getSymbolForDeclaration(arbitraryParentDeclaration, this._typeChecker);
486 parentAstSymbol = this._fetchAstSymbol({
487 followedSymbol: parentSymbol,
488 isExternal: options.isExternal,
489 includeNominalAnalysis: false,
490 addIfMissing: true
491 });
492 if (!parentAstSymbol) {
493 throw new node_core_library_1.InternalError('Unable to construct a parent AstSymbol for ' + followedSymbol.name);
494 }
495 }
496 }
497 }
498 const localName = options.localName || AstSymbolTable.getLocalNameForSymbol(followedSymbol);
499 astSymbol = new AstSymbol_1.AstSymbol({
500 followedSymbol: followedSymbol,
501 localName: localName,
502 isExternal: options.isExternal,
503 nominalAnalysis: nominalAnalysis,
504 parentAstSymbol: parentAstSymbol,
505 rootAstSymbol: parentAstSymbol ? parentAstSymbol.rootAstSymbol : undefined
506 });
507 this._astSymbolsBySymbol.set(followedSymbol, astSymbol);
508 // Okay, now while creating the declarations we will wire them up to the
509 // their corresponding parent declarations
510 for (const declaration of followedSymbol.declarations || []) {
511 let parentAstDeclaration = undefined;
512 if (parentAstSymbol) {
513 const parentDeclaration = this._tryFindFirstAstDeclarationParent(declaration);
514 if (!parentDeclaration) {
515 throw new node_core_library_1.InternalError('Missing parent declaration');
516 }
517 parentAstDeclaration = this._astDeclarationsByDeclaration.get(parentDeclaration);
518 if (!parentAstDeclaration) {
519 throw new node_core_library_1.InternalError('Missing parent AstDeclaration');
520 }
521 }
522 const astDeclaration = new AstDeclaration_1.AstDeclaration({
523 declaration,
524 astSymbol,
525 parent: parentAstDeclaration
526 });
527 this._astDeclarationsByDeclaration.set(declaration, astDeclaration);
528 }
529 }
530 if (options.isExternal !== astSymbol.isExternal) {
531 throw new node_core_library_1.InternalError(`Cannot assign isExternal=${options.isExternal} for` +
532 ` the symbol ${astSymbol.localName} because it was previously registered` +
533 ` with isExternal=${astSymbol.isExternal}`);
534 }
535 return astSymbol;
536 }
537 /**
538 * Returns the first parent satisfying isAstDeclaration(), or undefined if none is found.
539 */
540 _tryFindFirstAstDeclarationParent(node) {
541 let currentNode = node.parent;
542 while (currentNode) {
543 if (AstDeclaration_1.AstDeclaration.isSupportedSyntaxKind(currentNode.kind)) {
544 return currentNode;
545 }
546 currentNode = currentNode.parent;
547 }
548 return undefined;
549 }
550}
551exports.AstSymbolTable = AstSymbolTable;
552//# sourceMappingURL=AstSymbolTable.js.map
\No newline at end of file