text: "use strict";
  // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
  // See LICENSE in the project root for license information.
  Object.defineProperty(exports, "__esModule", { value: true });
  const path = require("path");
  const ts = require("typescript");
  const AstSymbol_1 = require("../analyzer/AstSymbol");
  const api_extractor_model_1 = require("@microsoft/api-extractor-model");
  class ValidationEnhancer {
      static analyze(collector) {
          const alreadyWarnedSymbols = new Set();
          for (const entity of collector.entities) {
              if (entity.astEntity instanceof AstSymbol_1.AstSymbol) {
                  if (entity.exported) {
                      entity.astEntity.forEachDeclarationRecursive((astDeclaration) => {
                          ValidationEnhancer._checkReferences(collector, astDeclaration, alreadyWarnedSymbols);
                      });
                      ValidationEnhancer._checkForInternalUnderscore(collector, entity, entity.astEntity);
                  }
              }
          }
      }
      static _checkForInternalUnderscore(collector, collectorEntity, astSymbol) {
          const astSymbolMetadata = collector.fetchMetadata(astSymbol);
          if (astSymbolMetadata.releaseTag === api_extractor_model_1.ReleaseTag.Internal && !astSymbolMetadata.releaseTagSameAsParent) {
              for (const exportName of collectorEntity.exportNames) {
                  if (exportName[0] !== '_') {
                      collector.messageRouter.addAnalyzerIssue("ae-internal-missing-underscore" /* InternalMissingUnderscore */, `The name ${exportName} should be prefixed with an underscore`
                          + ` because the declaration is marked as "@internal"`, astSymbol, { exportName });
                  }
              }
          }
      }
      static _checkReferences(collector, astDeclaration, alreadyWarnedSymbols) {
          const astSymbolMetadata = collector.fetchMetadata(astDeclaration.astSymbol);
          const astSymbolReleaseTag = astSymbolMetadata.releaseTag;
          for (const referencedEntity of astDeclaration.referencedAstEntities) {
              if (referencedEntity instanceof AstSymbol_1.AstSymbol) {
                  // If this is e.g. a member of a namespace, then we need to be checking the top-level scope to see
                  // whether it's exported.
                  //
                  // TODO: Technically we should also check each of the nested scopes along the way.
                  const rootSymbol = referencedEntity.rootAstSymbol;
                  if (!rootSymbol.isExternal) {
                      const collectorEntity = collector.tryGetCollectorEntity(rootSymbol);
                      if (collectorEntity && collectorEntity.exported) {
                          const referencedMetadata = collector.fetchMetadata(referencedEntity);
                          const referencedReleaseTag = referencedMetadata.releaseTag;
                          if (api_extractor_model_1.ReleaseTag.compare(astSymbolReleaseTag, referencedReleaseTag) > 0) {
                              collector.messageRouter.addAnalyzerIssue("ae-incompatible-release-tags" /* IncompatibleReleaseTags */, `The symbol "${astDeclaration.astSymbol.localName}"`
                                  + ` is marked as ${api_extractor_model_1.ReleaseTag.getTagName(astSymbolReleaseTag)},`
                                  + ` but its signature references "${referencedEntity.localName}"`
                                  + ` which is marked as ${api_extractor_model_1.ReleaseTag.getTagName(referencedReleaseTag)}`, astDeclaration);
                          }
                      }
                      else {
                          const entryPointFilename = path.basename(collector.workingPackage.entryPointSourceFile.fileName);
                          if (!alreadyWarnedSymbols.has(referencedEntity)) {
                              alreadyWarnedSymbols.add(referencedEntity);
                              // The main usage scenario for ECMAScript symbols is to attach private data to a JavaScript object,
                              // so as a special case, we do NOT report them as forgotten exports.
                              if (!ValidationEnhancer._isEcmaScriptSymbol(referencedEntity)) {
                                  collector.messageRouter.addAnalyzerIssue("ae-forgotten-export" /* ForgottenExport */, `The symbol "${rootSymbol.localName}" needs to be exported`
                                      + ` by the entry point ${entryPointFilename}`, astDeclaration);
                              }
                          }
                      }
                  }
              }
          }
      }
      // Detect an AstSymbol that refers to an ECMAScript symbol declaration such as:
      //
      // const mySymbol: unique symbol = Symbol('mySymbol');
      static _isEcmaScriptSymbol(astSymbol) {
          if (astSymbol.astDeclarations.length !== 1) {
              return false;
          }
          // We are matching a form like this:
          //
          // - VariableDeclaration:
          //   - Identifier:  pre=[mySymbol]
          //   - ColonToken:  pre=[:] sep=[ ]
          //   - TypeOperator:
          //     - UniqueKeyword:  pre=[unique] sep=[ ]
          //     - SymbolKeyword:  pre=[symbol]
          const astDeclaration = astSymbol.astDeclarations[0];
          if (ts.isVariableDeclaration(astDeclaration.declaration)) {
              const variableTypeNode = astDeclaration.declaration.type;
              if (variableTypeNode) {
                  for (const token of variableTypeNode.getChildren()) {
                      if (token.kind === ts.SyntaxKind.SymbolKeyword) {
                          return true;
                      }
                  }
              }
          }
          return false;
      }
  }
  exports.ValidationEnhancer = ValidationEnhancer;
  //# sourceMappingURL=ValidationEnhancer.js.map