kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/typescript/indexer.ts (about)

     1  /*
     2   * Copyright 2017 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  import 'source-map-support/register';
    18  
    19  import * as fs from 'fs';
    20  import * as path from 'path';
    21  import * as ts from 'typescript';
    22  
    23  import {EdgeKind, FactName, JSONEdge, JSONFact, JSONMarkedSource, makeOrdinalEdge, MarkedSourceKind, NodeKind, OrdinalEdge, Subkind, VName} from './kythe';
    24  import * as utf8 from './utf8';
    25  import {CompilationUnit, Context, IndexerHost, IndexingOptions, Plugin, TSNamespace} from './plugin_api';
    26  
    27  const LANGUAGE = 'typescript';
    28  
    29  enum RefType {
    30    READ,
    31    WRITE,
    32    READ_WRITE
    33  }
    34  
    35  /**
    36   * toArray converts an Iterator to an array of its values.
    37   * It's necessary when running in ES5 environments where for-of loops
    38   * don't iterate through Iterators.
    39   */
    40  function toArray<T>(it: Iterator<T>): T[] {
    41    const array: T[] = [];
    42    for (let next = it.next(); !next.done; next = it.next()) {
    43      array.push(next.value);
    44    }
    45    return array;
    46  }
    47  
    48  /**
    49   * stripExtension strips the .d.ts, .ts or .tsx extension from a path.
    50   * It's used to map a file path to the module name.
    51   */
    52  function stripExtension(path: string): string {
    53    return path.replace(/\.(d\.)?tsx?$/, '');
    54  }
    55  
    56  /**
    57   * Determines if a node is a variable-like declaration.
    58   *
    59   * TODO(https://github.com/microsoft/TypeScript/issues/33115): Replace this with
    60   * a native `ts.isHasExpressionInitializer` if TypeScript ever adds it.
    61   */
    62  function hasExpressionInitializer(node: ts.Node):
    63      node is ts.HasExpressionInitializer {
    64    return ts.isVariableDeclaration(node) || ts.isParameter(node) ||
    65        ts.isBindingElement(node) || ts.isPropertySignature(node) ||
    66        ts.isPropertyDeclaration(node) || ts.isPropertyAssignment(node) ||
    67        ts.isEnumMember(node);
    68  }
    69  
    70  /**
    71   * Determines if a node is a static member of a class.
    72   */
    73  function isStaticMember(node: ts.Node, klass: ts.Declaration): boolean {
    74    return ts.isPropertyDeclaration(node) && node.parent === klass &&
    75        ((ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Static) > 0);
    76  }
    77  
    78  function todo(sourceRoot: string, node: ts.Node, message: string) {
    79    const sourceFile = node.getSourceFile();
    80    const file = path.relative(sourceRoot, sourceFile.fileName);
    81    const {line, character} =
    82        ts.getLineAndCharacterOfPosition(sourceFile, node.getStart());
    83    console.warn(`TODO: ${file}:${line}:${character}: ${message}`);
    84  }
    85  
    86  /**
    87   * Convert VName to a string that can be used as key in Maps.
    88   */
    89  function vnameToString(vname: VName): string {
    90    return `(${vname.corpus},${vname.language},${vname.path},${vname.root},${
    91        vname.signature})`;
    92  }
    93  
    94  type NamespaceAndContext = string&{__brand: 'nsctx'};
    95  /**
    96   * A SymbolVNameStore stores a mapping of symbols to the (many) VNames it may
    97   * have. Each TypeScript symbol can be be of a different TypeScript namespace
    98   * and be declared in a unique context, leading to a total (`TSNamespace` *
    99   * `Context`) number of possible VNames for the symbol.
   100   *
   101   *              TSNamespace + Context
   102   *              -----------   -------
   103   *              TYPE          Any
   104   * ts.Symbol -> VALUE         Getter  -> VName
   105   *              NAMESPACE     Setter
   106   *                            ...
   107   *
   108   * The `Any` context makes no guarantee of symbol declaration disambiguation.
   109   * As a result, unless explicitly set for a given symbol and namespace, the
   110   * VName of an `Any` context is lazily set to the VName of an arbitrary context.
   111   */
   112  class SymbolVNameStore {
   113    private readonly store =
   114        new Map<ts.Symbol, Map<NamespaceAndContext, Readonly<VName>>>();
   115  
   116    /**
   117     * Serializes a namespace and context as a string to lookup in the store.
   118     *
   119     * Each instance of a JavaScript object is unique, so using one as a key fails
   120     * because a new object would be generated every time the store is queried.
   121     */
   122    private serialize(ns: TSNamespace, context: Context): NamespaceAndContext {
   123      return `${ns}${context}` as NamespaceAndContext;
   124    }
   125  
   126    /** Get a symbol VName for a given namespace and context, if it exists. */
   127    get(symbol: ts.Symbol, ns: TSNamespace, context: Context): VName|undefined {
   128      if (this.store.has(symbol)) {
   129        const nsCtx = this.serialize(ns, context);
   130        return this.store.get(symbol)!.get(nsCtx);
   131      }
   132      return undefined;
   133    }
   134  
   135    /**
   136     * Set a symbol VName for a given namespace and context. Throws if a VName
   137     * already exists.
   138     */
   139    set(symbol: ts.Symbol, ns: TSNamespace, context: Context, vname: VName) {
   140      let vnameMap = this.store.get(symbol);
   141      const nsCtx = this.serialize(ns, context);
   142      if (vnameMap) {
   143        if (vnameMap.has(nsCtx)) {
   144          throw new Error(`VName already set with signature ${
   145              vnameMap.get(nsCtx)!.signature}`);
   146        }
   147        vnameMap.set(nsCtx, vname);
   148      } else {
   149        this.store.set(symbol, new Map([[nsCtx, vname]]));
   150      }
   151  
   152      // Set the symbol VName for the given namespace and `Any` context, if it has
   153      // not already been set.
   154      const nsAny = this.serialize(ns, Context.Any);
   155      vnameMap = this.store.get(symbol)!;
   156      if (!vnameMap.has(nsAny)) {
   157        vnameMap.set(nsAny, vname);
   158      }
   159    }
   160  }
   161  
   162  /**
   163   * StandardIndexerContext provides the standard definition of information about
   164   * a TypeScript program and common methods used by the TypeScript indexer and
   165   * its plugins. See the IndexerContext interface definition for more details.
   166   */
   167  class StandardIndexerContext implements IndexerHost {
   168    private offsetTables = new Map<string, utf8.OffsetTable>();
   169  
   170    /** A shorter name for the rootDir in the CompilerOptions. */
   171    private sourceRoot: string;
   172  
   173    /**
   174     * rootDirs is the list of rootDirs in the compiler options, sorted
   175     * longest first.  See this.moduleName().
   176     */
   177    private rootDirs: string[];
   178  
   179    /** symbolNames is a store of ts.Symbols to their assigned VNames. */
   180    private symbolNames = new SymbolVNameStore();
   181  
   182    /**
   183     * anonId increments for each anonymous block, to give them unique
   184     * signatures.
   185     */
   186    private anonId = 0;
   187  
   188    /**
   189     * anonNames maps nodes to the anonymous names assigned to them.
   190     */
   191    private anonNames = new Map<ts.Node, string>();
   192  
   193    private typeChecker: ts.TypeChecker;
   194  
   195    constructor(
   196      public readonly program: ts.Program,
   197      public readonly compilationUnit: CompilationUnit,
   198      public readonly options: IndexingOptions,
   199    ) {
   200      this.sourceRoot = this.program.getCompilerOptions().rootDir || process.cwd();
   201      let rootDirs = this.program.getCompilerOptions().rootDirs || [this.sourceRoot];
   202      rootDirs = rootDirs.map(d => d + '/');
   203      rootDirs.sort((a, b) => b.length - a.length);
   204      this.rootDirs = rootDirs;
   205      this.typeChecker = this.program.getTypeChecker();
   206    }
   207  
   208    getOffsetTable(path: string): Readonly<utf8.OffsetTable> {
   209      let table = this.offsetTables.get(path);
   210      if (!table) {
   211        const buf = (this.options.readFile || fs.readFileSync)(path);
   212        table = new utf8.OffsetTable(buf);
   213        this.offsetTables.set(path, table);
   214      }
   215      return table;
   216    }
   217  
   218    getSymbolAtLocation(node: ts.Node): ts.Symbol|undefined {
   219      // Practically any interesting node has a Symbol: variables, classes, functions.
   220      // Both named and anonymous have Symbols. We tie Symbols to Vnames so its
   221      // important to get Symbol object for as many nodes as possible. Unfortunately
   222      // Typescript doesn't provide good API for extracting Symbol from Nodes.
   223      // It is supported well for named nodes, probably logic being that if you can't
   224      // refer to a node then no need to have Symbol. But for Kythe we need to handle
   225      // anonymous nodes as well. So we do hacks here.
   226      // See similar bugs that haven't been resolved properly:
   227      // https://github.com/microsoft/TypeScript/issues/26511
   228      //
   229      // Open FR: https://github.com/microsoft/TypeScript/issues/55433
   230      let sym = this.typeChecker.getSymbolAtLocation(node);
   231      if (sym) return sym;
   232      // Check if it's named node.
   233      if ('name' in node) {
   234        sym = this.typeChecker.getSymbolAtLocation((node as ts.NamedDeclaration).name!);
   235        if (sym) return sym;
   236      }
   237      // Sad hack. Nodes have symbol property but it's not exposed in the API.
   238      // We could create our own Symbol instance to avoid depending on non-public API.
   239      // But it's not clear whether it will be less maintainance.
   240      return (node as any).symbol;
   241    }
   242  
   243    getSymbolAtLocationFollowingAliases(node: ts.Node): ts.Symbol|undefined {
   244      let sym = this.getSymbolAtLocation(node);
   245      while (sym && (sym.flags & ts.SymbolFlags.Alias) > 0) {
   246        // a hack to prevent following aliases in cases like:
   247        // import * as fooNamespace from './foo';
   248        // here fooNamespace is an alias for the 'foo' module.
   249        // We don't want to follow it so that users can easier usages
   250        // of fooNamespace in the file.
   251        const decl = sym.declarations?.[0];
   252        if (decl && ts.isNamespaceImport(decl)) {
   253          break;
   254        }
   255  
   256        sym = this.typeChecker.getAliasedSymbol(sym);
   257      }
   258      return sym;
   259    }
   260  
   261    /**
   262     * anonName assigns a freshly generated name to a Node.
   263     * It's used to give stable names to e.g. anonymous objects.
   264     */
   265    anonName(node: ts.Node): string {
   266      let name = this.anonNames.get(node);
   267      if (!name) {
   268        name = `anon${this.anonId++}`;
   269        this.anonNames.set(node, name);
   270      }
   271      return name;
   272    }
   273  
   274    /**
   275     * scopedSignature computes a scoped name for a ts.Node.
   276     * E.g. if you have a function `foo` containing a block containing a variable
   277     * `bar`, it might return a VName like
   278     *   signature: "foo.block0.bar""
   279     *   path: <appropriate path to module>
   280     */
   281    scopedSignature(startNode: ts.Node): VName {
   282      let moduleName: string|undefined;
   283      const parts: string[] = [];
   284  
   285      // Traverse the containing blocks upward, gathering names from nodes that
   286      // introduce scopes.
   287      for (let node: ts.Node|undefined = startNode,
   288                     lastNode: ts.Node|undefined = undefined;
   289           node != null; lastNode = node, node = node.parent) {
   290        // Nodes that are rvalues of a named initialization should not introduce a
   291        // new scope. For instance, in `const a = class A {}`, `A` should
   292        // contribute nothing to the scoped signature.
   293        if (node.parent && hasExpressionInitializer(node.parent) &&
   294            node.parent.name.kind === ts.SyntaxKind.Identifier) {
   295          continue;
   296        }
   297  
   298        switch (node.kind) {
   299          case ts.SyntaxKind.ExportAssignment:
   300            const exportDecl = node as ts.ExportAssignment;
   301            if (!exportDecl.isExportEquals) {
   302              // It's an "export default" statement.
   303              // This is semantically equivalent to exporting a variable
   304              // named 'default'.
   305              parts.push('default');
   306            } else {
   307              parts.push('export=');
   308            }
   309            break;
   310          case ts.SyntaxKind.ArrowFunction:
   311            // Arrow functions are anonymous, so generate a unique id.
   312            parts.push(`arrow${this.anonId++}`);
   313            break;
   314          case ts.SyntaxKind.FunctionExpression:
   315            // Function expressions look like
   316            //   (function() {})
   317            // which have no name but introduce an anonymous scope.
   318            parts.push(`func${this.anonId++}`);
   319            break;
   320          case ts.SyntaxKind.Block:
   321            // Blocks need their own scopes for contained variable declarations.
   322            if (node.parent &&
   323                (node.parent.kind === ts.SyntaxKind.FunctionDeclaration ||
   324                 node.parent.kind === ts.SyntaxKind.MethodDeclaration ||
   325                 node.parent.kind === ts.SyntaxKind.Constructor ||
   326                 node.parent.kind === ts.SyntaxKind.ForStatement ||
   327                 node.parent.kind === ts.SyntaxKind.ForInStatement ||
   328                 node.parent.kind === ts.SyntaxKind.ForOfStatement)) {
   329              // A block that's an immediate child of the above node kinds
   330              // already has a scoped name generated by that parent.
   331              // (It would be fine to not handle this specially and just fall
   332              // through to the below code, but avoiding it here makes the names
   333              // simpler.)
   334              continue;
   335            }
   336            parts.push(`block${this.anonId++}`);
   337            break;
   338          case ts.SyntaxKind.ForStatement:
   339          case ts.SyntaxKind.ForInStatement:
   340          case ts.SyntaxKind.ForOfStatement:
   341            // Introduce a naming scope for all variables declared within the
   342            // statement, so that the two 'x's declared here get different names:
   343            //   for (const x in y) { ... }
   344            //   for (const x in y) { ... }
   345            parts.push(`for${this.anonId++}`);
   346            break;
   347          case ts.SyntaxKind.BindingElement:
   348          case ts.SyntaxKind.ClassDeclaration:
   349          case ts.SyntaxKind.ClassExpression:
   350          case ts.SyntaxKind.EnumDeclaration:
   351          case ts.SyntaxKind.EnumMember:
   352          case ts.SyntaxKind.FunctionDeclaration:
   353          case ts.SyntaxKind.InterfaceDeclaration:
   354          case ts.SyntaxKind.ImportEqualsDeclaration:
   355          case ts.SyntaxKind.ImportSpecifier:
   356          case ts.SyntaxKind.ExportSpecifier:
   357          case ts.SyntaxKind.MethodDeclaration:
   358          case ts.SyntaxKind.MethodSignature:
   359          case ts.SyntaxKind.NamespaceImport:
   360          case ts.SyntaxKind.ObjectLiteralExpression:
   361          case ts.SyntaxKind.Parameter:
   362          case ts.SyntaxKind.PropertyAccessExpression:
   363          case ts.SyntaxKind.PropertyAssignment:
   364          case ts.SyntaxKind.PropertyDeclaration:
   365          case ts.SyntaxKind.PropertySignature:
   366          case ts.SyntaxKind.TypeAliasDeclaration:
   367          case ts.SyntaxKind.TypeParameter:
   368          case ts.SyntaxKind.VariableDeclaration:
   369          case ts.SyntaxKind.GetAccessor:
   370          case ts.SyntaxKind.SetAccessor:
   371          case ts.SyntaxKind.ShorthandPropertyAssignment:
   372            const decl = node as ts.NamedDeclaration;
   373            if (decl.name) {
   374              switch (decl.name.kind) {
   375                case ts.SyntaxKind.Identifier:
   376                case ts.SyntaxKind.PrivateIdentifier:
   377                case ts.SyntaxKind.StringLiteral:
   378                case ts.SyntaxKind.NumericLiteral:
   379                case ts.SyntaxKind.ComputedPropertyName:
   380                case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
   381                  let part;
   382                  if (ts.isComputedPropertyName(decl.name)) {
   383                    const sym = this.getSymbolAtLocationFollowingAliases(decl.name);
   384                    part = sym ? sym.name : this.anonName(decl.name);
   385                  } else {
   386                    part = decl.name.text;
   387                  }
   388                  // Wrap literals in quotes, so that characters used in other
   389                  // signatures do not interfere with the signature created by a
   390                  // literal. For instance, a literal
   391                  //   obj.prop
   392                  // may interefere with the signature of `prop` on an object
   393                  // `obj`. The literal receives a signature
   394                  //   "obj.prop"
   395                  // to avoid this.
   396                  if (ts.isStringLiteral(decl.name)) {
   397                    part = `"${part}"`;
   398                  }
   399                  // Instance members of a class are scoped to the type of the
   400                  // class.
   401                  if (ts.isClassDeclaration(decl) && lastNode !== undefined &&
   402                      ts.isClassElement(lastNode) &&
   403                      !isStaticMember(lastNode, decl) &&
   404                      // special case constructor. We want it to no have
   405                      // #type modifier as constructor will have the same name
   406                      // as class.
   407                      !ts.isConstructorDeclaration(startNode)) {
   408                    part += '#type';
   409                  }
   410                  // Getters and setters semantically refer to the same entities
   411                  // but are declared differently, so they are differentiated.
   412                  if (ts.isGetAccessor(decl)) {
   413                    part += ':getter';
   414                  } else if (ts.isSetAccessor(decl)) {
   415                    part += ':setter';
   416                  }
   417                  parts.push(part);
   418                  break;
   419                default:
   420                  // Skip adding an anonymous scope for variables declared in an
   421                  // array or object binding pattern like `const [a] = [0]`.
   422                  break;
   423              }
   424            } else {
   425              parts.push(this.anonName(node));
   426            }
   427            break;
   428          case ts.SyntaxKind.Constructor:
   429            // Class members declared with a shorthand in the constructor should
   430            // be scoped to the class, not the constructor.
   431            if (!ts.isParameterPropertyDeclaration(startNode, startNode.parent) &&
   432                startNode !== node) {
   433              parts.push('constructor');
   434            }
   435            break;
   436          case ts.SyntaxKind.ImportClause:
   437            // An import clause can have one of two forms:
   438            //   import foo from './bar';
   439            //   import {foo as far} from './bar';
   440            // In the first case the clause has a name "foo". In this case add the
   441            // name of the clause to the signature.
   442            // In the second case the clause has no explicit name. This
   443            // contributes nothing to the signature without risk of naming
   444            // conflicts because TS imports are essentially file-global lvalues.
   445            const importClause = node as ts.ImportClause;
   446            if (importClause.name) {
   447              parts.push(importClause.name.text);
   448            }
   449            break;
   450          case ts.SyntaxKind.ModuleDeclaration:
   451            const modDecl = node as ts.ModuleDeclaration;
   452            if (modDecl.name.kind === ts.SyntaxKind.StringLiteral) {
   453              // Syntax like:
   454              //   declare module 'foo/bar' {}
   455              // This is the syntax for defining symbols in another, named
   456              // module.
   457              moduleName = (modDecl.name as ts.StringLiteral).text;
   458            } else if (modDecl.name.kind === ts.SyntaxKind.Identifier) {
   459              // Syntax like:
   460              //   declare module foo {}
   461              // without quotes is just an obsolete way of saying 'namespace'.
   462              parts.push((modDecl.name as ts.Identifier).text);
   463            }
   464            break;
   465          case ts.SyntaxKind.SourceFile:
   466            // moduleName can already be set if the target was contained within
   467            // a "declare module 'foo/bar'" block (see the handling of
   468            // ModuleDeclaration).  Otherwise, the module name is derived from the
   469            // name of the current file.
   470            if (!moduleName) {
   471              moduleName = this.moduleName((node as ts.SourceFile).fileName);
   472            }
   473            break;
   474          case ts.SyntaxKind.JsxElement:
   475          case ts.SyntaxKind.JsxSelfClosingElement:
   476          case ts.SyntaxKind.JsxAttribute:
   477            // Given a unique anonymous name to all JSX nodes. This prevents
   478            // conflicts in cases where attributes would otherwise have the same
   479            // name, like `src` in
   480            //   <img src={a} />
   481            //   <img src={b} />
   482            parts.push(`jsx${this.anonId++}`);
   483            break;
   484          default:
   485            // Most nodes are children of other nodes that do not introduce a
   486            // new namespace, e.g. "return x;", so ignore all other parents
   487            // by default.
   488            // TODO: namespace {}, etc.
   489  
   490            // If the node is actually some subtype that has a 'name' attribute
   491            // and it's not empty it's likely this function should have handled
   492            // it. Dynamically probe for this case and warn if we missed one.
   493            if ((node as any).name != null) {
   494              todo(
   495                  this.sourceRoot, node,
   496                  `scopedSignature: ${ts.SyntaxKind[node.kind]} ` +
   497                      `has unused 'name' property`);
   498            }
   499        }
   500      }
   501  
   502      // The names were gathered from bottom to top, so reverse before joining.
   503      const signature = parts.reverse().join('.');
   504      return Object.assign(
   505          this.pathToVName(moduleName!), {signature, language: LANGUAGE});
   506    }
   507  
   508    /**
   509     * getSymbolName computes the VName of a ts.Symbol. A Context can be
   510     * optionally specified to help disambiguate nodes with multiple declarations.
   511     * See the documentation of Context for more information.
   512     */
   513    getSymbolName(
   514        sym: ts.Symbol, ns: TSNamespace, context: Context = Context.Any): VName
   515        |undefined {
   516      const stored = this.symbolNames.get(sym, ns, context);
   517      if (stored) return stored;
   518  
   519      if (!sym.declarations || sym.declarations.length < 1) {
   520        return undefined;
   521      }
   522  
   523      let declarations = sym.declarations;
   524      // Disambiguate symbols with multiple declarations using a context.
   525      if (sym.declarations.length > 1) {
   526        switch (context) {
   527          case Context.Getter:
   528            declarations = declarations.filter(ts.isGetAccessor);
   529            break;
   530          case Context.Setter:
   531            declarations = declarations.filter(ts.isSetAccessor);
   532            break;
   533          default:
   534            break;
   535        }
   536      }
   537  
   538      const decl = declarations[0];
   539      const vname = this.scopedSignature(decl);
   540      // The signature of a value is undecorated.
   541      // The signature of a type has the #type suffix.
   542      // The signature of a namespace has the #namespace suffix.
   543      if (ns === TSNamespace.TYPE) {
   544        vname.signature += '#type';
   545      } else if (ns === TSNamespace.NAMESPACE) {
   546        vname.signature += '#namespace';
   547      } else if (ns === TSNamespace.TYPE_MIGRATION) {
   548        vname.signature += '#mtype';
   549      }
   550  
   551      // Cache the VName for future lookups.
   552      this.symbolNames.set(sym, ns, context, vname);
   553      return vname;
   554    }
   555  
   556    /**
   557     * moduleName returns the ES6 module name of a path to a source file.
   558     * E.g. foo/bar.ts and foo/bar.d.ts both have the same module name,
   559     * 'foo/bar', and rootDirs (like bazel-bin/) are eliminated.
   560     * See README.md for a discussion of this.
   561     */
   562    moduleName(sourcePath: string): string {
   563      // Compute sourcePath as relative to one of the rootDirs.
   564      // This canonicalizes e.g. bazel-bin/foo to just foo.
   565      // Note that this.rootDirs is sorted longest first, so we'll use the
   566      // longest match.
   567      for (const rootDir of this.rootDirs) {
   568        if (sourcePath.startsWith(rootDir)) {
   569          sourcePath = path.relative(rootDir, sourcePath);
   570          break;
   571        }
   572      }
   573      return stripExtension(sourcePath);
   574    }
   575  
   576    /**
   577     * pathToVName returns the VName for a given file path.
   578     *
   579     * This function is used for 2 distinct cases that should be ideally separated
   580     * in 2 different functions. `path` can be one of two:
   581     * 1. Full path like 'bazel-out/genfiles/path/to/file.ts'.
   582     *    This path is used to build VNames for files and anchors.
   583     * 2. Module name like 'path/to/file'.
   584     *    This path is used to build VNames for semantic nodes.
   585     *
   586     * Only for full paths `pathVnames` contains an entry. For short paths (module
   587     * names) this function will defaults to calculating vname based on path
   588     * and compilation unit.
   589     */
   590    pathToVName(path: string): VName {
   591      const vname = this.compilationUnit.fileVNames.get(path);
   592      return {
   593        signature: '',
   594        language: '',
   595        corpus: vname && vname.corpus ? vname.corpus :
   596                                        this.compilationUnit.rootVName.corpus,
   597        root: vname && vname.corpus ? vname.root : this.compilationUnit.rootVName.root,
   598        path: vname && vname.path ? vname.path : path,
   599      };
   600    }
   601  }
   602  
   603  const RE_FIRST_NON_WS = /\S|$/;
   604  const RE_NEWLINE = /\r?\n/;
   605  const MAX_MS_TEXT_LENGTH = 1_000;
   606  /**
   607   * Formats a MarkedSource text component by
   608   * - stripping the text to its first MAX_MS_TEXT_LENGTH characters
   609   * - trimming the text
   610   * - stripping the leading whitespace of each line in multi-line string by the
   611   *   shortest non-zero whitespace length. For example,
   612   *   [
   613   *       1,
   614   *       2,
   615   *     ]
   616   *   becomes
   617   *   [
   618   *     1,
   619   *     2,
   620   *   ]
   621   */
   622  function fmtMarkedSource(s: string) {
   623    if (s.search(RE_FIRST_NON_WS) === s.length) {
   624      // String is all whitespace, keep as-is
   625      return s;
   626    }
   627    let isChopped = false;
   628    if (s.length > MAX_MS_TEXT_LENGTH) {
   629      // Trim left first to pick up more chars before chopping the string
   630      s = s.trimLeft();
   631      s = s.substring(0, MAX_MS_TEXT_LENGTH);
   632      isChopped = true;
   633    }
   634    s = s.replace(/\t/g, '    ');  // normalize tabs for display
   635    const lines = s.split(RE_NEWLINE);
   636    let shortestLeading = lines[lines.length - 1].search(RE_FIRST_NON_WS);
   637    for (let i = 1; i < lines.length - 1; ++i) {
   638      shortestLeading =
   639          Math.min(shortestLeading, lines[i].search(RE_FIRST_NON_WS));
   640    }
   641    for (let i = 1; i < lines.length; ++i) {
   642      lines[i] = lines[i].substring(shortestLeading);
   643    }
   644    s = lines.join('\n');
   645    if (isChopped) {
   646      s += '...';
   647    }
   648    return s;
   649  }
   650  
   651  function isNonNullableArray<T>(arr: Array<T>): arr is Array<NonNullable<T>> {
   652    return arr.findIndex(el => el === undefined || el === null) === -1;
   653  }
   654  
   655  /** Visitor manages the indexing process for a single TypeScript SourceFile. */
   656  class Visitor {
   657    /** kFile is the VName for the 'file' node representing the source file. */
   658    kFile: VName;
   659  
   660    /** A shorter name for the rootDir in the CompilerOptions. */
   661    sourceRoot: string;
   662  
   663    typeChecker: ts.TypeChecker;
   664  
   665    /** influencers is a stack of influencer VNames. */
   666    private readonly influencers: Set<VName>[] = [];
   667  
   668    /** Cached anchor nodes. Signature is used as key. */
   669    private readonly anchors = new Map<string, VName>();
   670  
   671    constructor(
   672        private readonly host: IndexerHost,
   673        private file: ts.SourceFile,
   674    ) {
   675      this.sourceRoot =
   676          this.host.program.getCompilerOptions().rootDir || process.cwd();
   677  
   678      this.typeChecker = this.host.program.getTypeChecker();
   679  
   680      this.kFile = this.newFileVName(file.fileName);
   681    }
   682  
   683    /**
   684     * newFileVName returns a new VName for the given file path.
   685     */
   686    newFileVName(path: string): VName {
   687      return this.host.pathToVName(path);
   688    }
   689  
   690    /**
   691     * newVName returns a new VName with a given signature and path.
   692     */
   693    newVName(signature: string, path: string): VName {
   694      return Object.assign(
   695          this.newFileVName(path), {signature: signature, language: LANGUAGE});
   696    }
   697  
   698    /** newAnchor emits a new anchor entry that covers a TypeScript node. */
   699    newAnchor(node: ts.Node, start = node.getStart(), end = node.end, tag = ''):
   700        VName {
   701      const signature = `@${tag}${start}:${end}`;
   702      const cachedName = this.anchors.get(signature);
   703      if (cachedName != null) return cachedName;
   704  
   705      const name =
   706          Object.assign({...this.kFile}, {signature, language: LANGUAGE});
   707      this.emitNode(name, NodeKind.ANCHOR);
   708      const offsetTable = this.host.getOffsetTable(node.getSourceFile().fileName);
   709      this.emitFact(
   710          name, FactName.LOC_START, offsetTable.lookupUtf8(start).toString());
   711      this.emitFact(
   712          name, FactName.LOC_END, offsetTable.lookupUtf8(end).toString());
   713      this.anchors.set(signature, name);
   714      return name;
   715    }
   716  
   717    /** emitNode emits a new node entry, declaring the kind of a VName. */
   718    emitNode(source: VName, kind: NodeKind) {
   719      this.emitFact(source, FactName.NODE_KIND, kind);
   720    }
   721  
   722    /** emitSubkind emits a new fact entry, declaring the subkind of a VName. */
   723    emitSubkind(source: VName, subkind: Subkind) {
   724      this.emitFact(source, FactName.SUBKIND, subkind);
   725    }
   726  
   727    /** emitFact emits a new fact entry, tying an attribute to a VName. */
   728    emitFact(source: VName, name: FactName, value: string|Uint8Array) {
   729      this.host.options.emit({
   730        source,
   731        fact_name: name,
   732        fact_value: Buffer.from(value).toString('base64'),
   733      });
   734    }
   735  
   736    /** emitEdge emits a new edge entry, relating two VNames. */
   737    emitEdge(source: VName, kind: EdgeKind|OrdinalEdge, target: VName) {
   738      this.host.options.emit({
   739        source,
   740        edge_kind: kind,
   741        target,
   742        fact_name: '/',
   743      });
   744    }
   745  
   746    /** forAllInfluencers executes fn for each influencer in the active set. */
   747    forAllInfluencers(fn: (influencer: VName) => void) {
   748      if (this.influencers.length != 0) {
   749        this.influencers[this.influencers.length - 1].forEach(fn);
   750      }
   751    }
   752  
   753    /** addInfluencer adds influencer to the active set. */
   754    addInfluencer(influencer: VName) {
   755      if (this.influencers.length != 0) {
   756        this.influencers[this.influencers.length - 1].add(influencer);
   757      }
   758    }
   759  
   760    /** popInfluencers pops the active influencer set. */
   761    popInfluencers() {
   762      if (this.influencers.length != 0) {
   763        this.influencers.pop();
   764      }
   765    }
   766  
   767    /** pushInfluencers pushes a new active influencer set. */
   768    pushInfluencers() {
   769      this.influencers.push(new Set<VName>());
   770    }
   771  
   772    visitTypeParameters(
   773        parent: VName|undefined,
   774        params: ReadonlyArray<ts.TypeParameterDeclaration>) {
   775      for (var ordinal = 0; ordinal < params.length; ++ordinal) {
   776        const param = params[ordinal];
   777        const sym = this.host.getSymbolAtLocationFollowingAliases(param.name);
   778        if (!sym) {
   779          todo(
   780              this.sourceRoot, param,
   781              `type param ${param.getText()} has no symbol`);
   782          return;
   783        }
   784        const kTVar = this.host.getSymbolName(sym, TSNamespace.TYPE_MIGRATION);
   785        if (kTVar && parent) {
   786          this.emitNode(kTVar, NodeKind.TVAR);
   787          this.emitEdge(
   788              this.newAnchor(
   789                  param.name, param.name.getStart(), param.name.end, 'M'),
   790              EdgeKind.DEFINES_BINDING, kTVar);
   791          this.emitEdge(parent, makeOrdinalEdge(EdgeKind.TPARAM, ordinal), kTVar);
   792          // ...<T extends A>
   793          if (param.constraint) {
   794            var superType = this.visitType(param.constraint);
   795            if (superType)
   796              this.emitEdge(kTVar, EdgeKind.BOUNDED_UPPER, superType);
   797          }
   798          // ...<T = A>
   799          if (param.default) this.visitType(param.default);
   800        }
   801      }
   802    }
   803  
   804    /**
   805     * Adds influence edges for return statements.
   806     */
   807    visitReturnStatement(node: ts.ReturnStatement) {
   808      this.pushInfluencers();
   809      ts.forEachChild(node, n => {
   810        this.visit(n);
   811      });
   812      const containingFunction = this.getContainingFunctionNode(node);
   813      if (!ts.isSourceFile(containingFunction)) {
   814        const containingVName =
   815            this.getSymbolAndVNameForFunctionDeclaration(containingFunction)
   816                .vname;
   817        if (containingVName) {
   818          this.forAllInfluencers(influencer => {
   819            this.emitEdge(influencer, EdgeKind.INFLUENCES, containingVName);
   820          });
   821        }
   822        // Handle case like
   823        // "return {name: 'Alice'};"
   824        // where return type of the function is a named type, e.g. Person.
   825        // This will connect 'name' property of the object literal to the
   826        // Person.name property.
   827        if (node.expression && ts.isObjectLiteralExpression(node.expression)) {
   828          this.connectObjectLiteralToType(
   829              node.expression, containingFunction.type);
   830        }
   831      }
   832      this.popInfluencers();
   833    }
   834  
   835    getCallAnchor(callee:any) {
   836      if (!this.host.options.emitRefCallOverIdentifier) {
   837        return undefined;
   838      }
   839      for (;;) {
   840        if (ts.isIdentifier(callee)) {
   841          return this.newAnchor(callee);
   842        }
   843        if (ts.isPropertyAccessExpression(callee)) {
   844          callee = callee.name;
   845          continue;
   846        }
   847        if (ts.isNewExpression(callee)) {
   848          callee = callee.expression;
   849          continue;
   850        }
   851        return undefined;
   852      }
   853    }
   854  
   855    /**
   856     * Emits `ref/call` edges required for call graph:
   857     * https://kythe.io/docs/schema/callgraph.html
   858     */
   859    visitCallOrNewExpression(node: ts.CallExpression|ts.NewExpression) {
   860      ts.forEachChild(node, n => {
   861        this.visit(n);
   862      });
   863  
   864      // Special case dynamic imports as they are represendted as CallExpressions.
   865      // We don't want to emit ref/call as we don't have anything to point it at:
   866      // there is no import() function
   867      if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
   868        this.visitDynamicImportCall(node);
   869        return;
   870      }
   871      // Handle case of immediately-invoked functions like:
   872      // (() => do stuff... )();
   873      let expression: ts.Node = node.expression;
   874      while (ts.isParenthesizedExpression(expression)) {
   875        expression = expression.expression;
   876      }
   877      const symbol = this.host.getSymbolAtLocationFollowingAliases(expression);
   878      if (!symbol) {
   879        return;
   880      }
   881      const name = this.host.getSymbolName(symbol, TSNamespace.VALUE);
   882      if (!name) {
   883        return;
   884      }
   885      const callAnchor = this.getCallAnchor(node.expression) ?? this.newAnchor(node);
   886      this.emitEdge(callAnchor, EdgeKind.REF_CALL, name);
   887  
   888      // Each call should have a childof edge to its containing function
   889      // scope.
   890      const containingFunction = this.getContainingFunctionNode(node);
   891      let containingVName: VName|undefined;
   892      if (ts.isSourceFile(containingFunction)) {
   893        containingVName = this.getSyntheticFileInitVName();
   894      } else {
   895        containingVName =
   896            this.getSymbolAndVNameForFunctionDeclaration(containingFunction)
   897                .vname;
   898      }
   899      if (containingVName) {
   900        this.emitEdge(callAnchor, EdgeKind.CHILD_OF, containingVName);
   901      }
   902      // Handle function/constructor calls like `doSomething({name: 'Alice'})`
   903      // where type of the argument is, for example, an interface Person. This
   904      // will add ref from object literal 'name' property to Person.name field.
   905      if (node.arguments != null) {
   906        const signature = this.typeChecker.getResolvedSignature(node);
   907        if (signature == null) return;
   908        for (let i = 0; i < node.arguments.length; i++) {
   909          const argument = node.arguments[i];
   910          // get parameter of the function/constructor signature.
   911          const signParameter = signature.parameters[i]?.valueDeclaration;
   912          if (ts.isObjectLiteralExpression(argument) && signParameter &&
   913              ts.isParameter(signParameter)) {
   914            this.connectObjectLiteralToType(argument, signParameter.type);
   915          }
   916        }
   917      }
   918    }
   919  
   920    /**
   921     * visitHeritage visits the X found in an 'extends X' or 'implements X'.
   922     *
   923     * These are subtle in an interesting way.  When you have
   924     *   interface X extends Y {}
   925     * that is referring to the *type* Y (because interfaces are types, not
   926     * values).  But it's also legal to write
   927     *   class X extends (class Z { ... }) {}
   928     * where the thing in the extends clause is itself an expression, and the
   929     * existing logic for visiting a class expression already handles modelling
   930     * the class as both a type and a value.
   931     *
   932     * The full set of possible combinations is:
   933     * - class extends => value
   934     * - interface extends => type
   935     * - class implements => type
   936     * - interface implements => illegal
   937     */
   938    visitHeritage(
   939        classOrInterface: VName|undefined,
   940        heritageClauses: ReadonlyArray<ts.HeritageClause>) {
   941      for (const heritage of heritageClauses) {
   942        if (heritage.token === ts.SyntaxKind.ExtendsKeyword && heritage.parent &&
   943            heritage.parent.kind !== ts.SyntaxKind.InterfaceDeclaration) {
   944          this.visit(heritage);
   945        } else {
   946          this.visitType(heritage);
   947        }
   948        // Add extends edges.
   949        if (classOrInterface == null) {
   950          // classOrInterface is null for anonymous classes.
   951          // But anonymous classes can implement and extends other
   952          // classes and interfaces. So currently this edge
   953          // is missing. Once we have nodes for anonymous classes -
   954          // we can add this missing edges.
   955          continue;
   956        }
   957        for (const baseType of heritage.types) {
   958          const type = this.typeChecker.getTypeAtLocation(baseType);
   959          if (!type || !type.symbol) {
   960            continue;
   961          }
   962          const vname = this.host.getSymbolName(type.symbol, TSNamespace.TYPE);
   963          if (vname) {
   964            this.emitEdge(classOrInterface, EdgeKind.EXTENDS, vname);
   965          }
   966        }
   967      }
   968    }
   969  
   970    visitInterfaceDeclaration(decl: ts.InterfaceDeclaration) {
   971      const sym = this.host.getSymbolAtLocationFollowingAliases(decl.name);
   972      if (!sym) {
   973        todo(
   974            this.sourceRoot, decl.name,
   975            `interface ${decl.name.getText()} has no symbol`);
   976        return;
   977      }
   978      const kType = this.host.getSymbolName(sym, TSNamespace.TYPE);
   979      if (kType) {
   980        this.emitNode(kType, NodeKind.INTERFACE);
   981        this.emitEdge(this.newAnchor(decl.name), EdgeKind.DEFINES_BINDING, kType);
   982        this.visitJSDoc(decl, kType);
   983        this.emitMarkedSourceForClasslikeDeclaration(decl, kType);
   984      }
   985  
   986      if (decl.typeParameters)
   987        this.visitTypeParameters(kType, decl.typeParameters);
   988      if (decl.heritageClauses) this.visitHeritage(kType, decl.heritageClauses);
   989      for (const member of decl.members) {
   990        this.visit(member);
   991      }
   992    }
   993  
   994    visitTypeAliasDeclaration(decl: ts.TypeAliasDeclaration) {
   995      const sym = this.host.getSymbolAtLocationFollowingAliases(decl.name);
   996      if (!sym) {
   997        todo(
   998            this.sourceRoot, decl.name,
   999            `type alias ${decl.name.getText()} has no symbol`);
  1000        return;
  1001      }
  1002      const kType = this.host.getSymbolName(sym, TSNamespace.TYPE);
  1003      if (!kType) return;
  1004      this.emitNode(kType, NodeKind.TALIAS);
  1005      this.emitEdge(this.newAnchor(decl.name), EdgeKind.DEFINES_BINDING, kType);
  1006      this.visitJSDoc(decl, kType);
  1007  
  1008      if (decl.typeParameters)
  1009        this.visitTypeParameters(kType, decl.typeParameters);
  1010      const kAlias = this.visitType(decl.type);
  1011      // Emit an "aliases" edge if the aliased type has a singular VName.
  1012      if (kAlias) {
  1013        this.emitEdge(kType, EdgeKind.ALIASES, kAlias);
  1014      }
  1015    }
  1016  
  1017    /**
  1018     * visitType is the main dispatch for visiting type nodes.
  1019     * It's separate from visit() because bare ts.Identifiers within a normal
  1020     * expression are values (handled by visit) but bare ts.Identifiers within
  1021     * a type are types (handled here).
  1022     *
  1023     * @return the VName of the type, if available.  (For more complex types,
  1024     *    e.g. Array<string>, we might not have a VName for the specific type.)
  1025     */
  1026    visitType(node: ts.Node): VName|undefined {
  1027      switch (node.kind) {
  1028        case ts.SyntaxKind.TypeReference:
  1029          const ref = node as ts.TypeReferenceNode;
  1030          const kType = this.visitType((node as ts.TypeReferenceNode).typeName);
  1031  
  1032          // Return no VName for types with type arguments because their VName is
  1033          // not qualified. E.g.
  1034          //   Array<string>
  1035          //   Array<number>
  1036          // have the same VName signature "Array"
  1037          if (ref.typeArguments) {
  1038            ref.typeArguments.forEach(type => this.visitType(type));
  1039            return;
  1040          }
  1041          return kType;
  1042        case ts.SyntaxKind.Identifier:
  1043          const sym = this.host.getSymbolAtLocationFollowingAliases(node);
  1044          if (!sym) {
  1045            todo(this.sourceRoot, node, `type ${node.getText()} has no symbol`);
  1046            return;
  1047          }
  1048          const name = this.host.getSymbolName(sym, TSNamespace.TYPE);
  1049          if (name) {
  1050            this.emitEdge(this.newAnchor(node), EdgeKind.REF, name);
  1051          }
  1052          const mname = this.host.getSymbolName(sym, TSNamespace.TYPE_MIGRATION);
  1053          if (mname) {
  1054            this.emitEdge(
  1055                this.newAnchor(node, node.getStart(), node.end, 'M'),
  1056                EdgeKind.REF, mname);
  1057          }
  1058          return name;
  1059        case ts.SyntaxKind.TypeReference:
  1060          const typeRef = node as ts.TypeReferenceNode;
  1061          if (!typeRef.typeArguments) {
  1062            // If it's an direct type reference, e.g. SomeInterface
  1063            // as opposed to SomeInterface<T>, then the VName for the type
  1064            // reference is just the inner type name.
  1065            return this.visitType(typeRef.typeName);
  1066          }
  1067          // Otherwise, leave it to the default handling.
  1068          break;
  1069        case ts.SyntaxKind.TypeQuery:
  1070          // This is a 'typeof' expression, which takes a value as its argument,
  1071          // so use visit() instead of visitType().
  1072          const typeQuery = node as ts.TypeQueryNode;
  1073          this.visit(typeQuery.exprName);
  1074          return;  // Avoid default recursion.
  1075      }
  1076  
  1077      // Default recursion, but using visitType(), not visit().
  1078      ts.forEachChild(node, n => {
  1079        this.visitType(n);
  1080      });
  1081      // Because we don't know the specific thing we visited, give the caller
  1082      // back no name.
  1083      return undefined;
  1084    }
  1085  
  1086    /**
  1087     * getPathFromModule gets the "module path" from the module import
  1088     * symbol referencing a module system path to reference to a module.
  1089     *
  1090     * E.g. from
  1091     *   import ... from './foo';
  1092     * getPathFromModule(the './foo' node) might return a string like
  1093     * 'path/to/project/foo'.  See this.moduleName().
  1094     */
  1095    getModulePathFromModuleReference(sym: ts.Symbol): string|undefined {
  1096      const sf = sym.valueDeclaration;
  1097      // If the module is not a source file, it does not have a unique file path.
  1098      // This can happen in cases of importing local modules, like
  1099      //   declare namespace Foo {}
  1100      //   import foo = Foo;
  1101      if (!sf || !ts.isSourceFile(sf)) return undefined;
  1102      return this.host.moduleName(sf.fileName);
  1103    }
  1104  
  1105    /**
  1106     * Returns the location of a text in the source code of a node.
  1107     */
  1108    getTextSpan(node: ts.Node, text: string): {start: number, end: number} {
  1109      const ofs = node.getText().indexOf(text);
  1110      if (ofs < 0) throw new Error(`${text} not found in ${node.getText()}`);
  1111      const start = node.getStart() + ofs;
  1112      const end = start + text.length;
  1113      return {start, end};
  1114    }
  1115  
  1116    /**
  1117     * Returns the symbol of a class constructor if it exists, otherwise nothing.
  1118     */
  1119    getCtorSymbol(klass: ts.ClassDeclaration): ts.Symbol|undefined {
  1120      if (klass.name) {
  1121        const sym = this.host.getSymbolAtLocationFollowingAliases(klass.name);
  1122        if (sym && sym.members) {
  1123          return sym.members.get(ts.InternalSymbolName.Constructor);
  1124        }
  1125      }
  1126      return undefined;
  1127    }
  1128  
  1129    getSymbolAndVNameForFunctionDeclaration(node: ts.FunctionLikeDeclaration):
  1130        {sym?: ts.Symbol, vname?: VName} {
  1131      let context: Context|undefined = undefined;
  1132      if (ts.isGetAccessor(node)) {
  1133        context = Context.Getter;
  1134      } else if (ts.isSetAccessor(node)) {
  1135        context = Context.Setter;
  1136      }
  1137      const sym = this.host.getSymbolAtLocationFollowingAliases(node);
  1138      if (!sym) {
  1139        return {};
  1140      }
  1141      const vname = this.host.getSymbolName(sym, TSNamespace.VALUE, context);
  1142      return {sym, vname};
  1143    }
  1144  
  1145    /**
  1146     * Given a node finds a function node that contains given node and returns it.
  1147     * If the node is not inside a function - returns SourceFile node. Examples:
  1148     *
  1149     * function foo() {
  1150     *   var b = 123;
  1151     * }
  1152     * var c = 567;
  1153     *
  1154     * For 'b' node this function will return 'foo' node.
  1155     * For 'c' node this function will return SourceFile node.
  1156     */
  1157    getContainingFunctionNode(node: ts.Node): ts.FunctionLikeDeclaration
  1158        |ts.SourceFile {
  1159      node = node.parent;
  1160      for (; node.kind !== ts.SyntaxKind.SourceFile; node = node.parent) {
  1161        const kind = node.kind;
  1162        if (kind === ts.SyntaxKind.FunctionDeclaration ||
  1163            kind === ts.SyntaxKind.FunctionExpression ||
  1164            kind === ts.SyntaxKind.ArrowFunction ||
  1165            kind === ts.SyntaxKind.MethodDeclaration ||
  1166            kind === ts.SyntaxKind.Constructor ||
  1167            kind === ts.SyntaxKind.GetAccessor ||
  1168            kind === ts.SyntaxKind.SetAccessor ||
  1169            kind === ts.SyntaxKind.MethodSignature) {
  1170          return node as ts.FunctionLikeDeclaration;
  1171        }
  1172      }
  1173      return node as ts.SourceFile;
  1174    }
  1175  
  1176    getSyntheticFileInitVName(): VName {
  1177      return this.newVName(
  1178          'fileInit:synthetic', this.host.moduleName(this.file.fileName));
  1179    }
  1180  
  1181    /**
  1182     * visitImport handles a single entry in an import statement, e.g.
  1183     * "bar" in code like
  1184     *   import {foo, bar} from 'baz';
  1185     * See visitImportDeclaration for the code handling the entire statement.
  1186     *
  1187     * @param name TypeScript import declaration node
  1188     * @param bindingAnchor anchor that "defines/binding" the local import
  1189     *     definition. If the bindingAnchor is null then the local definition
  1190     *     won't be created and refs to the local definition will be reassigned
  1191     *     to the remote definition.
  1192     * @param refAnchor anchor that "ref" the import's remote declaration
  1193     */
  1194    visitImport(
  1195        name: ts.Node, bindingAnchor: Readonly<VName>|null,
  1196        refAnchor: Readonly<VName>) {
  1197      // An import both aliases a symbol from another module
  1198      // (call it the remote symbol) and it defines a local symbol.
  1199      //
  1200      // Those two symbols often have the same name, with statements like:
  1201      //   import {foo} from 'bar';
  1202      // But they can be different, in e.g.
  1203      //   import {foo as baz} from 'bar';
  1204      // Which maps the remote symbol named 'foo' to a local named 'baz'.
  1205      //
  1206      // In all cases TypeScript maintains two different ts.Symbol objects,
  1207      // one for the local and one for the remote.  In principle for the
  1208      // simple import statement
  1209      //   import {foo} from 'bar';
  1210      // "foo" should both:
  1211      //   - "ref/imports" the remote symbol
  1212      //   - "defines/binding" the local symbol
  1213  
  1214      const localSym = this.host.getSymbolAtLocation(name);
  1215      if (!localSym) {
  1216        throw new Error(`TODO: local name ${name} has no symbol`);
  1217      }
  1218      const remoteSym = this.typeChecker.getAliasedSymbol(localSym);
  1219  
  1220      // The imported symbol can refer to a type, a value, or both. Attempt to
  1221      // define local imports and reference remote definitions in both cases.
  1222      if (remoteSym.flags & ts.SymbolFlags.Value) {
  1223        const kRemoteValue =
  1224            this.host.getSymbolName(remoteSym, TSNamespace.VALUE);
  1225        const kLocalValue = this.host.getSymbolName(localSym, TSNamespace.VALUE);
  1226        if (!kRemoteValue || !kLocalValue) return;
  1227  
  1228        if (bindingAnchor) {
  1229          // The local import value is a "variable" with an "import" subkind, and
  1230          // aliases its remote definition.
  1231          this.emitNode(kLocalValue, NodeKind.VARIABLE);
  1232          this.emitFact(kLocalValue, FactName.SUBKIND, Subkind.IMPORT);
  1233          this.emitEdge(kLocalValue, EdgeKind.ALIASES, kRemoteValue);
  1234          this.emitEdge(bindingAnchor, EdgeKind.DEFINES_BINDING, kLocalValue);
  1235        }
  1236        // Emit edges from the referencing anchor to the import's remote definition.
  1237        this.emitEdge(refAnchor, EdgeKind.REF_IMPORTS, kRemoteValue);
  1238      }
  1239      if (remoteSym.flags & ts.SymbolFlags.Type) {
  1240        const kRemoteType = this.host.getSymbolName(remoteSym, TSNamespace.TYPE);
  1241        const kLocalType = this.host.getSymbolName(localSym, TSNamespace.TYPE);
  1242        if (!kRemoteType || !kLocalType) return;
  1243  
  1244        if (bindingAnchor) {
  1245          // The local import value is a "talias" (type alias) with an "import"
  1246          // subkind, and aliases its remote definition.
  1247          this.emitNode(kLocalType, NodeKind.TALIAS);
  1248          this.emitFact(kLocalType, FactName.SUBKIND, Subkind.IMPORT);
  1249          this.emitEdge(kLocalType, EdgeKind.ALIASES, kRemoteType);
  1250          this.emitEdge(bindingAnchor, EdgeKind.DEFINES_BINDING, kLocalType);
  1251        }
  1252        // Emit edges from the referencing anchor to the import's remote definition.
  1253        this.emitEdge(refAnchor, EdgeKind.REF_IMPORTS, kRemoteType);
  1254      }
  1255    }
  1256  
  1257    /**
  1258     * Emits `ref/import` edge from a module name to its definition.
  1259     * It is used by static and dynamic imports:
  1260     *
  1261     * import {Bar} from './foo'; // static
  1262     *
  1263     * const {Bar} = await import('./foo'); // dynamic
  1264     *
  1265     * @param moduleRef Module reference. E.g. './foo' from the examples above.
  1266     * @returns true if corresponding module was found and an edge was emitted.
  1267     */
  1268    emitRefToImportedModule(moduleRef: ts.Node): boolean {
  1269      const moduleSym = this.host.getSymbolAtLocation(moduleRef);
  1270      if (!moduleSym) {
  1271        // This can occur when the module failed to resolve to anything.
  1272        // See testdata/import_missing.ts for more on how that could happen.
  1273        return false;
  1274      }
  1275      const modulePath = this.getModulePathFromModuleReference(moduleSym);
  1276      if (modulePath) {
  1277        const kModule = this.newVName('module', modulePath);
  1278        this.emitEdge(this.newAnchor(moduleRef), EdgeKind.REF_IMPORTS, kModule);
  1279      } else {
  1280        // Check if module being imported is declared via `declare module`
  1281        // and if so - output ref to that statement.
  1282        const decl = moduleSym.valueDeclaration;
  1283        if (decl && ts.isModuleDeclaration(decl)) {
  1284          const kModule =
  1285              this.host.getSymbolName(moduleSym, TSNamespace.NAMESPACE);
  1286          if (!kModule) return false;
  1287          this.emitEdge(this.newAnchor(moduleRef), EdgeKind.REF_IMPORTS, kModule);
  1288        }
  1289      }
  1290      return true;
  1291    }
  1292  
  1293    /** visitImportDeclaration handles the various forms of "import ...". */
  1294    visitImportDeclaration(decl: ts.ImportDeclaration|
  1295                           ts.ImportEqualsDeclaration) {
  1296      // All varieties of import statements reference a module on the right,
  1297      // so start by linking that.
  1298      let moduleRef;
  1299      if (ts.isImportDeclaration(decl)) {
  1300        // This is a regular import declaration
  1301        //     import ... from ...;
  1302        // where the module name is moduleSpecifier.
  1303        moduleRef = decl.moduleSpecifier;
  1304      } else {
  1305        // This is an import equals declaration, which has two cases:
  1306        //     import foo = require('./bar');
  1307        //     import foo = M.bar;
  1308        // In the first case the moduleReference is an ExternalModuleReference
  1309        // whose module name is the expression inside the `require` call.
  1310        // In the second case the moduleReference is the module name.
  1311        moduleRef = ts.isExternalModuleReference(decl.moduleReference) ?
  1312            decl.moduleReference.expression :
  1313            decl.moduleReference;
  1314      }
  1315      const moduleFound = this.emitRefToImportedModule(moduleRef);
  1316      if (!moduleFound) {
  1317        return;
  1318      }
  1319  
  1320      // TODO(#4021): See discussion.
  1321      // Pending changes, an anchor in a Code Search UI cannot currently be
  1322      // displayed as a node definition and as referencing other nodes.  Instead,
  1323      // for non-renamed imports the local node definition is placed on the
  1324      // "import" text:
  1325      //   //- @foo ref BarFoo
  1326      //   //- @import defines/binding LocalFoo
  1327      //   //- LocalFoo aliases BarFoo
  1328      //   import {foo} from 'bar';
  1329      // For renamed imports the definition and references are separated as
  1330      // expected:
  1331      //   //- @foo ref BarFoo
  1332      //   //- @baz defines/binding LocalBaz
  1333      //   //- @baz aliases BarFoo
  1334      //   import {foo as baz} from 'bar';
  1335      //
  1336      // Create an anchor for the import text.
  1337      const importTextSpan = this.getTextSpan(decl, 'import');
  1338      const importTextAnchor =
  1339          this.newAnchor(decl, importTextSpan.start, importTextSpan.end);
  1340  
  1341      if (ts.isImportEqualsDeclaration(decl)) {
  1342        // This is an equals import, e.g.:
  1343        //   import foo = require('./bar');
  1344        //
  1345        // TODO(#4021): Bind the local definition and reference the remote
  1346        // definition on the import name.
  1347        const refAnchor = this.newAnchor(decl.name);
  1348        this.visitImport(decl.name, /* bindingAnchor= */ null, refAnchor);
  1349        return;
  1350      }
  1351  
  1352      if (!decl.importClause) {
  1353        // This is a side-effecting import that doesn't declare anything, e.g.:
  1354        //   import 'foo';
  1355        return;
  1356      }
  1357      const clause = decl.importClause;
  1358  
  1359      if (clause.name) {
  1360        // This is a default import, e.g.:
  1361        //   import foo from './bar';
  1362        //
  1363        // TODO(#4021): Bind the local definition and reference the remote
  1364        // definition on the import name.
  1365        const refAnchor = this.newAnchor(clause.name);
  1366        this.visitImport(clause.name, /* bindingAnchor= */ null, refAnchor);
  1367        return;
  1368      }
  1369  
  1370      if (!clause.namedBindings) {
  1371        // TODO: I believe clause.name or clause.namedBindings are always present,
  1372        // which means this check is not necessary, but the types don't show that.
  1373        throw new Error(`import declaration ${decl.getText()} has no bindings`);
  1374      }
  1375      switch (clause.namedBindings.kind) {
  1376        case ts.SyntaxKind.NamespaceImport:
  1377          // This is a namespace import, e.g.:
  1378          //   import * as foo from 'foo';
  1379          const name = clause.namedBindings.name;
  1380          const sym = this.host.getSymbolAtLocation(name);
  1381          if (!sym) {
  1382            todo(
  1383                this.sourceRoot, clause,
  1384                `namespace import ${clause.getText()} has no symbol`);
  1385            return;
  1386          }
  1387          const kModuleObject = this.host.getSymbolName(sym, TSNamespace.VALUE);
  1388          if (!kModuleObject) return;
  1389          this.emitNode(kModuleObject, NodeKind.VARIABLE);
  1390          this.emitEdge(
  1391              this.newAnchor(name), EdgeKind.DEFINES_BINDING, kModuleObject);
  1392          break;
  1393        case ts.SyntaxKind.NamedImports:
  1394          // This is named imports, e.g.:
  1395          //   import {bar, baz} from 'foo';
  1396          const imports = clause.namedBindings.elements;
  1397          for (const imp of imports) {
  1398            // If the named import has a property name, e.g. `bar` in
  1399            //   import {bar as baz} from 'foo';
  1400            // bind the local definition on the import name "baz" and reference
  1401            // the remote definition on the property name "bar". Otherwise, bind
  1402            // the local definition on "import" and reference the remote
  1403            // definition on the import name.
  1404            //
  1405            // TODO(#4021): Unify binding and reference anchors.
  1406            let bindingAnchor, refAnchor;
  1407            if (imp.propertyName) {
  1408              bindingAnchor = this.newAnchor(imp.name);
  1409              refAnchor = this.newAnchor(imp.propertyName);
  1410            } else {
  1411              refAnchor = this.newAnchor(imp.name);
  1412              bindingAnchor = null;
  1413            }
  1414            this.visitImport(imp.name, bindingAnchor, refAnchor);
  1415          }
  1416          break;
  1417      }
  1418    }
  1419  
  1420    /**
  1421     * Handles dynamic imports like:
  1422     *
  1423     * const {SomeSym} = await import('./another/file');
  1424     *
  1425     * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
  1426     *
  1427     * @param node import() statement. TS represents dynamic imports as CallExpressions.
  1428     */
  1429    visitDynamicImportCall(node: ts.CallExpression) {
  1430      const moduleRef = node.arguments[0];
  1431      this.emitRefToImportedModule(moduleRef);
  1432    }
  1433  
  1434    /**
  1435     * When a file imports another file, with syntax like
  1436     *   import * as x from 'some/path';
  1437     * we wants 'some/path' to refer to a VName that just means "the entire
  1438     * file".  It doesn't refer to any text in particular, so we just mark
  1439     * the first letter in the file as the anchor for this.
  1440     */
  1441    emitModuleAnchor(sf: ts.SourceFile) {
  1442      const kMod =
  1443          this.newVName('module', this.host.moduleName(this.file.fileName));
  1444      this.emitFact(kMod, FactName.NODE_KIND, NodeKind.RECORD);
  1445      this.emitEdge(this.kFile, EdgeKind.CHILD_OF, kMod);
  1446  
  1447      // Emit the anchor, bound to the beginning of the file.
  1448      const anchor = this.newAnchor(this.file, 0, 0);
  1449      this.emitEdge(anchor, EdgeKind.DEFINES_IMPLICIT, kMod);
  1450    }
  1451  
  1452    /**
  1453     * Emits an implicit property for a getter or setter.
  1454     * For instance, a getter/setter `foo` in class `A` will emit an implicit
  1455     * property on that class with signature `A.foo`, and create "property/reads"
  1456     * and "property/writes" from the getters/setters to the implicit property.
  1457     */
  1458    emitImplicitProperty(
  1459        decl: ts.GetAccessorDeclaration|ts.SetAccessorDeclaration, anchor: VName,
  1460        funcVName: VName) {
  1461      // Remove trailing ":getter"/":setter" suffix
  1462      const propSignature = funcVName.signature.split(':').slice(0, -1).join(':');
  1463      const implicitProp = {...funcVName, signature: propSignature};
  1464  
  1465      this.emitNode(implicitProp, NodeKind.VARIABLE);
  1466      this.emitSubkind(implicitProp, Subkind.IMPLICIT);
  1467      this.emitEdge(anchor, EdgeKind.DEFINES_BINDING, implicitProp);
  1468  
  1469      const sym = this.host.getSymbolAtLocation(decl.name);
  1470      if (!sym || !sym.declarations) {
  1471        throw new Error('Getter/setter declaration has no symbols.');
  1472      }
  1473  
  1474      if (sym.declarations.find(ts.isGetAccessor)) {
  1475        // Emit a "property/reads" edge between the getter and the property
  1476        const getter =
  1477            this.host.getSymbolName(sym, TSNamespace.VALUE, Context.Getter);
  1478        if (!getter) return;
  1479        this.emitEdge(getter, EdgeKind.PROPERTY_READS, implicitProp);
  1480      }
  1481      if (sym.declarations.find(ts.isSetAccessor)) {
  1482        // Emit a "property/writes" edge between the setter and the property
  1483        const setter =
  1484            this.host.getSymbolName(sym, TSNamespace.VALUE, Context.Setter);
  1485        if (!setter) return;
  1486        this.emitEdge(setter, EdgeKind.PROPERTY_WRITES, implicitProp);
  1487      }
  1488    }
  1489  
  1490    /**
  1491     * Handles code like:
  1492     *   export default ...;
  1493     *   export = ...;
  1494     */
  1495    visitExportAssignment(assign: ts.ExportAssignment) {
  1496      if (assign.isExportEquals) {
  1497        const span = this.getTextSpan(assign, 'export =');
  1498        const anchor = this.newAnchor(assign, span.start, span.end);
  1499        this.emitEdge(
  1500            anchor, EdgeKind.DEFINES_BINDING, this.host.scopedSignature(assign));
  1501      } else {
  1502        // export default <expr>;
  1503        // is the same as exporting the expression under the symbol named
  1504        // "default".  But we don't have a nice name to link the symbol to!
  1505        // So instead we link the keyword "default" itself to the VName.
  1506        // The TypeScript AST does not expose the location of the 'default'
  1507        // keyword so we just find it in the source text to link it.
  1508        const span = this.getTextSpan(assign, 'default');
  1509        const anchor = this.newAnchor(assign, span.start, span.end);
  1510        this.emitEdge(
  1511            anchor, EdgeKind.DEFINES_BINDING, this.host.scopedSignature(assign));
  1512      }
  1513    }
  1514  
  1515    /**
  1516     * Handles code that explicitly exports a symbol, like:
  1517     *   export {Foo} from './bar';
  1518     *
  1519     * Note that export can also be a modifier on a declaration, like:
  1520     *   export class Foo {}
  1521     * and that case is handled as part of the ordinary declaration handling.
  1522     */
  1523    visitExportDeclaration(decl: ts.ExportDeclaration) {
  1524      if (decl.exportClause && ts.isNamedExports(decl.exportClause)) {
  1525        for (const exp of decl.exportClause.elements) {
  1526          const localSym = this.host.getSymbolAtLocation(exp.name);
  1527          if (!localSym) {
  1528            console.error(`TODO: export ${exp.name} has no symbol`);
  1529            continue;
  1530          }
  1531          const remoteSym = this.typeChecker.getAliasedSymbol(localSym);
  1532          const anchor = this.newAnchor(exp.name);
  1533          // Aliased export; propertyName is the 'as <...>' bit.
  1534          const propertyAnchor =
  1535              exp.propertyName ? this.newAnchor(exp.propertyName) : null;
  1536          // Symbol is a value.
  1537          if (remoteSym.flags & ts.SymbolFlags.Value) {
  1538            const kExport = this.host.getSymbolName(remoteSym, TSNamespace.VALUE);
  1539            if (kExport) {
  1540              this.emitEdge(anchor, EdgeKind.REF, kExport);
  1541              if (propertyAnchor) {
  1542                this.emitEdge(propertyAnchor, EdgeKind.REF, kExport);
  1543              }
  1544            }
  1545          }
  1546          // Symbol is a type.
  1547          if (remoteSym.flags & ts.SymbolFlags.Type) {
  1548            const kExport = this.host.getSymbolName(remoteSym, TSNamespace.TYPE);
  1549            if (kExport) {
  1550              this.emitEdge(anchor, EdgeKind.REF, kExport);
  1551              if (propertyAnchor) {
  1552                this.emitEdge(propertyAnchor, EdgeKind.REF, kExport);
  1553              }
  1554            }
  1555          }
  1556        }
  1557      }
  1558      if (decl.moduleSpecifier) {
  1559        const moduleSym = this.host.getSymbolAtLocation(decl.moduleSpecifier);
  1560        if (moduleSym) {
  1561          const moduleName = this.getModulePathFromModuleReference(moduleSym);
  1562          if (moduleName) {
  1563            const kModule = this.newVName('module', moduleName);
  1564            this.emitEdge(
  1565                this.newAnchor(decl.moduleSpecifier), EdgeKind.REF_IMPORTS,
  1566                kModule);
  1567          }
  1568        }
  1569      }
  1570    }
  1571  
  1572    visitVariableStatement(stmt: ts.VariableStatement) {
  1573      // A VariableStatement contains potentially multiple variable declarations,
  1574      // as in:
  1575      //   var x = 3, y = 4;
  1576      // In the (common) case where there's a single variable declared, we look
  1577      // for documentation for that variable above the entire statement.
  1578      if (stmt.declarationList.declarations.length === 1) {
  1579        const vname =
  1580            this.visitVariableDeclaration(stmt.declarationList.declarations[0]);
  1581        if (vname) this.visitJSDoc(stmt, vname);
  1582        return;
  1583      }
  1584  
  1585      // Otherwise, use default recursion over the statement.
  1586      ts.forEachChild(stmt, n => this.visit(n));
  1587    }
  1588  
  1589    /**
  1590     * Note: visitVariableDeclaration is also used for class properties;
  1591     * the decl parameter is the union of the attributes of the two types.
  1592     * @return the generated VName for the declaration, if any.
  1593     */
  1594    visitVariableDeclaration(decl: {
  1595      name: ts.BindingName|ts.PropertyName,
  1596      type?: ts.TypeNode,
  1597      initializer?: ts.Expression, kind: ts.SyntaxKind,
  1598    }&ts.Node): VName|undefined {
  1599      let vname: VName|undefined;
  1600      switch (decl.name.kind) {
  1601        case ts.SyntaxKind.Identifier:
  1602        case ts.SyntaxKind.PrivateIdentifier:
  1603        case ts.SyntaxKind.StringLiteral:
  1604        case ts.SyntaxKind.NumericLiteral:
  1605          const sym = this.host.getSymbolAtLocation(decl.name);
  1606          if (!sym) {
  1607            todo(
  1608                this.sourceRoot, decl.name,
  1609                `declaration ${decl.name.getText()} has no symbol`);
  1610            return undefined;
  1611          }
  1612          vname = this.host.getSymbolName(sym, TSNamespace.VALUE);
  1613          if (vname) {
  1614            this.emitNode(vname, NodeKind.VARIABLE);
  1615            this.emitEdge(
  1616                this.newAnchor(decl.name), EdgeKind.DEFINES_BINDING, vname);
  1617          }
  1618  
  1619          decl.name.forEachChild(child => this.visit(child));
  1620          break;
  1621        case ts.SyntaxKind.ObjectBindingPattern:
  1622        case ts.SyntaxKind.ArrayBindingPattern:
  1623          for (const element of (decl.name as ts.BindingPattern).elements) {
  1624            this.visit(element);
  1625          }
  1626          // Handle case like `const {name} = p;` where p is or type Person.
  1627          // This will add ref from the `name` variable to Person.name.
  1628          if (ts.isObjectBindingPattern(decl.name)) {
  1629            this.connectObjectBindingPatternToType(decl.name);
  1630          }
  1631          break;
  1632        case ts.SyntaxKind.ComputedPropertyName:
  1633          decl.name.forEachChild(child => this.visit(child));
  1634          break;
  1635        default:
  1636          break;
  1637      }
  1638  
  1639      if (decl.type) this.visitType(decl.type);
  1640      if (decl.initializer) this.visit(decl.initializer);
  1641      if (decl.type && decl.initializer &&
  1642          ts.isObjectLiteralExpression(decl.initializer)) {
  1643        this.connectObjectLiteralToType(decl.initializer, decl.type);
  1644      }
  1645      if (!vname) {
  1646        return undefined;
  1647      }
  1648  
  1649      if (ts.isVariableDeclaration(decl) || ts.isPropertyAssignment(decl) ||
  1650          ts.isPropertyDeclaration(decl) || ts.isBindingElement(decl) ||
  1651          ts.isShorthandPropertyAssignment(decl) ||
  1652          ts.isPropertySignature(decl) || ts.isJsxAttribute(decl)) {
  1653        this.emitMarkedSourceForVariable(decl, vname);
  1654      } else {
  1655        todo(this.sourceRoot, decl, 'Emit variable delaration code');
  1656      }
  1657  
  1658      if (ts.isPropertyDeclaration(decl)) {
  1659        const declNode = decl as ts.PropertyDeclaration;
  1660        if (isStaticMember(declNode, declNode.parent)) {
  1661          this.emitFact(vname, FactName.TAG_STATIC, '');
  1662        }
  1663      }
  1664      if (ts.isPropertySignature(decl) ||
  1665          ts.isPropertyDeclaration(decl) ||
  1666          ts.isPropertyAssignment(decl) ||
  1667          ts.isShorthandPropertyAssignment(decl)) {
  1668        this.emitSubkind(vname, Subkind.FIELD);
  1669        this.emitChildofEdge(vname, decl.parent);
  1670      }
  1671      if (ts.isShorthandPropertyAssignment(decl)) {
  1672        const origSym = this.typeChecker.getShorthandAssignmentValueSymbol(decl);
  1673        if (origSym) {
  1674          const origVName = this.host.getSymbolName(origSym, TSNamespace.VALUE);
  1675          if (origVName) {
  1676            this.emitEdge(this.newAnchor(decl.name), EdgeKind.REF_ID, origVName);
  1677          }
  1678        }
  1679      }
  1680      return vname;
  1681    }
  1682  
  1683    getIdentifierForMarkedSourceNode(node: ts.Node): string {
  1684      if (ts.isConstructorDeclaration(node)) {
  1685        return 'constructor';
  1686      }
  1687      if ('name' in node) {
  1688        return (node as ts.DeclarationStatement).name?.getText() ?? 'anonymous';
  1689      }
  1690      return 'anonymous';
  1691    }
  1692  
  1693    collectContextPartsForMarkedSource(node: ts.Node): string[] {
  1694      switch (node.kind) {
  1695        case ts.SyntaxKind.PropertyDeclaration:
  1696        case ts.SyntaxKind.PropertySignature:
  1697        case ts.SyntaxKind.EnumMember:
  1698        case ts.SyntaxKind.MethodDeclaration:
  1699        case ts.SyntaxKind.MethodSignature:
  1700        case ts.SyntaxKind.Constructor:
  1701          // Parent is a class/interface/enum. Use their name as context.
  1702          return [this.getIdentifierForMarkedSourceNode(node.parent)];
  1703        case ts.SyntaxKind.Parameter:
  1704          const method = node.parent;
  1705          if (!ts.isMethodSignature(method)
  1706              && !ts.isMethodDeclaration(method)
  1707              && !ts.isConstructorDeclaration(method)
  1708              && !ts.isFunctionDeclaration(method)) {
  1709            // We expect that parent of parameter is always a method. If it is not
  1710            // then return undefined so no context will be emitted.
  1711            return [];
  1712          }
  1713          // If method has parent (class) then add it to context. So it looks like
  1714          // 'ClassName.methodName'. Otherwise if it's a standalone function - just
  1715          // return function name.
  1716          const methodContext = this.collectContextPartsForMarkedSource(method);
  1717          methodContext.push(this.getIdentifierForMarkedSourceNode(method));
  1718          return methodContext;
  1719      }
  1720      // For all other nodes like variables, functions, classes don't use context
  1721      // for now. Other languages use namespace or filename as context for those but
  1722      // it doesn't provide much information.
  1723      return [];
  1724    }
  1725  
  1726    /**
  1727     * This function builds CONTEXT node for marked source for a node. Context is what
  1728     * the given node belongs to. For example for class method context is the name of the class.
  1729     *
  1730     * When the provided node doesn't have a context, e.g. variable, then this method returns null.
  1731     *
  1732     * Compared to other languages (Java, Go) context calculation here is simpler. We
  1733     * don't produce fully qualified class name including package (as TS doesn't have a concept of
  1734     * qualified name). Though we could add filename as namespace in future.
  1735     *
  1736     * We also don't handle nesting. In TS one can have class within a method within a class
  1737     * within a method. It is possible to fully calculate such name (similar to what we do in
  1738     * scopedSignature) but given that it's user-visible string - keep it simple.
  1739     * Even though it's incomplete.
  1740     */
  1741    buildMarkedSourceContextNode(node: ts.Node): JSONMarkedSource|null {
  1742      const parts = this.collectContextPartsForMarkedSource(node);
  1743      if (parts.length === 0) {
  1744        return null;
  1745      } else {
  1746        return {
  1747          kind: MarkedSourceKind.CONTEXT,
  1748          post_child_text: '.',
  1749          add_final_list_token: true,
  1750          child: parts.map(c => ({kind: MarkedSourceKind.IDENTIFIER, pre_text: c})),
  1751        };
  1752      }
  1753    }
  1754  
  1755    /**
  1756     * Emits a code fact for a variable or property declaration, specifying how
  1757     * the declaration should be presented to users.
  1758     *
  1759     * The form of the code fact is
  1760     *     ((property)|(local var)|const|let) <name>: <type>( = <initializer>)?
  1761     * where `(local var)` is the declaration of a variable in a catch clause.
  1762     */
  1763    emitMarkedSourceForVariable(
  1764        decl: ts.VariableDeclaration|ts.PropertyAssignment|
  1765        ts.PropertyDeclaration|ts.BindingElement|ts.ShorthandPropertyAssignment|
  1766        ts.PropertySignature|ts.JsxAttribute|ts.ParameterDeclaration|ts.EnumMember,
  1767        declVName: VName) {
  1768      const codeParts: JSONMarkedSource[] = [];
  1769      let varDecl;
  1770      const bindingPath: Array<string|number|undefined> = [];
  1771      if (ts.isBindingElement(decl)) {
  1772        // The node we want to emit code for is a BindingElement. This parent of
  1773        // this is always a BindingPattern; the parent of the BindingPattern is
  1774        // another BindingPattern, a ParameterDeclaration, or VariableDeclaration.
  1775        // We handle ParameterDeclarations in `visitParameters`, so here we only
  1776        // care about the declaration from a VariableDeclaration.
  1777        bindingPath.push(this.bindingElemIndex(decl));
  1778        varDecl = decl.parent.parent;
  1779        while (!ts.isVariableDeclaration(varDecl) && varDecl !== undefined) {
  1780          if (ts.isParameter(varDecl)) return;
  1781          bindingPath.push(this.bindingElemIndex(varDecl));
  1782          varDecl = varDecl.parent.parent;
  1783        }
  1784        if (varDecl === undefined) {
  1785          todo(
  1786              this.sourceRoot, decl,
  1787              `Does not have variable and parameter declaration.`);
  1788        }
  1789      } else {
  1790        varDecl = decl;
  1791      }
  1792      const context = this.buildMarkedSourceContextNode(decl);
  1793      if (context != null) {
  1794        codeParts.push(context);
  1795      }
  1796      if (ts.isParameter(varDecl) && varDecl.dotDotDotToken) {
  1797        codeParts.push({
  1798          // TODO: this should probably be TYPE but the current renderer adds an
  1799          // unnecessary space after TYPEs
  1800          kind: MarkedSourceKind.MODIFIER,
  1801          pre_text: '...',
  1802        });
  1803      }
  1804      codeParts.push({
  1805        kind: MarkedSourceKind.IDENTIFIER,
  1806        pre_text: fmtMarkedSource(this.getIdentifierForMarkedSourceNode(decl)),
  1807      });
  1808      if (!ts.isEnumMember(varDecl)) {
  1809        const ty = this.typeChecker.getTypeAtLocation(decl);
  1810        const tyStr = this.typeChecker.typeToString(ty, decl);
  1811        codeParts.push(
  1812          {kind: MarkedSourceKind.TYPE, pre_text: ': ', post_text: fmtMarkedSource(tyStr)});
  1813  
  1814      }
  1815      if ('initializer' in varDecl && varDecl.initializer) {
  1816        let init: ts.Node = varDecl.initializer;
  1817  
  1818        if (ts.isObjectLiteralExpression(init) ||
  1819            ts.isArrayLiteralExpression(init)) {
  1820          const narrowedInit = isNonNullableArray(bindingPath) &&
  1821              this.walkObjectLikeLiteral(init, bindingPath.reverse());
  1822          init = narrowedInit || init;
  1823        }
  1824  
  1825        codeParts.push(
  1826          {kind: MarkedSourceKind.INITIALIZER, pre_text: fmtMarkedSource(init.getText())});
  1827      }
  1828  
  1829      const markedSource = {kind: MarkedSourceKind.BOX, child: codeParts};
  1830      this.emitFact(declVName, FactName.CODE_JSON, JSON.stringify(markedSource));
  1831    }
  1832  
  1833    /**
  1834     * Emits a code fact for a class specifying how the declaration should be presented to users.
  1835     */
  1836    emitMarkedSourceForClasslikeDeclaration(
  1837      decl: ts.ClassLikeDeclaration|ts.InterfaceDeclaration|ts.EnumDeclaration, declVName: VName) {
  1838      const markedSource: JSONMarkedSource =
  1839        {kind: MarkedSourceKind.IDENTIFIER, pre_text: this.getIdentifierForMarkedSourceNode(decl)};
  1840      this.emitFact(declVName, FactName.CODE_JSON, JSON.stringify(markedSource));
  1841    }
  1842  
  1843    /**
  1844     * Emits a code fact for a function specifying how the declaration should be presented to users.
  1845     */
  1846    emitMarkedSourceForFunction(decl: ts.FunctionLikeDeclaration, declVName: VName) {
  1847      const codeParts: JSONMarkedSource[] = [];
  1848      const context = this.buildMarkedSourceContextNode(decl);
  1849      if (context != null) {
  1850        codeParts.push(context);
  1851      }
  1852      codeParts.push({
  1853        kind: MarkedSourceKind.IDENTIFIER,
  1854        pre_text: this.getIdentifierForMarkedSourceNode(decl),
  1855      });
  1856      codeParts.push({
  1857        kind: MarkedSourceKind.PARAMETER_LOOKUP_BY_PARAM,
  1858        pre_text: '(',
  1859        post_child_text: ', ',
  1860        post_text: ')'
  1861      });
  1862      const signature = this.typeChecker.getTypeAtLocation(decl).getCallSignatures()[0];
  1863      if (signature) {
  1864        const returnType = signature.getReturnType();
  1865        const returnTypeStr = this.typeChecker.typeToString(returnType, decl);
  1866        codeParts.push({
  1867          kind: MarkedSourceKind.TYPE,
  1868          pre_text: ': ',
  1869          post_text: fmtMarkedSource(returnTypeStr),
  1870        });
  1871      }
  1872      const markedSource = {kind: MarkedSourceKind.BOX, child: codeParts};
  1873      this.emitFact(declVName, FactName.CODE_JSON, JSON.stringify(markedSource));
  1874    }
  1875  
  1876    /**
  1877     * Given a path of properties, walks the properties/elements of an
  1878     * object/array literal, yielding the final node along the path.
  1879     *
  1880     * If the path or object literal is malformed (i.e. the property does not
  1881     * exist or the object to walk is not a literal), nothing is returned.
  1882     */
  1883    walkObjectLikeLiteral(
  1884        objLiteral: ts.ArrayLiteralExpression|ts.ObjectLiteralExpression,
  1885        path: Array<string|number>,
  1886        ): ts.Node|undefined {
  1887      let node: ts.Node = objLiteral;
  1888      for (const prop of path) {
  1889        let next: ts.Node|undefined;
  1890        if (ts.isObjectLiteralExpression(node)) {
  1891          // The property name node text is the "index" of the property. See
  1892          // `bindingElemIndex` for more details.
  1893          next = node.properties.find(
  1894              p => p.name && this.getPropertyNameStr(p.name) === prop);
  1895          if (next && ts.isPropertyAssignment(next)) {
  1896            next = next.initializer;
  1897          }
  1898        } else if (
  1899            ts.isArrayLiteralExpression(node) && typeof prop === 'number') {
  1900          next = (node as ts.ArrayLiteralExpression).elements[prop];
  1901        }
  1902        if (!next) {
  1903          todo(
  1904              this.sourceRoot, node,
  1905              `expected to be an object-like literal with property '${prop}'`)
  1906          return;
  1907        }
  1908        node = next;
  1909      }
  1910      return node;
  1911    }
  1912  
  1913    /**
  1914     * Given an object literal that is used in a place where given type is
  1915     * expected - adds refs from the literals properties to the type's properties.
  1916     */
  1917    connectObjectLiteralToType(
  1918        literal: ts.ObjectLiteralExpression, typeNode: ts.TypeNode|undefined) {
  1919      if (typeNode == null) return;
  1920      const type = this.typeChecker.getTypeFromTypeNode(typeNode);
  1921      for (const prop of literal.properties) {
  1922        if (ts.isPropertyAssignment(prop) ||
  1923            ts.isShorthandPropertyAssignment(prop) ||
  1924            ts.isMethodDeclaration(prop)) {
  1925          this.emitPropertyRef(prop.name, type);
  1926        }
  1927      }
  1928    }
  1929  
  1930    /**
  1931     * Given object binding pattern like `const {name} = getPerson();` tries to
  1932     * add refs from binding variables to the propeties of the type. Like `name`
  1933     * is connected to `Person.name`.
  1934     */
  1935    connectObjectBindingPatternToType(binding: ts.ObjectBindingPattern) {
  1936      const type = this.typeChecker.getTypeAtLocation(binding);
  1937      for (const prop of binding.elements) {
  1938        if (prop.propertyName) {
  1939          this.emitPropertyRef(prop.propertyName, type);
  1940        } else if (ts.isIdentifier(prop.name)) {
  1941          this.emitPropertyRef(prop.name, type);
  1942        }
  1943      }
  1944    }
  1945  
  1946    /**
  1947     * Helper function to emit `ref` node from a given property (usually of object
  1948     * literal or destructuring pattern) to the type. Checks whether provided type
  1949     * contains a property with the same name and will use it for `ref` node.
  1950     */
  1951    emitPropertyRef(prop: ts.PropertyName, type: ts.Type) {
  1952      const propName = this.getPropertyNameStr(prop);
  1953      if (propName == null) return;
  1954      const propertyOnType = type.getProperty(propName);
  1955      if (propertyOnType == null) return;
  1956      const propFlags = propertyOnType.flags;
  1957      const isType = (propFlags &
  1958                      (ts.SymbolFlags.Class | ts.SymbolFlags.Interface |
  1959                       ts.SymbolFlags.RegularEnum | ts.SymbolFlags.TypeAlias)) > 0;
  1960      const vname = this.host.getSymbolName(
  1961          propertyOnType, isType ? TSNamespace.TYPE : TSNamespace.VALUE);
  1962      if (vname == null) return;
  1963      const anchor = this.newAnchor(prop);
  1964      this.emitEdge(anchor, EdgeKind.REF_ID, vname);
  1965    }
  1966  
  1967    /**
  1968     * Returns the property "index" of a bound element in a binding pattern, if
  1969     * known. For example,
  1970     * - `1` has index `2` in the binding pattern `[3, 2, 1]`
  1971     * - `c` has index `c` in the binding pattern `{a, b, c}`
  1972     * - `calias` has index `c` in the binding pattern `{a, b, c: calias}`
  1973     */
  1974    bindingElemIndex(elem: ts.BindingElement): string|number|undefined {
  1975      const bindingPat = elem.parent;
  1976      if (ts.isObjectBindingPattern(bindingPat)) {
  1977        if (elem.propertyName) {
  1978          return this.getPropertyNameStr(elem.propertyName);
  1979        }
  1980        switch (elem.name.kind) {
  1981          case ts.SyntaxKind.Identifier:
  1982            return elem.name.text;
  1983          case ts.SyntaxKind.ArrayBindingPattern:
  1984          case ts.SyntaxKind.ObjectBindingPattern:
  1985            return undefined;
  1986        }
  1987      } else {
  1988        return bindingPat.elements.indexOf(elem);
  1989      }
  1990    }
  1991  
  1992    /**
  1993     * Returns the string content of a property name, if known.
  1994     * The name of complex computed properties is often not known.
  1995     */
  1996    getPropertyNameStr(elem: ts.PropertyName): string|undefined {
  1997      switch (elem.kind) {
  1998        case ts.SyntaxKind.Identifier:
  1999        case ts.SyntaxKind.StringLiteral:
  2000        case ts.SyntaxKind.NumericLiteral:
  2001        case ts.SyntaxKind.PrivateIdentifier:
  2002        case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
  2003          return elem.text;
  2004        case ts.SyntaxKind.ComputedPropertyName:
  2005          const name = this.host.getSymbolAtLocation(elem)?.name;
  2006          // If the computed property expression is more complicated than an
  2007          // identifier (e.g. `['red' + 'cat']` or `[fn()]`), the name isn't
  2008          // resolved and the symbol name is marked as "__computed". This doesn't
  2009          // help us for indexing, so return "undefined" in such cases.
  2010          // Constant evaluation of the computed property name is not always
  2011          // possible (e.g. the expression may be a function call), and usage of
  2012          // computed properties is probably rare enough that handling the "simple
  2013          // case" is good enough for now.
  2014          return name !== '__computed' ? name : undefined;
  2015      }
  2016    }
  2017  
  2018    /**
  2019     * Emit "overrides" edges if this method overrides extended classes or
  2020     * implemented interfaces, which are listed in the Heritage Clauses of
  2021     * a class or interface.
  2022     *     class X extends A implements B, C {}
  2023     *             ^^^^^^^^^-^^^^^^^^^^^^^^^----- `HeritageClause`s
  2024     * Look at each type listed in the heritage clauses and traverse its
  2025     * members. If the type has a member that matches the method visited in
  2026     * this function (`kFunc`), emit an "overrides" edge to that member.
  2027     */
  2028    emitOverridesEdgeForFunction(
  2029        funcSym: ts.Symbol, funcVName: VName,
  2030        parent: ts.ClassLikeDeclaration|ts.InterfaceDeclaration) {
  2031      if (parent.heritageClauses == null) {
  2032        return;
  2033      }
  2034      for (const heritage of parent.heritageClauses) {
  2035        for (const baseType of heritage.types) {
  2036          const type = this.typeChecker.getTypeAtLocation(baseType.expression);
  2037          if (!type || !type.symbol || !type.symbol.members) {
  2038            continue;
  2039          }
  2040  
  2041          const funcName = funcSym.name;
  2042          const funcFlags = funcSym.flags;
  2043  
  2044          // Find a member of with the same type (same flags) and same name
  2045          // as the overriding method.
  2046          const overriddenCondition = (sym: ts.Symbol) =>
  2047              Boolean(sym.flags & funcFlags) && sym.name === funcName;
  2048  
  2049          const overridden = toArray<ts.Symbol>(type.symbol.members.values())
  2050              .find(overriddenCondition);
  2051          if (overridden) {
  2052            const base = this.host.getSymbolName(overridden, TSNamespace.VALUE);
  2053            if (base) {
  2054              this.emitEdge(funcVName, EdgeKind.OVERRIDES, base);
  2055            }
  2056          } else {
  2057            // If parent class or interface doesn't have this method - it's possible
  2058            // that parent's parent might. To check for that recurse to the parent's parent
  2059            // classes/interfaces.
  2060            const decl = type.symbol.declarations?.[0];
  2061            if (decl && (ts.isClassLike(decl) || ts.isInterfaceDeclaration(decl))) {
  2062              this.emitOverridesEdgeForFunction(funcSym, funcVName, decl);
  2063            }
  2064          }
  2065        }
  2066      }
  2067    }
  2068  
  2069    visitFunctionLikeDeclaration(decl: ts.FunctionLikeDeclaration) {
  2070      this.visitDecorators(ts.canHaveDecorators(decl) ? ts.getDecorators(decl) : []);
  2071      const {sym, vname} = this.getSymbolAndVNameForFunctionDeclaration(decl);
  2072      if (!vname) {
  2073        todo(
  2074            this.sourceRoot, decl,
  2075            `function declaration ${decl.getText()} has no symbol`);
  2076        return;
  2077      }
  2078      const isNameComputedProperty =
  2079          decl.name && decl.name.kind === ts.SyntaxKind.ComputedPropertyName;
  2080      if (isNameComputedProperty) {
  2081        this.visit((decl.name as ts.ComputedPropertyName).expression);
  2082      }
  2083  
  2084      // Treat functions with computed names as anonymous. From developer point of
  2085      // view in the following expression: `const k = {[foo]() {}};` the part
  2086      // `[foo]` isn't a separate symbol that you can click. Only `foo` should be
  2087      // xref'ed and lead to the `foo` definition.
  2088      if (decl.name && !isNameComputedProperty) {
  2089        if (!sym) {
  2090          todo(
  2091              this.sourceRoot, decl.name,
  2092              `function declaration ${decl.name.getText()} has no symbol`);
  2093          return;
  2094        }
  2095  
  2096        const declAnchor = this.newAnchor(decl.name);
  2097        this.emitNode(vname, NodeKind.FUNCTION);
  2098        this.emitEdge(declAnchor, EdgeKind.DEFINES_BINDING, vname);
  2099  
  2100        // Getters/setters also emit an implicit class property entry. If a
  2101        // getter is present, it will bind this entry; otherwise a setter will.
  2102        if (ts.isGetAccessor(decl) ||
  2103            (ts.isSetAccessor(decl) &&
  2104             (!sym.declarations || !sym.declarations.find(ts.isGetAccessor)))) {
  2105          this.emitImplicitProperty(decl, declAnchor, vname);
  2106        }
  2107  
  2108        this.visitJSDoc(decl, vname, false);
  2109      }
  2110      this.emitEdge(this.newAnchor(decl), EdgeKind.DEFINES, vname);
  2111  
  2112      if (decl.parent) {
  2113        this.emitChildofEdge(vname, decl.parent);
  2114        if ((ts.isClassLike(decl.parent) ||
  2115             ts.isInterfaceDeclaration(decl.parent)) &&
  2116            sym) {
  2117          this.emitOverridesEdgeForFunction(sym, vname, decl.parent);
  2118        }
  2119      }
  2120  
  2121      this.visitParameters(decl.parameters, vname);
  2122  
  2123      if (decl.type) {
  2124        // "type" here is the return type of the function.
  2125        this.visitType(decl.type);
  2126      }
  2127  
  2128      if (decl.typeParameters)
  2129        this.visitTypeParameters(vname, decl.typeParameters);
  2130      if (decl.body) {
  2131        this.visit(decl.body);
  2132      } else {
  2133        this.emitFact(vname, FactName.COMPLETE, 'incomplete');
  2134      }
  2135      this.emitMarkedSourceForFunction(decl, vname);
  2136    }
  2137  
  2138    /**
  2139     * Emits childof edge from member to their parents. Parent can be class/interface/enum.
  2140     * See https://kythe.io/docs/schema/#childof
  2141     */
  2142    emitChildofEdge(vname: VName, parent: ts.Node) {
  2143      if (!ts.isClassLike(parent) && !ts.isInterfaceDeclaration(parent) &&
  2144          !ts.isEnumDeclaration(parent)) {
  2145        return;
  2146      }
  2147      const parentName = parent.name;
  2148      if (parentName == null) {
  2149        return;
  2150      }
  2151      const parentSym = this.host.getSymbolAtLocationFollowingAliases(parentName);
  2152      if (!parentSym) {
  2153        todo(this.sourceRoot, parentName, `parent ${parentName} has no symbol`);
  2154        return;
  2155      }
  2156      const kParent = this.host.getSymbolName(parentSym, TSNamespace.TYPE);
  2157      if (kParent) {
  2158        this.emitEdge(vname, EdgeKind.CHILD_OF, kParent);
  2159      }
  2160    }
  2161  
  2162    /**
  2163     * Visits a function parameters, which can be recursive in the case of
  2164     * parameters created via bound elements:
  2165     *    function foo({a, b: {c, d}}, e, f) {}
  2166     * In this code, a, c, d, e, f are all parameters with increasing parameter
  2167     * numbers [0, 4].
  2168     */
  2169    visitParameters(
  2170        parameters: ReadonlyArray<ts.ParameterDeclaration>, kFunc: VName) {
  2171      let paramNum = 0;
  2172      const recurseVisit =
  2173          (param: ts.ParameterDeclaration|ts.BindingElement) => {
  2174            this.visitDecorators(ts.canHaveDecorators(param) ? ts.getDecorators(param) : []);
  2175  
  2176            switch (param.name.kind) {
  2177              case ts.SyntaxKind.Identifier:
  2178                const sym = this.host.getSymbolAtLocationFollowingAliases(param.name);
  2179                if (!sym) {
  2180                  todo(
  2181                      this.sourceRoot, param.name,
  2182                      `param ${param.name.getText()} has no symbol`);
  2183                  return;
  2184                }
  2185                const kParam = this.host.getSymbolName(sym, TSNamespace.VALUE);
  2186                if (!kParam) return;
  2187                this.emitNode(kParam, NodeKind.VARIABLE);
  2188  
  2189                this.emitEdge(
  2190                    kFunc, makeOrdinalEdge(EdgeKind.PARAM, paramNum), kParam);
  2191                ++paramNum;
  2192  
  2193                if (ts.isParameterPropertyDeclaration(param, param.parent)) {
  2194                  // Class members defined in the parameters of a constructor are
  2195                  // children of the class type.
  2196                  const parentName = param.parent.parent.name;
  2197                  if (parentName !== undefined) {
  2198                    const parentSym = this.host.getSymbolAtLocationFollowingAliases(parentName);
  2199                    if (parentSym !== undefined) {
  2200                      const kClass =
  2201                          this.host.getSymbolName(parentSym, TSNamespace.TYPE);
  2202                      if (!kClass) return;
  2203                      this.emitEdge(kParam, EdgeKind.CHILD_OF, kClass);
  2204                    }
  2205                  }
  2206                } else {
  2207                  this.emitEdge(kParam, EdgeKind.CHILD_OF, kFunc);
  2208                }
  2209  
  2210                this.emitEdge(
  2211                    this.newAnchor(param.name), EdgeKind.DEFINES_BINDING, kParam);
  2212                this.emitMarkedSourceForVariable(param, kParam);
  2213                break;
  2214              case ts.SyntaxKind.ObjectBindingPattern:
  2215              case ts.SyntaxKind.ArrayBindingPattern:
  2216                const elements = toArray(param.name.elements.entries());
  2217                for (const [index, element] of elements) {
  2218                  if (ts.isBindingElement(element)) {
  2219                    recurseVisit(element);
  2220                  }
  2221                }
  2222                // Handles case like:
  2223                // funtion doSomething({name}: Person) { ... }
  2224                // and adds refs from `name` variable to the Person.name field.
  2225                if (ts.isObjectBindingPattern(param.name)) {
  2226                  this.connectObjectBindingPatternToType(param.name);
  2227                }
  2228                break;
  2229              default:
  2230                break;
  2231            }
  2232  
  2233            if (ts.isParameter(param) && param.type) this.visitType(param.type);
  2234            if (param.initializer) this.visit(param.initializer);
  2235          }
  2236  
  2237      for (const element of parameters) {
  2238        recurseVisit(element);
  2239      }
  2240    }
  2241  
  2242    visitDecorators(decors: ReadonlyArray<ts.Decorator> | undefined) {
  2243      if (decors) {
  2244        for (const decor of decors) {
  2245          this.visit(decor);
  2246        }
  2247      }
  2248    }
  2249  
  2250    /**
  2251     * Visits a module declaration, which can look like any of the following:
  2252     *     declare module 'foo';
  2253     *     declare module 'foo' {}
  2254     *     declare module foo {}
  2255     *     namespace Foo {}
  2256     */
  2257    visitModuleDeclaration(decl: ts.ModuleDeclaration) {
  2258      let sym = this.host.getSymbolAtLocation(decl.name);
  2259      if (!sym) {
  2260        todo(
  2261            this.sourceRoot, decl.name,
  2262            `module declaration ${decl.name.getText()} has no symbol`);
  2263        return;
  2264      }
  2265      // A TypeScript module declaration declares both a namespace (a Kythe
  2266      // "record") and a value (most similar to a "package", which defines a
  2267      // module with declarations).
  2268      const kNamespace = this.host.getSymbolName(sym, TSNamespace.NAMESPACE);
  2269      const kValue = this.host.getSymbolName(sym, TSNamespace.VALUE);
  2270      if (!kNamespace || !kValue) return;
  2271      // It's possible that same namespace appears multiple time. We need to
  2272      // emit only single node for that namespace and single defines/binding
  2273      // edge.
  2274      if (sym.valueDeclaration === decl) {
  2275        this.emitNode(kNamespace, NodeKind.RECORD);
  2276        this.emitSubkind(kNamespace, Subkind.NAMESPACE);
  2277        this.emitNode(kValue, NodeKind.PACKAGE);
  2278  
  2279        const nameAnchor = this.newAnchor(decl.name);
  2280        this.emitEdge(nameAnchor, EdgeKind.DEFINES_BINDING, kNamespace);
  2281        this.emitEdge(nameAnchor, EdgeKind.DEFINES_BINDING, kValue);
  2282        // If no body then it is incomplete module definition, like declare module
  2283        // 'foo';
  2284        this.emitFact(
  2285            kNamespace, FactName.COMPLETE,
  2286            decl.body ? 'definition' : 'incomplete');
  2287      }
  2288  
  2289      // The entire module declaration defines the created namespace.
  2290      this.emitEdge(this.newAnchor(decl), EdgeKind.DEFINES, kValue);
  2291  
  2292      this.visitDecorators(ts.canHaveDecorators(decl) ? ts.getDecorators(decl) : []);
  2293      if (decl.body) this.visit(decl.body);
  2294    }
  2295  
  2296    visitClassDeclaration(decl: ts.ClassDeclaration) {
  2297      this.visitDecorators(ts.canHaveDecorators(decl) ? ts.getDecorators(decl) : []);
  2298      let kClass: VName|undefined;
  2299      if (decl.name) {
  2300        const sym = this.host.getSymbolAtLocation(decl.name);
  2301        if (!sym) {
  2302          todo(
  2303              this.sourceRoot, decl.name,
  2304              `class ${decl.name.getText()} has no symbol`);
  2305          return;
  2306        }
  2307        // A 'class' declaration declares both a type (a 'record', representing
  2308        // instances of the class) and a value (least ambigiously, also the
  2309        // class declaration).
  2310        kClass = this.host.getSymbolName(sym, TSNamespace.TYPE);
  2311        if (!kClass) return;
  2312        this.emitNode(kClass, NodeKind.RECORD);
  2313        const anchor = this.newAnchor(decl.name);
  2314        this.emitEdge(anchor, EdgeKind.DEFINES_BINDING, kClass);
  2315  
  2316        // Emit constructor.
  2317        const kCtor = this.host.getSymbolName(sym, TSNamespace.VALUE);
  2318        if (!kCtor) return;
  2319        let ctorAnchor = anchor;
  2320        // If the class has an explicit constructor method - use it as an anchor.
  2321        const ctorSymbol = this.getCtorSymbol(decl);
  2322        if (ctorSymbol && ctorSymbol.declarations) {
  2323          const ctorDecl = ctorSymbol.declarations[0];
  2324          const span = this.getTextSpan(ctorDecl, 'constructor');
  2325          ctorAnchor = this.newAnchor(ctorDecl, span.start, span.end);
  2326        }
  2327        this.emitNode(kCtor, NodeKind.FUNCTION);
  2328        this.emitSubkind(kCtor, Subkind.CONSTRUCTOR);
  2329        this.emitEdge(ctorAnchor, EdgeKind.DEFINES_BINDING, kCtor);
  2330  
  2331        this.visitJSDoc(decl, kClass);
  2332        this.emitMarkedSourceForClasslikeDeclaration(decl, kClass);
  2333      }
  2334      if (decl.typeParameters)
  2335        this.visitTypeParameters(kClass, decl.typeParameters);
  2336      if (decl.heritageClauses) this.visitHeritage(kClass, decl.heritageClauses);
  2337      for (const member of decl.members) {
  2338        this.visit(member);
  2339      }
  2340    }
  2341  
  2342    visitEnumDeclaration(decl: ts.EnumDeclaration) {
  2343      const sym = this.host.getSymbolAtLocation(decl.name);
  2344      if (!sym) return;
  2345      const kType = this.host.getSymbolName(sym, TSNamespace.TYPE);
  2346      if (!kType) return;
  2347      this.emitNode(kType, NodeKind.RECORD);
  2348      const kValue = this.host.getSymbolName(sym, TSNamespace.VALUE);
  2349      if (!kValue) return;
  2350      this.emitNode(kValue, NodeKind.CONSTANT);
  2351  
  2352      const anchor = this.newAnchor(decl.name);
  2353      this.emitEdge(anchor, EdgeKind.DEFINES_BINDING, kType);
  2354      this.emitEdge(anchor, EdgeKind.DEFINES_BINDING, kValue);
  2355      for (const member of decl.members) {
  2356        this.visit(member);
  2357      }
  2358      this.emitMarkedSourceForClasslikeDeclaration(decl, kValue);
  2359    }
  2360  
  2361    visitEnumMember(decl: ts.EnumMember) {
  2362      const sym = this.host.getSymbolAtLocation(decl.name);
  2363      if (!sym) return;
  2364      const kMember = this.host.getSymbolName(sym, TSNamespace.VALUE);
  2365      if (!kMember) return;
  2366      this.emitNode(kMember, NodeKind.CONSTANT);
  2367      this.emitEdge(this.newAnchor(decl.name), EdgeKind.DEFINES_BINDING, kMember);
  2368      this.emitMarkedSourceForVariable(decl, kMember);
  2369      this.emitChildofEdge(kMember, decl.parent);
  2370    }
  2371  
  2372    visitExpressionMember(node: ts.Node) {
  2373      const sym = this.host.getSymbolAtLocationFollowingAliases(node);
  2374      if (!sym) {
  2375        // E.g. a field of an "any".
  2376        return;
  2377      }
  2378      if (!sym.declarations || sym.declarations.length === 0) {
  2379        // An undeclared symbol, e.g. "undefined".
  2380        return;
  2381      }
  2382      const isClass = (sym.flags & ts.SymbolFlags.Class) > 0;
  2383      const isConstructorCall = ts.isNewExpression(node.parent);
  2384      const ns = isClass && !isConstructorCall ? TSNamespace.TYPE : TSNamespace.VALUE;
  2385      const name = this.host.getSymbolName(sym, ns);
  2386      if (!name) return;
  2387      const anchor = this.newAnchor(node);
  2388  
  2389      const refType = this.getRefType(node, sym);
  2390      if (refType == RefType.READ || refType == RefType.READ_WRITE) {
  2391        this.emitEdge(anchor, EdgeKind.REF, name);
  2392      }
  2393      if (refType == RefType.WRITE || refType == RefType.READ_WRITE) {
  2394        this.emitEdge(anchor, EdgeKind.REF_WRITES, name);
  2395      }
  2396      // For classes emit ref/id to the type node in addition to regular
  2397      // ref. When user check refs for a class - they usually check get
  2398      // refs of the class node, not the constructor node. That's why
  2399      // we need ref/id from all usages to the class node.
  2400      if (isConstructorCall) {
  2401        const className = this.host.getSymbolName(sym, TSNamespace.TYPE);
  2402        if (className != null) {
  2403          this.emitEdge(anchor, EdgeKind.REF_ID, className);
  2404        }
  2405      }
  2406      this.addInfluencer(name);
  2407    }
  2408  
  2409    /**
  2410     * Determines the type of reference type of a {@link ts.Node} in its parent
  2411     * expression.
  2412     * @param node The {@link ts.Node} being referenced
  2413     * @param sym The {@link ts.Symbol} associated with the node
  2414     * @returns The {@link RefType} indicating whether the reference is READ,
  2415     * WRITE, or READ_WRITE
  2416     */
  2417    getRefType(node: ts.Node, sym: ts.Symbol): RefType {
  2418      // If the identifier being accessed is a property of a class, we need to
  2419      // recurse through the parents nodes until we get the true parent
  2420      // expression.
  2421      let parent = node.parent;
  2422      while (ts.isPropertyAccessExpression(parent)) {
  2423        parent = parent.parent;
  2424      }
  2425  
  2426      if (ts.isPrefixUnaryExpression(parent) ||
  2427          ts.isPostfixUnaryExpression(parent)) {
  2428        let operator: ts.SyntaxKind = parent.operator;
  2429        let operand: ts.Expression = parent.operand;
  2430  
  2431        const operandNode = ts.isPropertyAccessExpression(operand) ?
  2432            this.propertyAccessIdentifier(operand) as ts.Node :
  2433            operand as ts.Node;
  2434  
  2435        // Check if the operand is the same as referenced symbol and if this was
  2436        // an increment or decrement operation. If both are true, this is a
  2437        // READ_WRITE reference.
  2438        if (this.expressionMatchesSymbol(operand, sym) &&
  2439            operandNode.pos === node.pos) {
  2440          if (operator === ts.SyntaxKind.PlusPlusToken ||
  2441              operator === ts.SyntaxKind.MinusMinusToken) {
  2442            return RefType.READ_WRITE;
  2443          }
  2444        }
  2445      } else if (ts.isBinaryExpression(parent)) {
  2446        const lhsNode = ts.isPropertyAccessExpression(parent.left) ?
  2447            this.propertyAccessIdentifier(parent.left) as ts.Node :
  2448            parent.left as ts.Node;
  2449        const opString = ts.tokenToString(parent.operatorToken.kind) || '';
  2450  
  2451        if (this.expressionMatchesSymbol(parent.left, sym) &&
  2452            lhsNode.pos === node.pos) {
  2453          const compoundAssignmentOperators = [
  2454            '+=', '-=', '*=', '**=', '/=', '%=', '<<=', '>>=', '>>>=', '&=', '|=',
  2455            '^='
  2456          ];
  2457          if (compoundAssignmentOperators.includes(opString)) {
  2458            // Compound assignment operations are always a READ_WRITE reference
  2459            return RefType.READ_WRITE;
  2460          } else if (opString === '=') {
  2461            // If the symbol is on the left side of a assignment operation, it
  2462            // is always at least a WRITE reference. However, we then need to
  2463            // check if the parent expression is also a binary expression. If so,
  2464            // we check whether the current binary expression is on the right
  2465            // side of that expression, indicating a chained assignment such as
  2466            // x = y = z. If this is the case, the reference becomes READ_WRITE
  2467            // instead.
  2468            if (parent.parent !== null && ts.isBinaryExpression(parent.parent)) {
  2469              if (ts.tokenToString(parent.parent.operatorToken.kind) === '=' &&
  2470                  parent.parent.right === parent) {
  2471                return RefType.READ_WRITE;
  2472              }
  2473            }
  2474            return RefType.WRITE;
  2475          }
  2476        }
  2477      }
  2478      return RefType.READ;
  2479    }
  2480  
  2481    /**
  2482     * Check whether a {@link ts.Expression} matches a symbol. If the expression
  2483     * is a {@link ts.PropertyAccessExpression}, the check is performed against
  2484     * the base property being accessed.
  2485     */
  2486    expressionMatchesSymbol(expression: ts.Expression, symbol: ts.Symbol):
  2487        boolean {
  2488      if (ts.isIdentifier(expression)) {
  2489        return this.host.getSymbolAtLocation(expression) === symbol;
  2490      } else if (ts.isPropertyAccessExpression(expression)) {
  2491        return this.host.getSymbolAtLocation(
  2492                   this.propertyAccessIdentifier(expression)) === symbol;
  2493      }
  2494      return false;
  2495    }
  2496  
  2497  
  2498    /**
  2499     * Recurses through a {@link ts.PropertyAccessExpression} to find the
  2500     * identifier of the base property being accessed. Recursion is necessary
  2501     * because a chained property accesses such as `obj.member.member` will
  2502     * have two layers of {@link ts.PropertyAccessExpression}s.
  2503     *
  2504     * @param expression The {@link ts.PropertyAccessExpression} to process
  2505     * @returns The identifier of the base property being accessed
  2506     */
  2507    propertyAccessIdentifier(expression: ts.PropertyAccessExpression):
  2508        ts.Identifier|ts.PrivateIdentifier {
  2509      while (ts.isPropertyAccessExpression(expression.parent)) {
  2510        expression = expression.parent;
  2511      }
  2512      return expression.name;
  2513    }
  2514  
  2515    /**
  2516     * Emits a reference from a "this" keyword to the type of the "this" object.
  2517     */
  2518    visitThisKeyword(keyword: ts.ThisExpression) {
  2519      const sym = this.host.getSymbolAtLocation(keyword);
  2520      if (!sym) {
  2521        // "this" refers to an object with no particular type, e.g.
  2522        //   let obj = {
  2523        //     foo() { this.foo(); }
  2524        //   };
  2525        return;
  2526      }
  2527      if (!sym.declarations || sym.declarations.length === 0) {
  2528        // "this" keyword is `globalThis`, which has no declarations.
  2529        return;
  2530      }
  2531  
  2532      const type = this.host.getSymbolName(sym, TSNamespace.TYPE);
  2533      if (!type) return;
  2534      const thisAnchor = this.newAnchor(keyword);
  2535      this.emitEdge(thisAnchor, EdgeKind.REF, type);
  2536    }
  2537  
  2538    /**
  2539     * visitJSDoc attempts to attach a 'doc' node to a given target, by looking
  2540     * for JSDoc comments.
  2541     */
  2542    visitJSDoc(node: ts.Node, target: VName, emitDeprecation: boolean = true) {
  2543      if (emitDeprecation) {
  2544        this.maybeTagDeprecated(node, target);
  2545      }
  2546  
  2547      const text = node.getFullText();
  2548      const comments = ts.getLeadingCommentRanges(text, 0);
  2549      if (!comments) return;
  2550  
  2551      let jsdoc: string|undefined;
  2552      for (const commentRange of comments) {
  2553        if (commentRange.kind !== ts.SyntaxKind.MultiLineCommentTrivia) continue;
  2554        const comment =
  2555            text.substring(commentRange.pos + 2, commentRange.end - 2);
  2556        if (!comment.startsWith('*')) {
  2557          // Not a JSDoc comment.
  2558          continue;
  2559        }
  2560        // Strip the ' * ' bits that start lines within the comment.
  2561        jsdoc = comment.replace(/^[ \t]*\* ?/mg, '');
  2562        break;
  2563      }
  2564      if (jsdoc === undefined) return;
  2565  
  2566      // Strip leading and trailing whitespace.
  2567      jsdoc = jsdoc.replace(/^\s+/, '').replace(/\s+$/, '');
  2568      const doc = this.newVName(target.signature + '#doc', target.path);
  2569      this.emitNode(doc, NodeKind.DOC);
  2570      this.emitEdge(doc, EdgeKind.DOCUMENTS, target);
  2571      this.emitFact(doc, FactName.TEXT, jsdoc);
  2572    }
  2573  
  2574    visitAsExpressions(node: ts.AsExpression) {
  2575      ts.forEachChild(node, (child) => {
  2576        this.visit(child);
  2577      });
  2578      // Handle case like `{name: 'Alice'} as Person` and connect `name` property
  2579      // to Person.name.
  2580      if (ts.isObjectLiteralExpression(node.expression)) {
  2581        this.connectObjectLiteralToType(node.expression, node.type);
  2582      }
  2583    }
  2584  
  2585    /**
  2586     * Tags a node as deprecated if its JSDoc marks it as so.
  2587     * TODO(TS 4.0): TS 4.0 exposes a JSDocDeprecatedTag.
  2588     */
  2589    maybeTagDeprecated(node: ts.Node, nodeVName: VName) {
  2590      const deprecatedTag =
  2591          ts.getJSDocTags(node).find(tag => tag.tagName.text === 'deprecated');
  2592      if (deprecatedTag) {
  2593        this.emitFact(
  2594            nodeVName, FactName.TAG_DEPRECATED,
  2595            (deprecatedTag.comment || '') as any);
  2596      }
  2597    }
  2598  
  2599    /** visit is the main dispatch for visiting AST nodes. */
  2600    visit(node: ts.Node): void {
  2601      switch (node.kind) {
  2602        case ts.SyntaxKind.ImportDeclaration:
  2603        case ts.SyntaxKind.ImportEqualsDeclaration:
  2604          return this.visitImportDeclaration(
  2605              node as ts.ImportDeclaration | ts.ImportEqualsDeclaration);
  2606        case ts.SyntaxKind.ExportAssignment:
  2607          return this.visitExportAssignment(node as ts.ExportAssignment);
  2608        case ts.SyntaxKind.ExportDeclaration:
  2609          return this.visitExportDeclaration(node as ts.ExportDeclaration);
  2610        case ts.SyntaxKind.VariableStatement:
  2611          return this.visitVariableStatement(node as ts.VariableStatement);
  2612        case ts.SyntaxKind.VariableDeclaration:
  2613          this.visitVariableDeclaration(node as ts.VariableDeclaration);
  2614          return;
  2615        case ts.SyntaxKind.PropertyAssignment:  // property in object literal
  2616        case ts.SyntaxKind.PropertyDeclaration:
  2617        case ts.SyntaxKind.PropertySignature:
  2618        case ts.SyntaxKind.ShorthandPropertyAssignment:
  2619          const vname =
  2620              this.visitVariableDeclaration(node as ts.PropertyDeclaration);
  2621          if (vname) this.visitJSDoc(node, vname);
  2622          return;
  2623        case ts.SyntaxKind.ArrowFunction:
  2624        case ts.SyntaxKind.Constructor:
  2625        case ts.SyntaxKind.FunctionDeclaration:
  2626        case ts.SyntaxKind.FunctionExpression:
  2627        case ts.SyntaxKind.MethodDeclaration:
  2628        case ts.SyntaxKind.MethodSignature:
  2629        case ts.SyntaxKind.GetAccessor:
  2630        case ts.SyntaxKind.SetAccessor:
  2631          return this.visitFunctionLikeDeclaration(
  2632              node as ts.FunctionLikeDeclaration);
  2633        case ts.SyntaxKind.ClassDeclaration:
  2634          return this.visitClassDeclaration(node as ts.ClassDeclaration);
  2635        case ts.SyntaxKind.InterfaceDeclaration:
  2636          return this.visitInterfaceDeclaration(node as ts.InterfaceDeclaration);
  2637        case ts.SyntaxKind.TypeAliasDeclaration:
  2638          return this.visitTypeAliasDeclaration(node as ts.TypeAliasDeclaration);
  2639        case ts.SyntaxKind.EnumDeclaration:
  2640          return this.visitEnumDeclaration(node as ts.EnumDeclaration);
  2641        case ts.SyntaxKind.EnumMember:
  2642          return this.visitEnumMember(node as ts.EnumMember);
  2643        case ts.SyntaxKind.TypeReference:
  2644          this.visitType(node as ts.TypeNode);
  2645          return;
  2646        case ts.SyntaxKind.BindingElement:
  2647          this.visitVariableDeclaration(node as ts.BindingElement);
  2648          return;
  2649        case ts.SyntaxKind.JsxAttribute:
  2650          // TODO: go/ts51upgrade - Auto-added to unblock TS5.1 migration.
  2651          //   TS2345: Argument of type 'JsxAttribute' is not assignable to parameter of type '{ name: ObjectBindingPattern | ArrayBindingPattern | Identifier | StringLiteral | NumericLiteral | ComputedPropertyName | PrivateIdentifier; type?: TypeNode | undefined; initializer?: Expression | undefined; kind: SyntaxKind; } & Node'.
  2652          // @ts-ignore
  2653          this.visitVariableDeclaration(node as ts.JsxAttribute);
  2654          return;
  2655        case ts.SyntaxKind.Identifier:
  2656        case ts.SyntaxKind.PrivateIdentifier:
  2657        case ts.SyntaxKind.StringLiteral:
  2658        case ts.SyntaxKind.NumericLiteral:
  2659          // Assume that this identifer is occurring as part of an
  2660          // expression; we handle identifiers that occur in other
  2661          // circumstances (e.g. in a type) separately in visitType.
  2662          this.visitExpressionMember(node);
  2663          return;
  2664        case ts.SyntaxKind.ThisKeyword:
  2665          return this.visitThisKeyword(node as ts.ThisExpression);
  2666        case ts.SyntaxKind.ModuleDeclaration:
  2667          return this.visitModuleDeclaration(node as ts.ModuleDeclaration);
  2668        case ts.SyntaxKind.CallExpression:
  2669        case ts.SyntaxKind.NewExpression:
  2670          this.visitCallOrNewExpression(
  2671              node as ts.CallExpression | ts.NewExpression);
  2672          return;
  2673        case ts.SyntaxKind.ReturnStatement:
  2674          this.visitReturnStatement(node as ts.ReturnStatement)
  2675          return;
  2676        case ts.SyntaxKind.AsExpression:
  2677          this.visitAsExpressions(node as ts.AsExpression);
  2678          return;
  2679        default:
  2680          // Use default recursive processing.
  2681          return ts.forEachChild(node, n => this.visit(n));
  2682      }
  2683    }
  2684  
  2685    /** index is the main entry point, starting the recursive visit. */
  2686    index() {
  2687      this.emitFact(this.kFile, FactName.NODE_KIND, NodeKind.FILE);
  2688      this.emitFact(this.kFile, FactName.TEXT, this.file.text);
  2689  
  2690      this.emitModuleAnchor(this.file);
  2691  
  2692      // Emit file-level init function to contain all call anchors that
  2693      // don't have parent functions.
  2694      const fileInitFunc = this.getSyntheticFileInitVName();
  2695      this.emitFact(fileInitFunc, FactName.NODE_KIND, NodeKind.FUNCTION);
  2696      this.emitEdge(
  2697          this.newAnchor(this.file, 0, 0), EdgeKind.DEFINES, fileInitFunc);
  2698  
  2699      ts.forEachChild(this.file, n => this.visit(n));
  2700    }
  2701  }
  2702  
  2703  /**
  2704   * Main plugin that runs over all srcs files in a compilation unit and emits
  2705   * Kythe data for all symbols in those files.
  2706   */
  2707  class TypescriptIndexer implements Plugin {
  2708    name = 'TypescriptIndexerPlugin';
  2709  
  2710    index(context: IndexerHost) {
  2711      for (const path of context.compilationUnit.srcs) {
  2712        const sourceFile = context.program.getSourceFile(path);
  2713        if (!sourceFile) {
  2714          throw new Error(`requested indexing ${path} not found in program`);
  2715        }
  2716        const visitor = new Visitor(context, sourceFile);
  2717        visitor.index();
  2718      }
  2719    }
  2720  }
  2721  
  2722  /**
  2723   * index indexes a TypeScript program, producing Kythe JSON objects for the
  2724   * source files in the specified paths.
  2725   *
  2726   * (A ts.Program is a configured collection of parsed source files, but
  2727   * the caller must specify the source files within the program that they want
  2728   * Kythe output for, because e.g. the standard library is contained within
  2729   * the Program and we only want to process it once.)
  2730   *
  2731   * @param paths Files to index
  2732   */
  2733  export function index(compilationUnit: CompilationUnit, options: IndexingOptions): ts.Diagnostic[] {
  2734    const program = ts.createProgram({
  2735      rootNames: compilationUnit.rootFiles,
  2736      options: options.compilerOptions,
  2737      host: options.compilerHost,
  2738    });
  2739    // Note: we only call getPreEmitDiagnostics (which causes type checking to
  2740    // happen) on the input paths as provided in paths.  This means we don't
  2741    // e.g. type-check the standard library unless we were explicitly told to.
  2742    const diags: ts.Diagnostic[] = [];
  2743    for (const path of compilationUnit.srcs) {
  2744      for (const diag of ts.getPreEmitDiagnostics(program, program.getSourceFile(path))) {
  2745        diags.push(diag);
  2746      }
  2747    }
  2748    // Note: don't abort if there are diagnostics.  This allows us to
  2749    // index programs with errors.  We return these diagnostics at the end
  2750    // so the caller can act on them if it wants.
  2751  
  2752    const indexingContext =  new StandardIndexerContext(program, compilationUnit, options);
  2753    const plugins = [new TypescriptIndexer(), ...(options.plugins ?? [])];
  2754    for (const plugin of plugins) {
  2755      try {
  2756        plugin.index(indexingContext);
  2757      } catch (err) {
  2758        if (indexingContext.options.failAnalysisOnPluginError) {
  2759          throw err;
  2760        }
  2761        console.error(`Plugin ${plugin.name} errored:`, err);
  2762      }
  2763    }
  2764  
  2765    return diags;
  2766  }
  2767  
  2768  /**
  2769   * loadTsConfig loads a tsconfig.json from a path, throwing on any errors
  2770   * like "file not found" or parse errors.
  2771   */
  2772  export function loadTsConfig(
  2773      tsconfigPath: string, projectPath: string,
  2774      host: ts.ParseConfigHost = ts.sys): ts.ParsedCommandLine {
  2775    projectPath = path.resolve(projectPath);
  2776    const {config: json, error} = ts.readConfigFile(tsconfigPath, host.readFile);
  2777    if (error) {
  2778      throw new Error(ts.formatDiagnostics([error], ts.createCompilerHost({})));
  2779    }
  2780    const config = ts.parseJsonConfigFileContent(json, host, projectPath);
  2781    if (config.errors.length > 0) {
  2782      throw new Error(
  2783          ts.formatDiagnostics(config.errors, ts.createCompilerHost({})));
  2784    }
  2785    return config;
  2786  }
  2787  
  2788  function main(argv: string[]) {
  2789    if (argv.length < 1) {
  2790      console.error('usage: indexer path/to/tsconfig.json [PATH...]');
  2791      return 1;
  2792    }
  2793  
  2794    const config = loadTsConfig(argv[0], path.dirname(argv[0]));
  2795    let inPaths = argv.slice(1);
  2796    if (inPaths.length === 0) {
  2797      inPaths = config.fileNames;
  2798    }
  2799  
  2800    // This program merely demonstrates the API, so use a fake corpus/root/etc.
  2801    const rootVName: VName = {
  2802      corpus: 'corpus',
  2803      root: '',
  2804      path: '',
  2805      signature: '',
  2806      language: '',
  2807    };
  2808    const compilationUnit: CompilationUnit = {
  2809      srcs: inPaths,
  2810      rootVName,
  2811      rootFiles: inPaths,
  2812      fileVNames: new Map(),
  2813    };
  2814    index(compilationUnit, {
  2815      compilerOptions: config.options,
  2816      compilerHost: ts.createCompilerHost(config.options),
  2817      emit(obj: JSONFact | JSONEdge) {
  2818        console.log(JSON.stringify(obj));
  2819      }
  2820    });
  2821    return 0;
  2822  }
  2823  
  2824  if (require.main === module) {
  2825    // Note: do not use process.exit(), because that does not ensure that
  2826    // process.stdout has been flushed(!).
  2827    process.exitCode = main(process.argv.slice(2));
  2828  }